www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - delegates with C linkage

reply Zarathustra <adam.chrapkowski gmail.com> writes:
I have obtained a strange error message when I tried to use delegate as an
argument in a C linkage function.

private extern (C) static {
  void d_foo_add_event_handler(Foo, void delegate());
}

class Foo {
  void addEventHandler(void delegate() handler) {
    d_foo_add_event_handler(this, handler);
  }
}
//__________________________________________________________________________
Error: function foo.d_foo_add_event_handler(Foo, void delegate()) does not
match parameter types (Foo, void delegate())
Error: cannot implicitly convert expression (handler) of type void delegate()
to void delegate()
The second error message is especially extreme.

When I use:
  struct DelegateVoid { void* ptr, funcptr; } // void delegate() ABI
  ...
  void d_foo_add_event_handler(Foo, DelegateVoid);
  ...
  d_foo_add_event_handler(this, *cast(DelegateVoid*)&handler);

Everything works well but I still have no idea why I cannot implicitly use
delegates.
Jun 04 2010
parent reply Mike Parker <aldacron gmail.com> writes:
Zarathustra wrote:
 I have obtained a strange error message when I tried to use delegate as an
 argument in a C linkage function.
 
 private extern (C) static {
   void d_foo_add_event_handler(Foo, void delegate());
 }
 
 class Foo {
   void addEventHandler(void delegate() handler) {
     d_foo_add_event_handler(this, handler);
   }
 }
 //__________________________________________________________________________
 Error: function foo.d_foo_add_event_handler(Foo, void delegate()) does not
 match parameter types (Foo, void delegate())
 Error: cannot implicitly convert expression (handler) of type void delegate()
 to void delegate()
 The second error message is especially extreme.
 
 When I use:
   struct DelegateVoid { void* ptr, funcptr; } // void delegate() ABI
   ...
   void d_foo_add_event_handler(Foo, DelegateVoid);
   ...
   d_foo_add_event_handler(this, *cast(DelegateVoid*)&handler);
 
 Everything works well but I still have no idea why I cannot implicitly use
 delegates.

For starters, your first delegate is declared in an extern(C) block, meaning it has C linkage. The second is declared outside of the block, meaning it has D linkage. So they are two different types of delegates. Secondly, I'm not sure if you can pass delegates to a C function. C code wouldn't understand delegates. They are not the same as function pointers. I suggest you use function pointers instead, paying attention to linkage. On an unrelated note, what are you expecting the static keyword to do in your function declaration? It does not restrict function visibility to module scope, as it does in C and C++. That's what private is for. Here, static is meaningless.
Jun 04 2010
next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Mike Parker <aldacron gmail.com> wrote:

 For starters, your first delegate is declared in an extern(C) block,  
 meaning it has C linkage. The second is declared outside of the block,  
 meaning it has D linkage. So they are two different types of delegates.

If this is correct, the problem should be fixable by writing extern(C) void d_foo_add_event_handler(Foo, void delegate()); or (not entirely sure about this one) alias void delegate() voidDelegate; extern(C) { void d_foo_add_event_handler(Foo, voidDelegate); } -- Simen
Jun 05 2010
prev sibling next sibling parent reply Zarathustra <adam.chrapkowski gmail.com> writes:
 For starters, your first delegate is declared in an extern(C) block,
 meaning it has C linkage. The second is declared outside of the block,
 meaning it has D linkage. So they are two different types of delegates.

 Secondly, I'm not sure if you can pass delegates to a C function. C code
 wouldn't understand delegates. They are not the same as function
 pointers. I suggest you use function pointers instead, paying attention
 to linkage.

Of course It is possible, because I have done it and it works pretty well.
 On an unrelated note, what are you expecting the static keyword to do in
 your function declaration? It does not restrict function visibility to
 module scope, as it does in C and C++. That's what private is for. Here,
 static is meaningless.

I expecting nothing :) I use it only to highlight that it is not a member function.
Jun 05 2010
parent reply dennis luehring <dl.soluz gmx.net> writes:
Am 05.06.2010 13:33, schrieb Zarathustra:
  Secondly, I'm not sure if you can pass delegates to a C function. C code
  wouldn't understand delegates. They are not the same as function
  pointers. I suggest you use function pointers instead, paying attention
  to linkage.

Of course It is possible, because I have done it and it works pretty well.

only with static methods - a real delegate needs the this-pointer in a register (not on the stack), thats the main difference between function ptr and delegates, thats why you can't use a (non static) method on for example the win-api callbacks, because the caller don't know your object
Jun 05 2010
parent reply Zarathustra <adam.chrapkowski gmail.com> writes:
 only with static methods - a real delegate needs the this-pointer in a
 register (not on the stack), thats the main difference between function
 ptr and delegates, thats why you can't use a (non static) method on for
 example the win-api callbacks, because the caller don't know your object

gcc: __attribute__((regparm (1))) put_first_paramerter_to_eax(d_object this){...} __attribute__((regparm (1))) - puts the first function parameter to EAX.
Jun 05 2010
parent reply dennis luehring <dl.soluz gmx.net> writes:
Am 05.06.2010 15:42, schrieb Zarathustra:
  only with static methods - a real delegate needs the this-pointer in a
  register (not on the stack), thats the main difference between function
  ptr and delegates, thats why you can't use a (non static) method on for
  example the win-api callbacks, because the caller don't know your object

gcc: __attribute__((regparm (1))) put_first_paramerter_to_eax(d_object this){...} __attribute__((regparm (1))) - puts the first function parameter to EAX.

ok so your using gcc and some "extensions"
Jun 05 2010
parent reply dennis luehring <dl.soluz gmx.net> writes:
Am 05.06.2010 16:03, schrieb dennis luehring:
 Am 05.06.2010 15:42, schrieb Zarathustra:
   only with static methods - a real delegate needs the this-pointer in a
   register (not on the stack), thats the main difference between function
   ptr and delegates, thats why you can't use a (non static) method on for
   example the win-api callbacks, because the caller don't know your object

gcc: __attribute__((regparm (1))) put_first_paramerter_to_eax(d_object this){...} __attribute__((regparm (1))) - puts the first function parameter to EAX.

ok so your using gcc and some "extensions"

but that will help on the caller side (you using gcc?) but D still won't accept an delegat in an extern C because this type does not exists in the C world btw: can show us code where you do this and it works - why don't you use your working code as an example?
Jun 05 2010
next sibling parent Zarathustra <adam.chrapkowski gmail.com> writes:
 but that will help on the caller side (you using gcc?) but D still won't
 accept an delegat in an extern C because this type does not exists in
 the C world

 btw: can show us code where you do this and it works - why don't you use
 your working code as an example?

project is big and the second one is that the possibility of doing that haven't been a part of my question. If you want to know how to do that look at the examples. (1) Look at the following (probably most simple) example of d thiscall under c: /* * test.c; */ #define d_foo_get_add _D4test3Foo6getAddMFiZi #include <stdio.h> typedef struct { void* vptr ; /* unused */ void* monitor; /* unused */ int a; } st_d_foo, *DFoo; __attribute__((regparm(1), stdcall)) int d_foo_get_add(DFoo this, int o_addme) { return this->a += o_addme; } ______________________________________________________________________________ /* * test.d */ module test; static import std.stdio; public class Foo { private int a; public int getAdd(int); } void main() { Foo foo = new Foo(); foo.a = 5; std.stdio.writefln("foo.a = %d", foo.getAdd(4)); } ______________________________________________________________________________ /* command line: */ gcc test.c -c -otest.c.o dmd test.d -c -oftest.d.o dmd test.d.o test.c.o -oftest ______________________________________________________________________________ /* output is of course*/ foo.a = 9 ______________________________________________________________________________ (2) And now lets add delegate: /* * test.c; */ #define d_foo_get_var _D4test3Foo6getAddMFiZi #define d_foo_dlg _D4test3Foo3dlgMFDFZiZv #define call_delegate(x) (x).funcptr((x).ptr) #include <stdio.h> typedef struct { void* vptr ; /* unused */ void* monitor; /* unused */ int a; } st_d_foo, *DFoo; typedef struct { void* ptr; int (__attribute__((regparm(1), stdcall)) * funcptr)(void*); } st_d_delegate; __attribute__((regparm(1), stdcall)) int d_foo_get_var(DFoo this, int o_addme) { return this->a += o_addme; } __attribute__((regparm(1), stdcall)) void d_foo_dlg(DFoo this, st_d_delegate o_handler) { printf("dlg = %d\n", call_delegate(o_handler)); } ______________________________________________________________________________ /* * test.d */ module test; static import std.stdio; public class Foo { private int a; public int getAdd(int); public void dlg(int delegate()); } public class Bar { int get7() { return 7; } } void main() { Foo foo = new Foo(); foo.a = 5; std.stdio.writefln("foo.a = %d", foo.getAdd(4)); Bar bar = new Bar(); foo.dlg(&bar.get7); } ______________________________________________________________________________ command line is the same ______________________________________________________________________________ /* output */ foo.a = 9 dlg = 7
Jun 05 2010
prev sibling next sibling parent reply dennis luehring <dl.soluz gmx.net> writes:
Am 06.06.2010 11:33, schrieb Simen kjaeraas:
 Also, pointers to delegates can be passed to C-linkage functions. A
 delegate is nothing but a struct, and as such there is no reason for
 it not to be passable to a C-linkage function.

my fault - yes its possible to use delegates in non D but delegat-struct knowing languages (and a small pice of special calling code), sorry Zarathrustra i missed the point of your post in whole
I say this is a bug.

me too are delegats part of the ABI, i can't find a delegat calling scheme in the ABI-Description - should this be in EAX, as last/first part on stack, ECX? wouldn't it be nice to have an complete call/use D features through c example around?
Jun 06 2010
parent reply Zarathustra <adam.chrapkowski gmail.com> writes:
 my fault - yes its possible to use delegates in non D but delegat-struct
 knowing languages (and a small pice of special calling code), sorry
 Zarathrustra i missed the point of your post in whole

 wouldn't it be nice to have an complete call/use D features through c
 example around?

member functions) and the dcall (for static functions). Additionally would be great if DMC will works on other OS than MSWindows. But probably there are only my dreams ;) Btw. I think there is a good chance for create the set examples which show how to use D features on the C level. I will thinking about it in my free time :) The idea is really interesting and it would be useful(especially to better understand these mechanisms) for some people.
 are delegats part of the ABI, i can't find a delegat calling scheme in
 the ABI-Description - should this be in EAX, as last/first part on
 stack, ECX?

= 'this'(context pointer) to EAX and call funcptr (pointer to function). If I remember well, the 'this' pointer is passed to functions by ECX in C++. The delegate structure layout is described in ABI. ABI, disassembler and trail-end-error method will give you (and me too, because I still need to explore many things) an answer to many questions.
 I say this is a bug.

Jun 06 2010
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Zarathustra:
 I think there is a good chance for create the set examples which show how to
use D
 features on the C level. I will thinking about it in my free time :) The idea
is
 really interesting and it would be useful(especially to better understand these
 mechanisms) for some people.

I have asked for something similar. In such explanations I'd even like some boxes&arrows diagrams that show the data structures with the pointers, etc. Even few of such images can improve the understanding. Bye, bearophile
Jun 06 2010
prev sibling parent dennis luehring <dl.soluz gmx.net> writes:
Am 06.06.2010 17:30, schrieb Zarathustra:
  are delegats part of the ABI, i can't find a delegat calling scheme in
  the ABI-Description - should this be in EAX, as last/first part on
  stack, ECX?

= 'this'(context pointer) to EAX and call funcptr (pointer to function). If I remember well, the 'this' pointer is passed to functions by ECX in C++. The delegate structure layout is described in ABI. ABI, disassembler and trail-end-error method will give you (and me too, because I still need to explore many things) an answer to many questions.

ok and it would be nice to have an clear description of this ABI+disasm trial-error-reusults in the ABI-Description - i think your example is needed to show walter the missing parts of the ABI-Doc
Jun 06 2010
prev sibling parent Don <nospam nospam.com> writes:
Simen kjaeraas wrote:
 dennis luehring <dl.soluz gmx.net> wrote:
 
 D still won't accept an delegat in an extern C because this type does 
 not exists in the C world

Nor do classes, and those certainly can be passed to a C-linkage function.

Yes, but I think that's a bug too. Quite a horrible one, in fact, since the class may get GC'd. On the "interfaceToC" page, class, type[], type[type] and delegate() are listed as having no C equivalent. They should all fail to compile.
Jun 07 2010
prev sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
dennis luehring <dl.soluz gmx.net> wrote:

 D still won't accept an delegat in an extern C because this type does  
 not exists in the C world

Nor do classes, and those certainly can be passed to a C-linkage function. Also, pointers to delegates can be passed to C-linkage functions. A delegate is nothing but a struct, and as such there is no reason for it not to be passable to a C-linkage function. I say this is a bug. -- Simen
Jun 06 2010