www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Delegates and C function pointers

reply "Nicolas Sicard" <dransic gmail.com> writes:
I would like to register a D delegate to a C API that takes a
function pointer as a callback and a void* pointer to pass data
to this callback.

My solution is in http://dpaste.dzfl.pl/7d9b504b4b965.

Is this code correct? Is there something simpler or already in
Phobos that I have overlooked?

Thanks
-- Nicolas
Nov 08 2014
next sibling parent reply "Gary Willoughby" <dev nomad.so> writes:
On Saturday, 8 November 2014 at 12:23:45 UTC, Nicolas Sicard 
wrote:
 I would like to register a D delegate to a C API that takes a
 function pointer as a callback and a void* pointer to pass data
 to this callback.

 My solution is in http://dpaste.dzfl.pl/7d9b504b4b965.

 Is this code correct? Is there something simpler or already in
 Phobos that I have overlooked?

 Thanks
 -- Nicolas
It looks very similar to what i'm doing here: https://github.com/nomad-software/tkd/blob/master/source/tkd/element/element.d#L174 I think you can simplify it though by just using a delegate member in the data struct. Like this (untested): static struct Data { void delegate() callback; } static extern(C) void adapter(void* ptr) { auto d = *(cast(Data*) ptr); d.callback() }
Nov 08 2014
parent "Nicolas Sicard" <dransic gmail.com> writes:
On Saturday, 8 November 2014 at 15:29:01 UTC, Gary Willoughby
wrote:
 On Saturday, 8 November 2014 at 12:23:45 UTC, Nicolas Sicard 
 wrote:
 I would like to register a D delegate to a C API that takes a
 function pointer as a callback and a void* pointer to pass data
 to this callback.

 My solution is in http://dpaste.dzfl.pl/7d9b504b4b965.

 Is this code correct? Is there something simpler or already in
 Phobos that I have overlooked?

 Thanks
 -- Nicolas
It looks very similar to what i'm doing here: https://github.com/nomad-software/tkd/blob/master/source/tkd/element/element.d#L174 I think you can simplify it though by just using a delegate member in the data struct. Like this (untested): static struct Data { void delegate() callback; } static extern(C) void adapter(void* ptr) { auto d = *(cast(Data*) ptr); d.callback() }
Yes that's simpler! Thanks. -- Nicolas
Nov 08 2014
prev sibling parent reply "anonymous" <anonymous example.com> writes:
On Saturday, 8 November 2014 at 12:23:45 UTC, Nicolas Sicard
wrote:
 I would like to register a D delegate to a C API that takes a
 function pointer as a callback and a void* pointer to pass data
 to this callback.

 My solution is in http://dpaste.dzfl.pl/7d9b504b4b965.

 Is this code correct? Is there something simpler or already in
 Phobos that I have overlooked?

 Thanks
 -- Nicolas
I think it's a little more complicated than it needs to be. Instead of going delegate->DelegateData*->delegate you can pass a pointer to the delegate: void doSomethingFromD(void delegate() dg) { static extern(C) void adapter(void* ptr) { auto dg = *cast(void delegate()*)ptr; dg(); } dosomething(&adapter, &dg); } &dg is fine if dosomething calls the callback before doSomethingFromD returns. If the callback is stored and called later on, you have to put the delegate somewhere more lasting. And you have to make sure that the GC doesn't collect it in the meantime. In your code you're new-ing, but you don't keep the reference around in D-land. The GC would happily collect the delegate then, because it doesn't look in C-land for references. For example, you could add all callbacks to a module level array: void delegate()[] activeCallbacks; void doSomethingFromD(void delegate() dg) { static extern(C) void adapter(void* ptr) { auto dg = *cast(void delegate()*)ptr; dg(); } activeCallbacks ~= dg; dosomething(&adapter, &activeCallbacks[$ - 1]); } Then when everything's done and you know that the callbacks are not needed anymore, empty activeCallbacks so that the GC can collect them. Essentially, you're back to manual management, and have to think about the life times of the callbacks.
Nov 08 2014
parent "Nicolas Sicard" <dransic gmail.com> writes:
On Saturday, 8 November 2014 at 16:01:09 UTC, anonymous wrote:
 On Saturday, 8 November 2014 at 12:23:45 UTC, Nicolas Sicard
 wrote:
 I would like to register a D delegate to a C API that takes a
 function pointer as a callback and a void* pointer to pass data
 to this callback.

 My solution is in http://dpaste.dzfl.pl/7d9b504b4b965.

 Is this code correct? Is there something simpler or already in
 Phobos that I have overlooked?

 Thanks
 -- Nicolas
I think it's a little more complicated than it needs to be. Instead of going delegate->DelegateData*->delegate you can pass a pointer to the delegate: void doSomethingFromD(void delegate() dg) { static extern(C) void adapter(void* ptr) { auto dg = *cast(void delegate()*)ptr; dg(); } dosomething(&adapter, &dg); } &dg is fine if dosomething calls the callback before doSomethingFromD returns. If the callback is stored and called later on, you have to put the delegate somewhere more lasting. And you have to make sure that the GC doesn't collect it in the meantime. In your code you're new-ing, but you don't keep the reference around in D-land. The GC would happily collect the delegate then, because it doesn't look in C-land for references. For example, you could add all callbacks to a module level array: void delegate()[] activeCallbacks; void doSomethingFromD(void delegate() dg) { static extern(C) void adapter(void* ptr) { auto dg = *cast(void delegate()*)ptr; dg(); } activeCallbacks ~= dg; dosomething(&adapter, &activeCallbacks[$ - 1]); } Then when everything's done and you know that the callbacks are not needed anymore, empty activeCallbacks so that the GC can collect them. Essentially, you're back to manual management, and have to think about the life times of the callbacks.
Thanks for the advice about the GC managed references! I'll have a look at it. -- Nicolas
Nov 08 2014