www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Possible Bug with Interfaces in Variadic Functions?

reply Daniel Giddings <danielg microforte.com.au> writes:
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
Sep 17 2006
parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
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
Sep 26 2006
parent Daniel Giddings <danielg microforte.com.au> writes:
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());
     }
   }
 
Sep 26 2006