Thread overview | ||||||||
---|---|---|---|---|---|---|---|---|
|
February 01, 2008 Using delegates for C callbacks. | ||||
---|---|---|---|---|
| ||||
Hi! I'm doing some code to interface with C and I need to use D delegates as C callbacks.
I've tested a lot of posibilities to do this, asking on IRC channels, but nothing seems to work entirely.
This is the closer I got:
1 import std.stdio;
2
3 extern (C) void f(void function(void*) cb, void* arg)
4 {
5 cb(arg);
6 }
7
8 extern (C) static void thunk(alias Fn)(void* arg)
9 {
10 Fn(arg);
11 }
12
13 void fcb(void* arg)
14 {
15 writefln("fcb: ", *cast (int*) arg);
16 }
17
18 class C
19 {
20 int x = 1;
21 void dcb(void* arg)
22 {
24 writefln("dcb: ", *cast (int*) arg, " = ", x);
25 }
26 }
27
28 void main()
29 {
30 int x = 1;
31 C c = new C;
32 thunk!(fcb)(&x);
33 //thunk!(c.dcb)(&x);
34 f(&thunk!(fcb), &x);
35 //f(&thunk!(c.dcb), &x);
36 }
The thunk for the plain function adaptation works fine, but not the delegate. If I uncomment the code at line 33 I get:
thunk.d:10: Error: need 'this' to access member dcb
I have a void* pointer to use, I can "inject" the this pointer, but I don't know how. Doing something like:
8 extern (C) static void thunk(alias Fn)(void* arg)
9 {
10 Fn.ptr = arg;
11 Fn();
12 }
Doesn't work, it says:
thunk.d:10: Error: no property 'ptr' for type 'void'
thunk.d:10: Error: constant dcb().ptr is not an lvalue
thunk.d:10: Error: cannot implicitly convert expression (arg) of type void* to int
Which I don't understand (specially the part of "no property 'ptr' for type 'void'", why dcb is void? I don't think I understand very well the semantics of an alias template parameter =S
Is there any recomended solution for this? I think it (or should be) a
fairly common problem (at least when making D bindings for C libraries).
--
Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/
----------------------------------------------------------------------------
GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05)
----------------------------------------------------------------------------
FINALMENTE EL CABALLITO FABIAN VA A PASAR UNA BUENA NAVIDAD
-- Crónica TV
|
February 01, 2008 Re: Using delegates for C callbacks. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Leandro Lucarella | Reply to Leandro, I have not tested this and don't have time to do so now const auto sig = [ /* the binary form of nop; nop; nop; */ ]; R Thunker(T, A...)(A a) // general thunk with tags { T delegate(A) dg; dg.fn = 0xdeadbeef; dg.ptr = 0xabadf00d; return dg(a); asm {nop; nop; nop; nop; } } R function(A) Thuked(R, A...)(R delegate(A) dg) // copy general thunk and replace tags with real stuff. { byte* start = cast(byte*)(&R Thunker!(T, A)); // get thunk int stop = lengthTo(start, sig); // find end start = start[0..stop].dup; // copy stop = lengthTo(start, 0xdeadbeaf); // relpace (cast(void**)(start+stop))[0] = dg.fn; stop = lengthTo(start, 0xabadf00d); // relpace (cast(void**)(start+stop))[0] = dg.ptr; return cast(typeof(ret)) start.ptr; // return } note: the dg.ptr and dg.fn may be incorrect but IIRC there is a way to do that. |
February 01, 2008 Re: Using delegates for C callbacks. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Leandro Lucarella | Leandro Lucarella wrote: > Hi! I'm doing some code to interface with C and I need to use D delegates > as C callbacks. > > I've tested a lot of posibilities to do this, asking on IRC channels, but > nothing seems to work entirely. [snip] > > The thunk for the plain function adaptation works fine, but not the > delegate. If I uncomment the code at line 33 I get: > > thunk.d:10: Error: need 'this' to access member dcb > > I have a void* pointer to use, I can "inject" the this pointer, but I > don't know how. Doing something like: > > 8 extern (C) static void thunk(alias Fn)(void* arg) > 9 { > 10 Fn.ptr = arg; > 11 Fn(); > 12 } > > Doesn't work, it says: > thunk.d:10: Error: no property 'ptr' for type 'void' > thunk.d:10: Error: constant dcb().ptr is not an lvalue > thunk.d:10: Error: cannot implicitly convert expression (arg) of type void* to int > > Which I don't understand (specially the part of "no property 'ptr' for > type 'void'", why dcb is void? I don't think I understand very well the > semantics of an alias template parameter =S > > > Is there any recomended solution for this? I think it (or should be) a > fairly common problem (at least when making D bindings for C libraries). > Try something like this: extern(C) void f(void function(void*) fn, void* closure) { fn(closure); } class C { void foo(int i) {} } struct Closure { C self; int arg; } // It is possible to do some template trickery in order to // generalize this thunk for any function signature and any // class, but it is simpler to get the point across with a // concrete example. extern(C) void thunk(alias Fn)(void* _closure) { void delegate(int) dg; Closure* closure = cast(Closure*)_closure; dg.funcptr = &Fn; dg.ptr = cast(void*)(closure.self); dg(closure.arg); } void main() { C c = new C; auto closure = new Closure; closure.self = c; closure.arg = 20; // Note that we're passing the function C.foo and not // the method c.foo. thunk!(C.foo)(closure); f(&thunk!(C.foo), closure); } -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.org |
February 02, 2008 Re: Using delegates for C callbacks. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Kirk McDonald | Kirk McDonald, el 1 de febrero a las 14:23 me escribiste: [snip] > Try something like this: > > extern(C) void f(void function(void*) fn, void* closure) { > fn(closure); > } > > class C { > void foo(int i) {} > } > > struct Closure { > C self; > int arg; > } > > // It is possible to do some template trickery in order to > // generalize this thunk for any function signature and any > // class, but it is simpler to get the point across with a > // concrete example. > extern(C) void thunk(alias Fn)(void* _closure) { > void delegate(int) dg; > Closure* closure = cast(Closure*)_closure; > dg.funcptr = &Fn; > dg.ptr = cast(void*)(closure.self); > dg(closure.arg); > } > > void main() { > C c = new C; > auto closure = new Closure; > closure.self = c; > closure.arg = 20; > // Note that we're passing the function C.foo and not > // the method c.foo. > thunk!(C.foo)(closure); > f(&thunk!(C.foo), closure); > } Thanks, Kirk! The trick about passing the C.foo function instead of the c.foo method was defenely the trick. I adapted your example to what I needed, which is simpler because I don't need the Closure wrapper, so the code is more general without extra complexity: import std.stdio; extern(C) void f(void function(void*) fn, void* closure) { fn(closure); } class C { int x; void foo() { writefln("foo: ", x); } } // It is possible to do some template trickery in order to // generalize this thunk for any function signature and any // class, but it is simpler to get the point across with a // concrete example. extern(C) void thunk(alias Fn)(void* closure) { void delegate() dg; dg.funcptr = &Fn; dg.ptr = closure; dg(); } void main() { C c = new C; // Note that we're passing the function C.foo and // not the method c.foo. c.x = 1; thunk!(C.foo)(cast (void*) c); c.x = 2; f(&thunk!(C.foo), cast (void*) c); } PS: Thanks BCS for the answer. I didn't try it either because I didn't understand it and found it too twisted, I was looking for something simpler :) -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- Dentro de 30 años Argentina va a ser un gran supermercado con 15 changuitos, porque esa va a ser la cantidad de gente que va a poder comprar algo. -- Sidharta Wiki |
February 02, 2008 Re: Using delegates for C callbacks. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Leandro Lucarella | Leandro Lucarella, el 1 de febrero a las 22:09 me escribiste: > Thanks, Kirk! The trick about passing the C.foo function instead of the c.foo method was defenely the trick. I adapted your example to what I needed, which is simpler because I don't need the Closure wrapper, so the code is more general without extra complexity: Well, I had some problems with that code, and wasn't general enough. What I really wanted was to be able to do something like this: import std.stdio; extern(C) void f(void function(void*) fn, void* closure) { fn(closure); } class C { int x; void foo() { writefln("C.foo: ", x); } } void thunk(void delegate() dg) { alias extern (C) void function(void*) fp; f(cast (fp) dg.funcptr, dg.ptr); } void main() { void foo() { writefln("foo"); } C c = new C; c.x = 1; thunk(&c.foo); thunk(&foo); } This compiles... and *runs*! At least with GDC (DMD complains about "no property 'funcptr' for type 'void delegate()'", I can see a lot of problems with that code but that :S) on Linux. Is this too wrong? I guess the casting from dg.funcptr to an extern (C) function is not (I don't know if D calling convention is warrantied to be the same as C, and I don't know if is warrantied that the first argument to a delegate is the context pointer), but I really want the generality and simplicity of this code, it makes no sense to need code more complex than that to do what I want to do. PS: Should I move this to digitalmars.D? -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- EXTRAÑA RELACION ENTRE UN JUBILADO Y UN JABALI -- Crónica TV |
February 02, 2008 Re: Using delegates for C callbacks. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Leandro Lucarella | Leandro Lucarella, el 2 de febrero a las 02:17 me escribiste: > Is this too wrong? I guess the casting from dg.funcptr to an extern (C) function is not (I don't know if D calling convention is warrantied to be the same as C, and I don't know if is warrantied that the first argument to a delegate is the context pointer), but I really want the generality and simplicity of this code, it makes no sense to need code more complex than that to do what I want to do. Well, it was that wrong, it only worked with delegates without arguments. I saw that the D calling conventions are defined in the D ABI specification so they aren't always the same as the C calling conventions, I guess. I finally decided to go with this: import std.stdio; extern(C) void c_f(void function(int, void*) fn, int data, void* closure) { fn(data, closure); } class C { int x; void foo(int data) { writefln("C.foo: x=", x, ", data=", data); } } struct Delegate { void delegate(int) dg; } extern (C) void thunk(int revents, void* data) { auto d = cast (Delegate*) data; d.dg(revents); } void d_f(void delegate(int) dg, int data) { auto d = new Delegate; d.dg = dg; c_f(&thunk, data, d); } void main() { void foo(int data) { writefln("foo: data=", data); } C c = new C; c.x = 1; d_f(&foo, 10); d_f(&c.foo, 5); } I'm not crazy about the heap allocation, but at least is simple, safe and general. And the templated thunk version wont work either if I want the API usage to be simple (with templates, users will be forced to pass the function pointer, which can be calculated at compile-time, separated from the context pointer, which is always a runtime value). -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- JUGAR COMPULSIVAMENTE ES PERJUDICIAL PARA LA SALUD. -- Casino de Mar del Plata |
Copyright © 1999-2021 by the D Language Foundation