Thread overview
Possible Bug with Interfaces in Variadic Functions?
Sep 18, 2006
Daniel Giddings
Sep 26, 2006
Bruno Medeiros
Sep 26, 2006
Daniel Giddings
September 18, 2006
Hi All, I'm new to D, and have come across the following problem. I thought it best to ask about it before reporting it as a bug.

The problem I've come across is passing a class with an interface base into a variadic function and accessing it as the interface. It crashes the program with some strange behaviour. As far as I can tell I'm not doing anything wrong.

Anyway, here's the program:

--------------------------------------------------------
import std.stdio;
import std.stdarg;

class B
{
	char[] f() { return "B.f"; }
}

class DB : B
{
	char[] f() { return "DB.f"; }
}

interface I
{
	char[] f();
}

class CI : I
{
	char[] f() { return "CI.f"; }
}

void output( ... )
{
	for( int i = 0; i < _arguments.length; ++i )
	{
		if( _arguments[i] == typeid(int) )
			writefln( "int: %s", va_arg!(int)(_argptr) );
		
		if( _arguments[i] == typeid(char[]) )
			writefln( "char[]: %s", va_arg!(char[])(_argptr) );
		
		if( _arguments[i] == typeid(B) )
			writefln( "B: %s", va_arg!(B)(_argptr).f() );	
		
		if( _arguments[i] == typeid(I) )
		{
			writefln( "Print here for test purposes only - CI appears after this" );
			writefln( "I: %s", va_arg!(I)(_argptr).f() );
		}
	}
}

void main()
{
	output( 5, "Hello World!", new DB, new CI );
}
--------------------------------------------------------

The output I'm receiving is:

int: 5
char[]: Hello World!
B: DB.f
Print here for test purposes only - CI appears after this
CI
I: Error: Access Violation

where as I would have expected:

int: 5
char[]: Hello World!
B: DB.f
Print here for test purposes only - CI appears after this
I: CI.f

I added the class example with a base class as well, and it behaves as I expected.

Is there anything I'm doing incorrectly? It's easy enough to get around the problem for what I want to do, but I thought I'd bring it up.

Cheers,

:-) Dan
September 26, 2006
Daniel Giddings wrote:
> Hi All, I'm new to D, and have come across the following problem. I thought it best to ask about it before reporting it as a bug.
> 
> The problem I've come across is passing a class with an interface base into a variadic function and accessing it as the interface. It crashes the program with some strange behaviour. As far as I can tell I'm not doing anything wrong.
> 
> Anyway, here's the program:
> 
> --------------------------------------------------------
> import std.stdio;
> import std.stdarg;
> 
> class B
> {
>     char[] f() { return "B.f"; }
> }
> 
> class DB : B
> {
>     char[] f() { return "DB.f"; }
> }
> 
> interface I
> {
>     char[] f();
> }
> 
> class CI : I
> {
>     char[] f() { return "CI.f"; }
> }
> 
> void output( ... )
> {
>     for( int i = 0; i < _arguments.length; ++i )
>     {
>         if( _arguments[i] == typeid(int) )
>             writefln( "int: %s", va_arg!(int)(_argptr) );
>                if( _arguments[i] == typeid(char[]) )
>             writefln( "char[]: %s", va_arg!(char[])(_argptr) );
>                if( _arguments[i] == typeid(B) )
>             writefln( "B: %s", va_arg!(B)(_argptr).f() );                  if( _arguments[i] == typeid(I) )
>         {
>             writefln( "Print here for test purposes only - CI appears after this" );
>             writefln( "I: %s", va_arg!(I)(_argptr).f() );
>         }
>     }
> }
> 
> void main()
> {
>     output( 5, "Hello World!", new DB, new CI );
> }
> --------------------------------------------------------
> 
> The output I'm receiving is:
> 
> int: 5
> char[]: Hello World!
> B: DB.f
> Print here for test purposes only - CI appears after this
> CI
> I: Error: Access Violation
> 
> where as I would have expected:
> 
> int: 5
> char[]: Hello World!
> B: DB.f
> Print here for test purposes only - CI appears after this
> I: CI.f
> 
> I added the class example with a base class as well, and it behaves as I expected.
> 
> Is there anything I'm doing incorrectly? It's easy enough to get around the problem for what I want to do, but I thought I'd bring it up.
> 
> Cheers,
> 
> :-) Dan

You are using typeid() comparison incorrectly (no fault of yours, the spec is erroneous, see http://d.puremagic.com/issues/show_bug.cgi?id=373 ).

Using '==' to compare TypeInfos will test the equality of the type's "archetype", that is, if they are both a struct, a pointer, a class, etc. Apparently interfaces and classes belong to the same archetype (which is class), so any TypeInfo equality comparison between any of these will allways result true.
Using 'is' to compare TypeInfos will test for an exact type match, which is also not what you, since it will disregard polymorphism.

Here's how you can do it:

  // Check for class archetype:
  if( _arguments[i] == typeid(Object) ) {
    auto obj = va_arg!(Object)(_argptr);
    // check if obj is-a I
    if( (cast(I) obj) != null ) {
      I myi = cast(I) obj;
      // use myi
      writefln(myi.f());
    }
  }

-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
September 26, 2006
That works nicely.

Thanks,

:-) Dan

Bruno Medeiros wrote:
> You are using typeid() comparison incorrectly (no fault of yours, the spec is erroneous, see http://d.puremagic.com/issues/show_bug.cgi?id=373 ).
> 
> Using '==' to compare TypeInfos will test the equality of the type's "archetype", that is, if they are both a struct, a pointer, a class, etc. Apparently interfaces and classes belong to the same archetype (which is class), so any TypeInfo equality comparison between any of these will allways result true.
> Using 'is' to compare TypeInfos will test for an exact type match, which is also not what you, since it will disregard polymorphism.
> 
> Here's how you can do it:
> 
>   // Check for class archetype:
>   if( _arguments[i] == typeid(Object) ) {
>     auto obj = va_arg!(Object)(_argptr);
>     // check if obj is-a I
>     if( (cast(I) obj) != null ) {
>       I myi = cast(I) obj;
>       // use myi
>       writefln(myi.f());
>     }
>   }
>