www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - Delegates

reply Sean Kelly <sean f4.ca> writes:
I'm not sure that this is a bug, but the behavior seems a bit odd so I thought
I'd post this anyway.  The code in (1) produces this result:

C:\>tst
Person
Error: Access Violation

while the code in (2) produces the expected result:

C:\>tst
John Doe

As for why I ran into the problem--I'm trying to use interfaces to clean up an
inter-library relationship (the gc and the Thread class), and I want to expose a
static opApply.

# interface IPerson
# {
#     char[] name();
# }
# 
# class Person : IPerson
# {
#     char[] name() { return "John Doe"; }
#     
#     static int opApply( int delegate( inout Person ) dg )
#     {
#         int ret = 0;
# 
#         foreach( Person val; all )
#         {
#             ret = dg( val );
#             if( ret )
#                 break;
#         }
#         return ret; 
#     }
#     
#     static Person[] all;
# }
# 
# interface IPersonList
# {
#     int opApply( int delegate( inout IPerson ) dg );
# }
# 
# class PersonList : IPersonList
# {
#     int opApply( int delegate( inout IPerson ) dg )
#     {   
#         // (1)
#         return Person.opApply( cast( int delegate( inout Person ) ) dg );
#         
#         // (2)
#         /*
#         int ret = 0;
# 
#         foreach( Person val; Person )
#         {
#             IPerson i = val;
#             ret = dg( i );
#             if( ret )
#                 break;
#         }
#         return ret;
#         */        
#     }    
# }
# 
# import std.c.stdio;
# 
# void main()
# {
#     Person.all ~= new Person();
#     foreach( IPerson p; new PersonList() )
#     {
#         printf( "%.*s\n", p.name );
#     }
# }
Mar 30 2005
next sibling parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
I did some thinking about this, and I think that I understand why this 
is.  Basically, the issue here (I'm guessing) is that the vtable layout 
of the Person class is different than the vtable layout of the IPerson 
interface.

I think that this would, most likely, work if IPerson was a base class 
rather than an interface, because in that case, the layout of Person 
(including the vtable layout) would just be a superset of IPerson. 
However, since you can inherit any number of interfaces into a single 
class, you can't expect the class to have the same layout.

In your example (2), you do the assignment:
	IPerson val = val;
which does the implicit cast from the class to the interface.  Then, 
when you pass that reference to the delegate which is expecting an 
interface reference.
Mar 30 2005
parent Sean Kelly <sean f4.ca> writes:
In article <d2fb8l$29um$1 digitaldaemon.com>, Russ Lewis says...
I did some thinking about this, and I think that I understand why this 
is.  Basically, the issue here (I'm guessing) is that the vtable layout 
of the Person class is different than the vtable layout of the IPerson 
interface.

Makes sense. I was looking at this from a C++ perspective where such differences don't exist. Thanks! Sean
Mar 30 2005
prev sibling parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
 #         // (1)
 #         return Person.opApply( cast( int delegate( inout Person ) ) 
 dg );

I agree with what Russ said. Here's a replacement for (1) that uses a helper delegate (nested function) to perform the conversion. int dgHelper(inout Person p) { IPerson ip = p; return dg(ip); } return Person.opApply( &dgHelper );
Mar 30 2005
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Ben Hinkle wrote:
#         // (1)
#         return Person.opApply( cast( int delegate( inout Person ) ) 
dg );

I agree with what Russ said. Here's a replacement for (1) that uses a helper delegate (nested function) to perform the conversion. int dgHelper(inout Person p) { IPerson ip = p; return dg(ip); } return Person.opApply( &dgHelper );

A more compact version, if you think it's readable: return Person.opApply( delegate int(inout Person p) { return dg(p); } );
Mar 30 2005
parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message 
news:d2ffrr$2eb8$1 digitaldaemon.com...
 Ben Hinkle wrote:
#         // (1)
#         return Person.opApply( cast( int delegate( inout Person ) ) 
dg );

I agree with what Russ said. Here's a replacement for (1) that uses a helper delegate (nested function) to perform the conversion. int dgHelper(inout Person p) { IPerson ip = p; return dg(ip); } return Person.opApply( &dgHelper );

A more compact version, if you think it's readable: return Person.opApply( delegate int(inout Person p) { return dg(p); } );

When I tried that the compiler complained that cast(IPerson)p wasn't a lvalue. So I had to introduce the IPersion ip = p; temporary and at that point I figured a nested function would be more readable. But it is fun to have delegate and opApply nesting like that :-)
Mar 30 2005
parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Ben Hinkle wrote:
 "Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message 
 news:d2ffrr$2eb8$1 digitaldaemon.com...
 
Ben Hinkle wrote:

#         // (1)
#         return Person.opApply( cast( int delegate( inout Person ) ) 
dg );

I agree with what Russ said. Here's a replacement for (1) that uses a helper delegate (nested function) to perform the conversion. int dgHelper(inout Person p) { IPerson ip = p; return dg(ip); } return Person.opApply( &dgHelper );

A more compact version, if you think it's readable: return Person.opApply( delegate int(inout Person p) { return dg(p); } );

When I tried that the compiler complained that cast(IPerson)p wasn't a lvalue. So I had to introduce the IPersion ip = p; temporary and at that point I figured a nested function would be more readable. But it is fun to have delegate and opApply nesting like that :-)

Ah, yes, the advantages of really trying out what you suggest. Well done! :)
Mar 30 2005