www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Callbacks in D as void functions

reply "Wsdes" <valentino.dileonardo bccgi.com> writes:
Hi all,

I already posted this to Code Project, but maybe you have a
solution for my problem.

I am trying to write a wrapper for a C API in D. In C I have the
following definition of a callback type:

typedef void (*Callback)(void*);

In a struct all members are of the type Callback. In the main
function each member is assigned a function like this:

Events.OnData = MyDtaCB;

Here the MyDtaCB function has no prototype but is defined as
follows:

void MyDtaCB(void* pVoid){
// Do stuff
}

Now I tried to port this to D. The wiki says that callback
functions should be declared as integer functions, so my D code
looks like this:

extern (C) alias CEventCallback_t = int function(int, int);
extern (C) CEventCallback_t MyDtaCB(void*);

Here's the catch: when I try to assign the MyDtaCB function to a
member of the Events struct, I get a compilation error because
MyDtaCB is a void function and therefore returns no value. Seems
to work in C, though, but I don't know how to handle this in D.

The D tutorial has a function called getCallback() which returns
the void function but itself has the Callback type as return
value. However, in D this would lead to the same error as before
since the void function has no non-empty return value.

Any suggestions how to accomplish this? My thought now was to
screw the function definitions of the C API and implement them in
D. But what's the point in having the API then?

Thanks in advance.
Nov 13 2014
next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Wsdes:

 The wiki says that callback
 functions should be declared as integer functions,
What's bad in returning void? Bye, bearophile
Nov 13 2014
prev sibling next sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 11/13/14 9:49 AM, Wsdes wrote:
 Hi all,

 I already posted this to Code Project, but maybe you have a
 solution for my problem.

 I am trying to write a wrapper for a C API in D. In C I have the
 following definition of a callback type:

 typedef void (*Callback)(void*);
in D, this would be: alias Callback = void function(void *);
 In a struct all members are of the type Callback. In the main
 function each member is assigned a function like this:

 Events.OnData = MyDtaCB;

 Here the MyDtaCB function has no prototype but is defined as
 follows:

 void MyDtaCB(void* pVoid){
 // Do stuff
 }

 Now I tried to port this to D. The wiki says that callback
 functions should be declared as integer functions, so my D code
 looks like this:

 extern (C) alias CEventCallback_t = int function(int, int);
 extern (C) CEventCallback_t MyDtaCB(void*);
This doesn't look right. MyDtaCB is a function taking void *, that returns a function pointer. Can you point at the wiki you are talking about?
 Here's the catch: when I try to assign the MyDtaCB function to a
 member of the Events struct, I get a compilation error because
 MyDtaCB is a void function and therefore returns no value. Seems
 to work in C, though, but I don't know how to handle this in D.
C is a lot looser in function pointer types. Without more context of what you are doing, it's hard to diagnose this any further. -Steve
Nov 13 2014
prev sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Thursday, 13 November 2014 at 14:50:00 UTC, Wsdes wrote:
 I am trying to write a wrapper for a C API in D. In C I have the
 following definition of a callback type:

 typedef void (*Callback)(void*);
I would translate this directly to D: extern(C) alias Callback = void function(void*);
 Here the MyDtaCB function has no prototype but is defined as
 follows:

 void MyDtaCB(void* pVoid){
 // Do stuff
 }
And don't forget extern(C) on this too: extern(C) void MyDtaCB(void* pVoid) { } And assign it to the struct: Events.OnData = &MyDtaCB; Unless you have a link to the wiki that talks about ints, maybe that says something different, but I like to keep my C and D code that calls it looking pretty much the same when I can.
Nov 13 2014
parent reply "Wsdes" <valentino.dileonardo bccgi.com> writes:
On Thursday, 13 November 2014 at 15:17:45 UTC, Adam D. Ruppe 
wrote:
 On Thursday, 13 November 2014 at 14:50:00 UTC, Wsdes wrote:
 I am trying to write a wrapper for a C API in D. In C I have 
 the
 following definition of a callback type:

 typedef void (*Callback)(void*);
I would translate this directly to D: extern(C) alias Callback = void function(void*);
 Here the MyDtaCB function has no prototype but is defined as
 follows:

 void MyDtaCB(void* pVoid){
 // Do stuff
 }
And don't forget extern(C) on this too: extern(C) void MyDtaCB(void* pVoid) { } And assign it to the struct: Events.OnData = &MyDtaCB; Unless you have a link to the wiki that talks about ints, maybe that says something different, but I like to keep my C and D code that calls it looking pretty much the same when I can.
Hi, thank you everybody for your replies. First of all, the link to the wiki that has an example of callbacks in C and D: http://dlang.org/interfaceToC.html Secondly, I tried your code and that was exactly what I was thinking and what I tried before. Then I thought I'd turn to the wiki example, so that's where the int function came from. In the meantime I changed that to return void so I gave you my old code :( Anyway, I think I got the problem solved. Well, there seems to never have been any problem as I am taught now. I asked the developer of the C API this morning if I should try to implement the callback functions redundantly in D and he said he will have a look with me later. So now it turns out that I cannot call the extern callback function because it's not provided within the library O.O I was already wondering why there are no prototypes to these callback functions but I assumed they are provided from another library that I don't have direct access to... So the solution to my problem finally is to define the function in my D file and then cast the function pointer to the Callback type like this: void MyDtaCB(void* v){ // Do stuff } Events.OnData = cast(Callback) &MyDtaCB; At least this one compiles. Now I'm facing other problems with struct members but I guess I'll figure this out. If not, I'll ask again so stay tuned for some more unsolved mysteries ;) Still, thank you all for your effort and time. Keep it up! :)
Nov 13 2014
next sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Thursday, 13 November 2014 at 15:58:34 UTC, Wsdes wrote:
 void MyDtaCB(void* v){
     // Do stuff
 }

 Events.OnData = cast(Callback) &MyDtaCB;
this compiles but might crash, the extern(C) is important on a callback to be used from a C program. I'd really recommend adding that to the function, even if you cast when assigning to the struct.
Nov 13 2014
prev sibling next sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 11/13/14 10:58 AM, Wsdes wrote:

 First of all, the link to the wiki that has an example of callbacks in C
 and D:
 http://dlang.org/interfaceToC.html

 Secondly, I tried your code and that was exactly what I was thinking and
 what I tried before. Then I thought I'd turn to the wiki example, so
 that's where the int function came from. In the meantime I changed that
 to return void so I gave you my old code :(
The int example in the above "wiki" page (not technically a wiki, but that's OK) is because the function type in C is ALSO taking ints :) Not all callback functions are the same. Your callback type in D should be the same as the C definition, but with extern(C). Can you tell us the exact prototype of the function you are trying to assign, whether it is a member of a struct, class, or is an inner function, and the exact prototype you declared the Callback type to be? I don't need to see any code of the function. -Steve
Nov 13 2014
prev sibling parent Mike Parker <aldacron gmail.com> writes:
On 11/14/2014 12:58 AM, Wsdes wrote:

 Anyway, I think I got the problem solved. Well, there seems to never
 have been any problem as I am taught now. I asked the developer of the C
 API this morning if I should try to implement the callback functions
 redundantly in D and he said he will have a look with me later. So now
 it turns out that I cannot call the extern callback function because
 it's not provided within the library O.O I was already wondering why
 there are no prototypes to these callback functions but I assumed they
 are provided from another library that I don't have direct access to...
Callbacks wouldn't be implemented in the library. They are intended to be implemented by the user of the library. That's why they're called "callbacks." The library "calls back" your function. In C, this is how event programming is usually handled. You pass a function pointer to the library and when some event occurs, the library calls your function (the callback).
 So the solution to my problem finally is to define the function in my D
 file and then cast the function pointer to the Callback type like this:

 void MyDtaCB(void* v){
      // Do stuff
 }

 Events.OnData = cast(Callback) &MyDtaCB;
Define it as extern( C ) and you don't need the cast. extern( C ) MyDtaCB( void* v ) { ... } Events.OnData = &MyDtaCB; Callbacks intended to be passed to C should *always* be implemented as extern( C ).
Nov 13 2014