digitalmars.D.learn - Using delegates for C callbacks.
- Leandro Lucarella (66/66) Feb 01 2008 Hi! I'm doing some code to interface with C and I need to use D delegate...
- BCS (25/25) Feb 01 2008 Reply to Leandro,
- Kirk McDonald (39/72) Feb 01 2008 Try something like this:
- Leandro Lucarella (47/84) Feb 01 2008 Thanks, Kirk! The trick about passing the C.foo function instead of the
- Leandro Lucarella (43/47) Feb 01 2008 Well, I had some problems with that code, and wasn't general enough. Wha...
- Leandro Lucarella (52/58) Feb 01 2008 Well, it was that wrong, it only worked with delegates without arguments...
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
Feb 01 2008
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.
Feb 01 2008
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
Feb 01 2008
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
Feb 01 2008
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
Feb 01 2008
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
Feb 01 2008









BCS <ao pathlink.com> 