www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - delegate cannot handle polymorphism?

reply Nub Public <nubpublic gmail.com> writes:
class B
{
	void fun() { writeln("B"); }
}

class D : B
{
	override void fun() { writeln("D"); }
}

void delegate() dg = &b.fun;
dg.ptr = cast(void*)d;
dg();


Compiler: DMD 2.053
It prints "B" instead of "D".
The equivalent code in C++ prints "D" nicely.

Is it a bug or by design? I hope it's a bug coz it's smarter and more 
useful for delegates to handle polymorphism IMHO.
Jun 27 2011
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2011-06-27 03:30:09 -0400, Nub Public <nubpublic gmail.com> said:

 class B
 {
 	void fun() { writeln("B"); }
 }
 
 class D : B
 {
 	override void fun() { writeln("D"); }
 }
 
 void delegate() dg = &b.fun;
 dg.ptr = cast(void*)d;
 dg();
 
 
 Compiler: DMD 2.053
 It prints "B" instead of "D".
 The equivalent code in C++ prints "D" nicely.

C++ doesn't have delegates, and D doesn't have member function pointers. Resolving virtual functions is done while you take its address in D, while in C++ the member function pointer type contains holds the vtable offset (or several in the case of multiple inheritance) which gets resolved only when you call the function. If you want the C++ behaviour, try using a delegate literal as a trampoline that gets the object as a parameter to then call your function: void delegate(B b) dg = (B b) { b.fun(); }; dg(d); -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Jun 27 2011
next sibling parent reply Nub Public <nubpublic gmail.com> writes:
On 6/27/2011 7:08 PM, Michel Fortin wrote:
 On 2011-06-27 03:30:09 -0400, Nub Public <nubpublic gmail.com> said:

 class B
 {
 void fun() { writeln("B"); }
 }

 class D : B
 {
 override void fun() { writeln("D"); }
 }

 void delegate() dg = &b.fun;
 dg.ptr = cast(void*)d;
 dg();


 Compiler: DMD 2.053
 It prints "B" instead of "D".
 The equivalent code in C++ prints "D" nicely.

C++ doesn't have delegates, and D doesn't have member function pointers. Resolving virtual functions is done while you take its address in D, while in C++ the member function pointer type contains holds the vtable offset (or several in the case of multiple inheritance) which gets resolved only when you call the function. If you want the C++ behaviour, try using a delegate literal as a trampoline that gets the object as a parameter to then call your function: void delegate(B b) dg = (B b) { b.fun(); }; dg(d);

Thank you. That was very helpful. What's the rational for this behavior though? Resolving the address of a virtual function at compile time seems a little counter-intuitive to me. I guess this way is slightly more efficient.
Jun 27 2011
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
Nub Public wrote:
 On 6/27/2011 7:08 PM, Michel Fortin wrote:
 On 2011-06-27 03:30:09 -0400, Nub Public <nubpublic gmail.com> said:

 class BOn 6/27/2011 7:08 PM, Michel Fortin wrote:

 class B
 {
 void fun() { writeln("B"); }
 }

 class D : B
 {
 override void fun() { writeln("D"); }
 }

 void delegate() dg = &b.fun;
 dg.ptr = cast(void*)d;
 dg();


 Compiler: DMD 2.053
 It prints "B" instead of "D".
 The equivalent code in C++ prints "D" nicely.

C++ doesn't have delegates, and D doesn't have member function pointers. Resolving virtual functions is done while you take its address in D, while in C++ the member function pointer type contains holds the vtable offset (or several in the case of multiple inheritance) which gets resolved only when you call the function. If you want the C++ behaviour, try using a delegate literal as a trampoline that gets the object as a parameter to then call your function: void delegate(B b) dg = (B b) { b.fun(); }; dg(d);

Thank you. That was very helpful. What's the rational for this behavior though? Resolving the address of a virtual function at compile time seems a little counter-intuitive to me. I guess this way is slightly more efficient.

A delegate literal consists of a function pointer and a context pointer. There is no polymorphism in that. A member function is a normal function you can take the address of. In general, you shouldn't update one of (ptr,funcptr) without updating the other unless you have good reasons to do so and know exactly what you are doing. Not having member pointers is AFAIK a direct consequence of the fact that nobody uses them in C++ and almost nobody even knows that they exist. Furthermore, their implementation is too involved, given their limited usefulness. Cheers, -Timon
Jun 27 2011
parent reply Nub Public <nubpublic gmail.com> writes:
On 6/28/2011 1:29 AM, Timon Gehr wrote:
 Nub Public wrote:
 On 6/27/2011 7:08 PM, Michel Fortin wrote:
 On 2011-06-27 03:30:09 -0400, Nub Public<nubpublic gmail.com>  said:

 class BOn 6/27/2011 7:08 PM, Michel Fortin wrote:

 class B
 {
 void fun() { writeln("B"); }
 }

 class D : B
 {
 override void fun() { writeln("D"); }
 }

 void delegate() dg =&b.fun;
 dg.ptr = cast(void*)d;
 dg();


 Compiler: DMD 2.053
 It prints "B" instead of "D".
 The equivalent code in C++ prints "D" nicely.

C++ doesn't have delegates, and D doesn't have member function pointers. Resolving virtual functions is done while you take its address in D, while in C++ the member function pointer type contains holds the vtable offset (or several in the case of multiple inheritance) which gets resolved only when you call the function. If you want the C++ behaviour, try using a delegate literal as a trampoline that gets the object as a parameter to then call your function: void delegate(B b) dg = (B b) { b.fun(); }; dg(d);

Thank you. That was very helpful. What's the rational for this behavior though? Resolving the address of a virtual function at compile time seems a little counter-intuitive to me. I guess this way is slightly more efficient.

A delegate literal consists of a function pointer and a context pointer. There is no polymorphism in that. A member function is a normal function you can take the address of.

I see.
 In general, you shouldn't update one of (ptr,funcptr) without updating the
other
 unless you have good reasons to do so and know exactly what you are doing.
 Not having member pointers is AFAIK a direct consequence of the fact that
nobody
 uses them in C++ and almost nobody even knows that they exist.
 Furthermore, their implementation is too involved, given their limited
usefulness.

 Cheers,
 -Timon

I have to disagree on this. Member function pointers are useful in many event handling systems. Imagine a signal and slot system. To register a member function as a slot, the most direct and intuitive way is to take a member function pointer. In c++0x and boost, <function>, <functional> and related libraries all work with member function pointers. My premise is simple: a virtual function should always be resolved by the dynamic identity of the object, regardless of whether it is called directly or through a function pointer. Perhaps the contextptr of D delegate cannot do this because it can refer to the enclosing context besides an object. But I'd love it for D to have the functionality of a truly polymorhic member function pointer. Maybe the restrictions on the keyword "function" can be relaxed to work with such a pointer?
Jun 27 2011
next sibling parent reply foobar <foo bar.com> writes:
== Quote from Nub Public (nubpublic gmail.com)'s article
 On 6/28/2011 1:29 AM, Timon Gehr wrote:
 Nub Public wrote:
 On 6/27/2011 7:08 PM, Michel Fortin wrote:
 On 2011-06-27 03:30:09 -0400, Nub Public<nubpublic gmail.com>  said:

 class BOn 6/27/2011 7:08 PM, Michel Fortin wrote:

 class B
 {
 void fun() { writeln("B"); }
 }

 class D : B
 {
 override void fun() { writeln("D"); }
 }

 void delegate() dg =&b.fun;
 dg.ptr = cast(void*)d;
 dg();


 Compiler: DMD 2.053
 It prints "B" instead of "D".
 The equivalent code in C++ prints "D" nicely.

C++ doesn't have delegates, and D doesn't have member function pointers. Resolving virtual functions is done while you take its address in D, while in C++ the member function pointer type contains holds the vtable offset (or several in the case of multiple inheritance) which gets resolved only when you call the function. If you want the C++ behaviour, try using a delegate literal as a trampoline that gets the object as a parameter to then call your function: void delegate(B b) dg = (B b) { b.fun(); }; dg(d);

Thank you. That was very helpful. What's the rational for this behavior though? Resolving the address of a virtual function at compile time seems a little counter-intuitive to me. I guess this way is slightly more efficient.

A delegate literal consists of a function pointer and a context pointer. There is no polymorphism in that. A member function is a normal function you can take the address of.

 In general, you shouldn't update one of (ptr,funcptr) without updating the
other
 unless you have good reasons to do so and know exactly what you are doing.
 Not having member pointers is AFAIK a direct consequence of the fact that
nobody
 uses them in C++ and almost nobody even knows that they exist.
 Furthermore, their implementation is too involved, given their limited
usefulness.

 Cheers,
 -Timon

Member function pointers are useful in many event handling systems. Imagine a signal and slot system. To register a member function as a slot, the most direct and intuitive way is to take a member function pointer. In c++0x and boost, <function>, <functional> and related libraries all work with member function pointers. My premise is simple: a virtual function should always be resolved by the dynamic identity of the object, regardless of whether it is called directly or through a function pointer. Perhaps the contextptr of D delegate cannot do this because it can refer to the enclosing context besides an object. But I'd love it for D to have the functionality of a truly polymorhic member function pointer. Maybe the restrictions on the keyword "function" can be relaxed to work with such a pointer?

The above premise is incorrect in that it compares to different concepts. A delegate is a closure whereas a c++ member function pointer is not. This means it captures the context in which the code was run. In the case of a method call, that context would be the specific instance when the delegate was created hence it cannot and should not be polymorphic. Regarding signal/slot designs: see for example C#'s implementation - this is incorporated into the language as "events" which are simply arrays of delegates. Implementing this via member function pointers is actually the uncommon case since most other languages use closures.
Jun 27 2011
parent Nub Public <nubpublic gmail.com> writes:
On 6/28/2011 2:54 PM, foobar wrote:
 == Quote from Nub Public (nubpublic gmail.com)'s article
 On 6/28/2011 1:29 AM, Timon Gehr wrote:
 Nub Public wrote:
 On 6/27/2011 7:08 PM, Michel Fortin wrote:
 On 2011-06-27 03:30:09 -0400, Nub Public<nubpublic gmail.com>   said:

 class BOn 6/27/2011 7:08 PM, Michel Fortin wrote:

 class B
 {
 void fun() { writeln("B"); }
 }

 class D : B
 {
 override void fun() { writeln("D"); }
 }

 void delegate() dg =&b.fun;
 dg.ptr = cast(void*)d;
 dg();


 Compiler: DMD 2.053
 It prints "B" instead of "D".
 The equivalent code in C++ prints "D" nicely.

C++ doesn't have delegates, and D doesn't have member function pointers. Resolving virtual functions is done while you take its address in D, while in C++ the member function pointer type contains holds the vtable offset (or several in the case of multiple inheritance) which gets resolved only when you call the function. If you want the C++ behaviour, try using a delegate literal as a trampoline that gets the object as a parameter to then call your function: void delegate(B b) dg = (B b) { b.fun(); }; dg(d);

Thank you. That was very helpful. What's the rational for this behavior though? Resolving the address of a virtual function at compile time seems a little counter-intuitive to me. I guess this way is slightly more efficient.

A delegate literal consists of a function pointer and a context pointer. There is no polymorphism in that. A member function is a normal function you can take the address of.

 In general, you shouldn't update one of (ptr,funcptr) without updating the
other
 unless you have good reasons to do so and know exactly what you are doing.
 Not having member pointers is AFAIK a direct consequence of the fact that
nobody
 uses them in C++ and almost nobody even knows that they exist.
 Furthermore, their implementation is too involved, given their limited
usefulness.

 Cheers,
 -Timon

Member function pointers are useful in many event handling systems. Imagine a signal and slot system. To register a member function as a slot, the most direct and intuitive way is to take a member function pointer. In c++0x and boost,<function>,<functional> and related libraries all work with member function pointers. My premise is simple: a virtual function should always be resolved by the dynamic identity of the object, regardless of whether it is called directly or through a function pointer. Perhaps the contextptr of D delegate cannot do this because it can refer to the enclosing context besides an object. But I'd love it for D to have the functionality of a truly polymorhic member function pointer. Maybe the restrictions on the keyword "function" can be relaxed to work with such a pointer?

The above premise is incorrect in that it compares to different concepts. A delegate is a closure whereas a c++ member function pointer is not. This means it captures the context in which the code was run. In the case of a method call, that context would be the specific instance when the delegate was created hence it cannot and should not be polymorphic. Regarding signal/slot designs: see for example C#'s implementation - this is incorporated into the language as "events" which are simply arrays of delegates. Implementing this via member function pointers is actually the uncommon case since most other languages use closures.

Thanks for the clarification.
Jun 28 2011
prev sibling parent Nub Public <nubpublic gmail.com> writes:
On 6/28/2011 7:13 PM, Steven Schveighoffer wrote:
 On Mon, 27 Jun 2011 22:23:32 -0400, Nub Public <nubpublic gmail.com> wrote:


 I have to disagree on this.
 Member function pointers are useful in many event handling systems.
 Imagine a signal and slot system. To register a member function as a
 slot, the most direct and intuitive way is to take a member function
 pointer. In c++0x and boost, <function>, <functional> and related
 libraries all work with member function pointers.

 My premise is simple: a virtual function should always be resolved by
 the dynamic identity of the object, regardless of whether it is called
 directly or through a function pointer.

This can be had using delegates, but it's not straightforward, you need to do a little bit of extra work. For example, if you did this: class A { final void callFoo() { foo(); } void foo() {writeln("A");} } class B : A { void foo() {writeln("B");} } A delegate to callFoo would do polymorphism, even when you change the context pointer (to a compatible instance). Note that it is highly unusual to change anything about a delegate, as type checking the context pointer is out the window. In fact, it is the property of delegates which makes them so useful -- because you don't have to care what the type of the context pointer is, the delegate's type does not depend on it.
 Perhaps the contextptr of D delegate cannot do this because it can
 refer to the enclosing context besides an object. But I'd love it for
 D to have the functionality of a truly polymorhic member function
 pointer. Maybe the restrictions on the keyword "function" can be
 relaxed to work with such a pointer?

The easiest thing to do is the suggestion from Michel Fortin -- create a function/delegate that accepts the base type. This is much safer as well. I would be highly suspect of code that alters any delegate components. -Steve

I see. Thank you.
Jun 28 2011
prev sibling parent Michel Fortin <michel.fortin michelf.com> writes:
On 2011-06-27 07:49:19 -0400, Nub Public <nubpublic gmail.com> said:

 What's the rational for this behavior though? Resolving the address of 
 a virtual function at compile time seems a little counter-intuitive to 
 me.

The address for a virtual function isn't necessarily resolved at compile time. It is resolved at the point where you use the address-of operator, and that'll check the vtable at runtime if necessary. In D: B b = new D; auto dg = &b.foo; // address is resolved at runtime by looking at the vtable dg(); // calls D.foo In C++: void (B::*fptr)() = &B::foo; B b = new D; b.*fptr(); // vtable lookup here, calls D.foo
 I guess this way is slightly more efficient.

It certainly is if you call the delegate more often than you create one. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Jun 28 2011
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 27 Jun 2011 22:23:32 -0400, Nub Public <nubpublic gmail.com> wrote:


 I have to disagree on this.
 Member function pointers are useful in many event handling systems.  
 Imagine a signal and slot system. To register a member function as a  
 slot, the most direct and intuitive way is to take a member function  
 pointer. In c++0x and boost, <function>, <functional> and related  
 libraries all work with member function pointers.

 My premise is simple: a virtual function should always be resolved by  
 the dynamic identity of the object, regardless of whether it is called  
 directly or through a function pointer.

This can be had using delegates, but it's not straightforward, you need to do a little bit of extra work. For example, if you did this: class A { final void callFoo() { foo(); } void foo() {writeln("A");} } class B : A { void foo() {writeln("B");} } A delegate to callFoo would do polymorphism, even when you change the context pointer (to a compatible instance). Note that it is highly unusual to change anything about a delegate, as type checking the context pointer is out the window. In fact, it is the property of delegates which makes them so useful -- because you don't have to care what the type of the context pointer is, the delegate's type does not depend on it.
 Perhaps the contextptr of D delegate cannot do this because it can refer  
 to the enclosing context besides an object. But I'd love it for D to  
 have the functionality of a truly polymorhic member function pointer.  
 Maybe the restrictions on the keyword "function" can be relaxed to work  
 with such a pointer?

The easiest thing to do is the suggestion from Michel Fortin -- create a function/delegate that accepts the base type. This is much safer as well. I would be highly suspect of code that alters any delegate components. -Steve
Jun 28 2011