www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Running a delegate inside a C function

reply Denis Martinez via Digitalmars-d-learn writes:
I am trying to make a binding for the Jack Audio Connection Kit, which I
figured would be a good exercise to start and hopefully to replace C++ for my
long term audio work.
I am making good progress (complete C API covered), and currently I am hitting
a brick wall where delegates are concerned, while making some proper idiomatic
D wrapper classes.

Below I have copied some relevant fragments of code, with the debug messages.

I want to pass a delegate as callback to a C function which is designed to
receive the usual (function, void*) arguments.
The delegate is passed to JackClient's process_callback method. Here I make a C
function for the purpose of wrapping, which receives into (void*)arg the
delegate pointer obtained by &dg. Later in the method I save the delegate in
the instance to protect it form being collected.

The problem is, as the C function is being called back, it crashes.
The trace tells that inside it the delegate address is determined correctly,
while dg.ptr and dg.funcptr are not.
Am I doing something wrong here?

This is the trace of the execution:
New jack_client with name: Toto
passed to jack_set_process_thread f=0x460950 arg=0x7fff8b32fa20
 delegate with funcptr=0x451d40 ptr=null
Press a key to stop.
1. enter with arg=0x7fff8b32fa20
2. delegate with funcptr=0xa ptr=0x69e8c0
Error executing command run: Program exited with code -11


--- app.d

  JackClient client = new JackClient;
  client.open("Toto", jack_options_t.JackNoStartServer, null);
  scope(exit) client.close();

  writeln("New jack_client with name: " ~ client.get_name());

  client.process_callback = delegate int(jack_nframes_t nframes) {
    writefln("PROCESSING %d frames", nframes);
    return 0;
  };

--- jack/jack.d

alias JackProcessDelegate = int delegate(jack_nframes_t nframes);

class JackClient {
  JackProcessDelegate process_callback_;

   property
  void process_callback(JackProcessDelegate dg)
  {
    extern (C) JackProcessCallback f = function int(jack_nframes_t nframes,
void *arg)
      {
        // return (*cast(JackProcessDelegate *)arg)(nframes);
        int ret;
        writefln("1. enter with arg=%#x", arg);
        auto dg = cast(JackProcessDelegate *)arg;
        writefln("2. delegate with funcptr=%#x ptr=%#x", dg.funcptr, dg.ptr);
        ret = (*dg)(nframes);
        writeln("3. end");
        return ret;
      };

    writefln("passed to jack_set_process_thread f=%#x arg=%#x", f, &dg);
    writefln(" delegate with funcptr=%#x ptr=%#x", dg.funcptr, dg.ptr);

    int ret = jack_set_process_callback(handle_, f, &dg);
    if (ret != 0) {
      throw new JackError("jack_set_process_callback");
    }
    process_callback_ = dg;
  }
}

 		 	   		  
Jun 07 2014
next sibling parent reply "Chris Cain" <zshazz gmail.com> writes:
On Saturday, 7 June 2014 at 20:04:41 UTC, Denis Martinez via 
Digitalmars-d-learn wrote:
     int ret = jack_set_process_callback(handle_, f, &dg);
&dg here is giving you a pointer to the dg variable sitting on the stack. The stack is almost certainly getting overwritten at some point.
Jun 07 2014
parent reply "Chris Cain" <zshazz gmail.com> writes:
On Saturday, 7 June 2014 at 20:21:26 UTC, Chris Cain wrote:
 On Saturday, 7 June 2014 at 20:04:41 UTC, Denis Martinez via 
 Digitalmars-d-learn wrote:
     int ret = jack_set_process_callback(handle_, f, &dg);
&dg here is giving you a pointer to the dg variable sitting on the stack. The stack is almost certainly getting overwritten at some point.
I'll add that to fix the problem, you're going to have to write it so that your process_callback function takes either a pointer: void process_callback(JackProcessDelegate* dg) or make it by reference: void process_callback(in JackProcessDelegate dg) //... or void process_callback(ref JackProcessDelegate dg) But do note that the same problem may apply to the person calling it... if they're storing the delegate "structure" on the stack, it might also be clobbered in much the same way. Take care
Jun 07 2014
parent reply "Denis Martinez" <denis.martinez live.com> writes:
On Saturday, 7 June 2014 at 20:37:48 UTC, Chris Cain wrote:
 On Saturday, 7 June 2014 at 20:21:26 UTC, Chris Cain wrote:
 On Saturday, 7 June 2014 at 20:04:41 UTC, Denis Martinez via 
 Digitalmars-d-learn wrote:
     int ret = jack_set_process_callback(handle_, f, &dg);
&dg here is giving you a pointer to the dg variable sitting on the stack. The stack is almost certainly getting overwritten at some point.
I'll add that to fix the problem, you're going to have to write it so that your process_callback function takes either a pointer: void process_callback(JackProcessDelegate* dg) or make it by reference: void process_callback(in JackProcessDelegate dg) //... or void process_callback(ref JackProcessDelegate dg) But do note that the same problem may apply to the person calling it... if they're storing the delegate "structure" on the stack, it might also be clobbered in much the same way. Take care
Thanks for the answer Chris, you are correct. I was expecting the closure to work similarly to Clang's blocks, which apparently it does not. I guess that delegates pass by copy, like structs do. So far I have tried a variety of solutions. 1. As it is, passing the delegate as "ref" does not work, because the compiler does not accept to do the implicit conversion. It is the same logic as C++, here in C++11 I would have passed by rvalue reference, a thing that does not appear to exist in D. 2. Is it possible to make a delegate to be allocated on the heap ? I think of a syntax such as: client.process_callback = new delegate int(jack_nframes_t nframes) { 3. I have coded a workaround for the time being. The function here copies the delegate into a heap-allocated structure, and takes the interior pointer. T *copyDelegate(T)(T dg) { struct Tmp { T dg; } auto x = new Tmp; x.dg = dg; return &x.dg; } I find this code to be rather inelegant, is there a better solution?
Jun 07 2014
next sibling parent "anonymous" <anonymous example.com> writes:
On Saturday, 7 June 2014 at 21:18:39 UTC, Denis Martinez wrote:
 Thanks for the answer Chris, you are correct.
 I was expecting the closure to work similarly to Clang's 
 blocks, which apparently it does not. I guess that delegates 
 pass by copy, like structs do.

 So far I have tried a variety of solutions.

 1. As it is, passing the delegate as "ref" does not work, 
 because the compiler does not accept to do the implicit 
 conversion. It is the same logic as C++, here in C++11 I would 
 have passed by rvalue reference, a thing that does not appear 
 to exist in D.

 2. Is it possible to make a delegate to be allocated on the 
 heap ?
 I think of a syntax such as:
   client.process_callback = new delegate int(jack_nframes_t 
 nframes) {

 3. I have coded a workaround for the time being.
 The function here copies the delegate into a heap-allocated 
 structure, and takes the interior pointer.

   T *copyDelegate(T)(T dg) {
     struct Tmp { T dg; }
     auto x = new Tmp;
     x.dg = dg;
     return &x.dg;
   }

 I find this code to be rather inelegant, is there a better 
 solution?
&[dg][0] maybe But you're storing dg in your (heap because class) object anyway, as process_callback_. Just do that first and use &process_callback_ instead of &dg. That also guarantees that there's a pointer to it in D-land so that the GC doesn't collect it.
Jun 07 2014
prev sibling parent "Chris Cain" <zshazz gmail.com> writes:
On Saturday, 7 June 2014 at 21:18:39 UTC, Denis Martinez wrote:
 2. Is it possible to make a delegate to be allocated on the 
 heap ?
 I think of a syntax such as:
   client.process_callback = new delegate int(jack_nframes_t 
 nframes) {
AFAIK, no.
 3. I have coded a workaround for the time being.
 The function here copies the delegate into a heap-allocated 
 structure, and takes the interior pointer.

   T *copyDelegate(T)(T dg) {
     struct Tmp { T dg; }
     auto x = new Tmp;
     x.dg = dg;
     return &x.dg;
   }
I'll have to note that if you're passing this somewhere in C, D won't be keeping track of it automatically anymore. Thus, it's very likely your delegate will be garbage collected at some point (thus the memory might be repurposed for other things causing a similar issue to what you're having now). Ultimately, I think if you want to keep the delegate around it's probably simpler to store it somewhere and use the pointer to that somewhere. See: http://dlang.org/interfaceToC.html for more info.
Jun 07 2014
prev sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
See my answer to this:

http://stackoverflow.com/questions/22845175/pass-delegates-to-external-c-functions-in-d


Since a delegate is two pointers and most C functions expect only 
one pointer, you need to do some kind of magic. There's one 
solution. Another is if the C function can pass a void* argument 
to the callback function, you pass your class that way and 
forward it to the method by casting it back.

But when interacting with C libraries, you should generally use a 
function pointer rather than a delegate.
Jun 07 2014