www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Cannot implicitly convert delegate to function

reply Lionello Lunesu <lio lunesu.remove.com> writes:
Hi,

Shouldn't a (pointer to a) function be convertible to a delegate? Seems 
to me it's like casting an int to a long; the latter has "more info" and 
can be converted back into the former without the loss of data. The same 
holds for function/delegate: a function could be converted to a delegate 
for "null"; by forgetting the "this" (null), we get the function pointer 
back.

#import std.format;
#void _disp_putc( dchar c ) {
#	int i = cast(int)c;
#	if (c >= 0x80)
#		i = '?';
#//	disp_putc(i);
#}
#void disp_writef(...) {
#	std.format.doFormat(&_disp_putc,_arguments,_argptr);
#}

Results in (v0.154):

dmd t.d

delegate(dchar),TypeInfo[],void*) does not match argument types (void(*)(dchar c),TypeInfo[],void*) t.d(9): cannot implicitly convert expression (& _disp_putc) of type void(*)(dchar c) to void delegate(dchar) Right? L.
Apr 27 2006
next sibling parent reply Lionello Lunesu <lio lunesu.remove.com> writes:
(Yes, obviously I could put the function inside a class, but that's just 
adding overhead that shouldn't be necessary in the first place)
Apr 27 2006
parent Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Lionello Lunesu wrote:
 (Yes, obviously I could put the function inside a class, but that's just 
 adding overhead that shouldn't be necessary in the first place)

Could always wrap it in an anonymous delegate: #import std.format; #void _disp_putc( dchar c ) { # int i = cast(int)c; # if (c >= 0x80) # i = '?'; #// disp_putc(i); #} #void disp_writef(...) { # std.format.doFormat(delegate void(dchar c){_disp_putc(c);},_arguments,_argptr); #} But still, for something this trivial its just overkill. I do believe I recall Walter saying that, at some unknown future point, he intends function pointers and delegates to merge into a single type, but I don't know what the precise plan of action (if any) might be to that end. It would certainly help solve soft spots like this one, though. -- Chris Nicholson-Sauls
Apr 27 2006
prev sibling next sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Lionello Lunesu" <lio lunesu.remove.com> wrote in message 
news:e2qig4$1ovk$1 digitaldaemon.com...
 Hi,

 Shouldn't a (pointer to a) function be convertible to a delegate? Seems to 
 me it's like casting an int to a long; the latter has "more info" and can 
 be converted back into the former without the loss of data. The same holds 
 for function/delegate: a function could be converted to a delegate for 
 "null"; by forgetting the "this" (null), we get the function pointer back.

Without the context pointer, a delegate cannot function properly. So casting from delegate to function isn't really possible. The other way around.. I don't think so either. Something tells me the mechanism for a delegate call would screw up if the context pointer were null, or if the function weren't designed to be a delegate.
Apr 27 2006
next sibling parent reply BCS <BCS_member pathlink.com> writes:
In article <e2qlrk$1uh5$1 digitaldaemon.com>, Jarrett Billingsley says...
"Lionello Lunesu" <lio lunesu.remove.com> wrote in message 
news:e2qig4$1ovk$1 digitaldaemon.com...
 Hi,

 Shouldn't a (pointer to a) function be convertible to a delegate? Seems to 
 me it's like casting an int to a long; the latter has "more info" and can 
 be converted back into the former without the loss of data. The same holds 
 for function/delegate: a function could be converted to a delegate for 
 "null"; by forgetting the "this" (null), we get the function pointer back.

Without the context pointer, a delegate cannot function properly. So casting from delegate to function isn't really possible. The other way around.. I don't think so either. Something tells me the mechanism for a delegate call would screw up if the context pointer were null, or if the function weren't designed to be a delegate.

It would take some ASM hacking but It might be possible to place the fn ptr in the context pointer and have the delegate's function rearrange the stack and then call (with tail recursion) the function.
Apr 27 2006
parent reply Lionello Lunesu <lio lunesu.remove.com> writes:
BCS wrote:
 In article <e2qlrk$1uh5$1 digitaldaemon.com>, Jarrett Billingsley says...
 "Lionello Lunesu" <lio lunesu.remove.com> wrote in message 
 news:e2qig4$1ovk$1 digitaldaemon.com...
 Hi,

 Shouldn't a (pointer to a) function be convertible to a delegate? Seems to 
 me it's like casting an int to a long; the latter has "more info" and can 
 be converted back into the former without the loss of data. The same holds 
 for function/delegate: a function could be converted to a delegate for 
 "null"; by forgetting the "this" (null), we get the function pointer back.

casting from delegate to function isn't really possible. The other way around.. I don't think so either. Something tells me the mechanism for a delegate call would screw up if the context pointer were null, or if the function weren't designed to be a delegate.

It would take some ASM hacking but It might be possible to place the fn ptr in the context pointer and have the delegate's function rearrange the stack and then call (with tail recursion) the function.

Isn't "this" basically a hidden parameter? A delegate would be a "this" together with a function pointer. Upon calling it would first load/push the hidden parameter before the call to the function. The question then is: does "this" interfere with a function's parameter? If, like in the MSVC case, "this" is always loaded into register ecx, it would just work: ecx would get "null" and the function gets called with the usual parameters. Since the function does not access [ecx] (it's a function, not a member of any class) the null would not interfere. L.
Apr 27 2006
parent reply Don Clugston <dac nospam.com.au> writes:
Lionello Lunesu wrote:
 BCS wrote:
 In article <e2qlrk$1uh5$1 digitaldaemon.com>, Jarrett Billingsley says...
 "Lionello Lunesu" <lio lunesu.remove.com> wrote in message 
 news:e2qig4$1ovk$1 digitaldaemon.com...
 Hi,

 Shouldn't a (pointer to a) function be convertible to a delegate? 
 Seems to me it's like casting an int to a long; the latter has "more 
 info" and can be converted back into the former without the loss of 
 data. The same holds for function/delegate: a function could be 
 converted to a delegate for "null"; by forgetting the "this" (null), 
 we get the function pointer back.

casting from delegate to function isn't really possible. The other way around.. I don't think so either. Something tells me the mechanism for a delegate call would screw up if the context pointer were null, or if the function weren't designed to be a delegate.

It would take some ASM hacking but It might be possible to place the fn ptr in the context pointer and have the delegate's function rearrange the stack and then call (with tail recursion) the function.

Isn't "this" basically a hidden parameter? A delegate would be a "this" together with a function pointer. Upon calling it would first load/push the hidden parameter before the call to the function. The question then is: does "this" interfere with a function's parameter? If, like in the MSVC case, "this" is always loaded into register ecx, it would just work: ecx would get "null" and the function gets called with the usual parameters. Since the function does not access [ecx] (it's a function, not a member of any class) the null would not interfere.

This is exactly right. Whether or not a function can be trivially converted to a delegate depends on the calling conventions used by the compiler. For the gory details, see my article http://www.codeproject.com/cpp/FastDelegate.asp which covers C++ member functions and delegates in more detail than anything else on the planet, and shows how ugly things become when a language doesn't define its ABI properly. -Don.
Apr 28 2006
parent Lionello Lunesu <lio lunesu.remove.com> writes:
Don Clugston wrote:
 For the gory details, see my article
 http://www.codeproject.com/cpp/FastDelegate.asp
 which covers C++ member functions and delegates in more detail than 
 anything else on the planet, and shows how ugly things become when a 
 language doesn't define its ABI properly.
 
 -Don.

Interesting article! Thanks for the link ;) L.
Apr 28 2006
prev sibling next sibling parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
Jarrett Billingsley wrote:
<snip>
 The other way around.. I don't think so either.  Something tells me the 
 mechanism for a delegate call would screw up if the context pointer were 
 null, or if the function weren't designed to be a delegate. 

But why? I can't understand why a function that doesn't touch the context pointer at all can't be trivially be converted to a delegate in which the context pointer is null. Stewart.
Apr 27 2006
next sibling parent reply Thomas Kuehne <thomas-dloop kuehne.cn> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Stewart Gordon schrieb am 2006-04-27:
 Jarrett Billingsley wrote:
<snip>
 The other way around.. I don't think so either.  Something tells me the 
 mechanism for a delegate call would screw up if the context pointer were 
 null, or if the function weren't designed to be a delegate. 

But why? I can't understand why a function that doesn't touch the context pointer at all can't be trivially be converted to a delegate in which the context pointer is null.

Let's try it: # class C{ # void foo(){ # printf("foo\n"); # } # # static void bar(){ # printf("bar\n"); # } # } # # int main(){ # C c = new C(); # # void delegate() d = &c.foo; # void* p = cast(void*)&d; # int* i = cast(int*) p; # # printf("delegate:\n"); # printf("%08X (instance)\n", c); # printf("%08X %08X (raw delegate)\n", i[0], i[1]); # d(); # # printf("\nfunction:\n"); # void function() f = &C.bar; # p = cast(void*)&f; # int* j = cast(int*) p; # printf("%08X (function)\n", &C.bar); # printf("%08X (raw function)\n", j[0]); # f(); # # printf("\nfunction -> instance delegate\n"); # i[1] = j[0]; # printf("%08X (instance)\n", c); # printf("%08X %08X (raw delegate)\n", i[0], i[1]); # d(); # # printf("\nfunction -> null delegate\n"); # i[0] = 0; # printf("%08X %08X (raw delegate)\n", i[0], i[1]); # d(); # # return 0; # } delegate: 55719FE0 (instance) 55719FE0 08049DF4 (raw delegate) foo function: 08049E0C (function) 08049E0C (raw function) bar function -> instance delegate 55719FE0 (instance) 55719FE0 08049E0C (raw delegate) bar function -> null delegate 00000000 08049E0C (raw delegate) bar I don't see any problems - except for missing compiler support. Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFEUSI03w+/yD4P9tIRAqZ+AJ9OsUv0mjgIcQCdLYV8gq9EQTdi/QCgp1TJ a4BV+qlS5NOAZrbQelm6vas= =+G5k -----END PGP SIGNATURE-----
Apr 27 2006
next sibling parent Lionello Lunesu <lio lunesu.remove.com> writes:
Thomas Kuehne wrote:
 -----BEGIN PGP SIGNED MESSAGE-----
 Hash: SHA1
 
 Stewart Gordon schrieb am 2006-04-27:
 Jarrett Billingsley wrote:
 <snip>
 The other way around.. I don't think so either.  Something tells me the 
 mechanism for a delegate call would screw up if the context pointer were 
 null, or if the function weren't designed to be a delegate. 

context pointer at all can't be trivially be converted to a delegate in which the context pointer is null.

Let's try it:

Great example, Thomas. It works, even if I add some parameters to the functions, or when I put the static function outside the class. It seems trivial to let the compiler allow the implicit conversion. Thanks, L.
Apr 27 2006
prev sibling parent reply Bruno Medeiros <brunodomedeirosATgmail SPAM.com> writes:
Thomas Kuehne wrote:
 -----BEGIN PGP SIGNED MESSAGE-----
 Hash: SHA1
 
 Stewart Gordon schrieb am 2006-04-27:
 Jarrett Billingsley wrote:
 <snip>
 The other way around.. I don't think so either.  Something tells me the 
 mechanism for a delegate call would screw up if the context pointer were 
 null, or if the function weren't designed to be a delegate. 

context pointer at all can't be trivially be converted to a delegate in which the context pointer is null.

Let's try it: # class C{ # void foo(){ # printf("foo\n"); # } # # static void bar(){ # printf("bar\n"); # } # } # # int main(){ # C c = new C(); # # void delegate() d = &c.foo; # void* p = cast(void*)&d; # int* i = cast(int*) p; # # printf("delegate:\n"); # printf("%08X (instance)\n", c); # printf("%08X %08X (raw delegate)\n", i[0], i[1]); # d(); # # printf("\nfunction:\n"); # void function() f = &C.bar; # p = cast(void*)&f; # int* j = cast(int*) p; # printf("%08X (function)\n", &C.bar); # printf("%08X (raw function)\n", j[0]); # f(); # # printf("\nfunction -> instance delegate\n"); # i[1] = j[0]; # printf("%08X (instance)\n", c); # printf("%08X %08X (raw delegate)\n", i[0], i[1]); # d(); # # printf("\nfunction -> null delegate\n"); # i[0] = 0; # printf("%08X %08X (raw delegate)\n", i[0], i[1]); # d(); # # return 0; # } delegate: 55719FE0 (instance) 55719FE0 08049DF4 (raw delegate) foo function: 08049E0C (function) 08049E0C (raw function) bar function -> instance delegate 55719FE0 (instance) 55719FE0 08049E0C (raw delegate) bar function -> null delegate 00000000 08049E0C (raw delegate) bar I don't see any problems - except for missing compiler support. Thomas

But, isn't it like Don said, that this thoroughly depends on the (so far undefined) calling conventions used by the compiler? -- Bruno Medeiros - CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
May 01 2006
parent BCS <BCS_member pathlink.com> writes:
In article <e35aik$2pd8$1 digitaldaemon.com>, Bruno Medeiros says...
Thomas Kuehne wrote:
 


 I don't see any problems - except for missing compiler support.
 
 Thomas
 
 

But, isn't it like Don said, that this thoroughly depends on the (so far undefined) calling conventions used by the compiler?

OTOH the compiler could do this just fine. It HAS to know the calling conventions.
May 01 2006
prev sibling parent reply pragma <pragma_member pathlink.com> writes:
In article <e2qv42$2gnt$1 digitaldaemon.com>, Stewart Gordon says...
Jarrett Billingsley wrote:
<snip>
 The other way around.. I don't think so either.  Something tells me the 
 mechanism for a delegate call would screw up if the context pointer were 
 null, or if the function weren't designed to be a delegate. 

But why? I can't understand why a function that doesn't touch the context pointer at all can't be trivially be converted to a delegate in which the context pointer is null.

Well, there's always the trival solution of declaring all of your "functions" as static members of a class - but that's not always possible. ;) Direct conversion can be done, but would require DMD to inject a good deal of code to replace an otherwise simple cast. For example, given the following: /**/ alias uint function(uint foo,uint bar) FunctionType; /**/ alias uint function(uint foo,uint bar) Delegatetype; /**/ FunctionType fn = &myfunc; An explicit cast from function to delegate is processed: /**/ Delegatetype dg = cast(DelegateType)&fn; In response to a function-to-delegate cast, with matching signatures, DMD would emit code equivalent to this: /**/ class FunctionTypeDelegate{ /**/ FunctionType fn; /**/ public this(FunctionType fn){ this.fn = fn; } /**/ uint dg(uint foo,uint bar){ return fn(foo,bar); } /**/ } /**/ DelegateType dg = &((new FunctionTypeDelegate(&fn)).dg); Using a proxy class/object in this way presents a side-effect-free way to tackle the problem. It's also friendly to different calling conventions, as well as going from one convention to another (the concept could even be useful for function-to-function and delegate-to-delegate where conventions don't match). DMD could simply generate the "FunctionTypeDelegate" class and provide it in the same manner as it would a template (in a COMDAT OMF record). This way, it won't cause issues if a cast elsewhere generates the same exact proxy. Now, this *could* be implemented via a template, barring some limitations. However, it would take some rather serious hacking to accomplish with the same kind of simplicity as a cast(), when you could probably hand-code the proxy above with far less effort (we're talking hours versus seconds, for the first time around). $0.02: Another way to look at it: any one type of delegate is compatible with exactly one type of function. So having the compiler interpret a cast() really doesn't gain you all that much. You may as well write a proxy for each delegate in cases where you absolutely have to be compatible with free-functions. Also, using proxies is the only way to accept multiple types of delegates in an abstract way, so it then becomes a matter of using just one convention for simplicity's sake. - EricAnderton at yahoo
Apr 27 2006
parent Bruno Medeiros <brunodomedeirosATgmail SPAM.com> writes:
pragma wrote:
 In article <e2qv42$2gnt$1 digitaldaemon.com>, Stewart Gordon says...
 Jarrett Billingsley wrote:
 <snip>
 The other way around.. I don't think so either.  Something tells me the 
 mechanism for a delegate call would screw up if the context pointer were 
 null, or if the function weren't designed to be a delegate. 

context pointer at all can't be trivially be converted to a delegate in which the context pointer is null.

Well, there's always the trival solution of declaring all of your "functions" as static members of a class - but that's not always possible. ;)

How would that be a solution? Static member functions of a class are free-functions. -- Bruno Medeiros - CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Apr 30 2006
prev sibling parent Lionello Lunesu <lio lunesu.remove.com> writes:
Jarrett Billingsley wrote:
 "Lionello Lunesu" <lio lunesu.remove.com> wrote in message 
 news:e2qig4$1ovk$1 digitaldaemon.com...
 Hi,

 Shouldn't a (pointer to a) function be convertible to a delegate? Seems to 
 me it's like casting an int to a long; the latter has "more info" and can 
 be converted back into the former without the loss of data. The same holds 
 for function/delegate: a function could be converted to a delegate for 
 "null"; by forgetting the "this" (null), we get the function pointer back.

Without the context pointer, a delegate cannot function properly. So casting from delegate to function isn't really possible.

I meant it the int-long way: you can cast an int to a long and back without loss of data. L.
Apr 27 2006
prev sibling parent Lionello Lunesu <lio lunesu.remove.com> writes:
I just noticed I got the subject the wrong way around :S
Apr 28 2006