Thread overview
Delegates
Mar 30, 2005
Sean Kelly
Mar 30, 2005
Russ Lewis
Mar 31, 2005
Sean Kelly
Mar 30, 2005
Ben Hinkle
Mar 31, 2005
Russ Lewis
Mar 31, 2005
Ben Hinkle
Mar 31, 2005
Russ Lewis
March 30, 2005
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 );
#     }
# }


March 30, 2005
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.
March 30, 2005
> #         // (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 );


March 31, 2005
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); }
                        );
March 31, 2005
"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 :-)


March 31, 2005
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


March 31, 2005
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! :)