www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Using a delegate when interfacing with C

reply "Marco Cosentino" <cosentino.ma gmail.com> writes:
Hi,
I'm quite new to D and I'm not able to find out what I'm doing
wrong.

Consider the following code:

   class ClientImplementation {
    private ProcessDelegate processDelegate;

    void setProcessDelegate(ProcessDelegate deleg) {
      this.processDelegate = deleg;
      extern(C) ProcessCallback callback = function int(NFrames
nframes, void* data) {
        auto client = *(cast(ClientImplementation*) data);
        return client.processDelegate(nframes);
      };
      this.setProcessCallback(callback, cast(void *) &this);
    }

In my D wrapper for a C API I want to use delegates. The C API
accepts a callback which has a generic void* parameter which can
be specified when setting the callback (it will be stored and
passed in the callback when it gets called... a common pattern in
C APIs).
So I want to use it to make delegates possible.
The problem with this approach is that I get a segmentation fault
on the line:
auto client = *(cast(ClientImplementation*) data);
as soon as the callback is called the first time.
The callback is called in another thread (but this shouldn't be a
problem since ClientImplementation is a class and therefore
instances are created in the heap).

Can somebody help me in figuring out why this happens?

I also tried unsuccesfully
auto client = cast(ClientImplementation*) data;
Jul 05 2014
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Saturday, 5 July 2014 at 22:18:56 UTC, Marco Cosentino wrote:
        auto client = *(cast(ClientImplementation*) data);
Try just auto client = cast(ClientImplementation) data; and this.setProcessCallback(callback, cast(void *) &this); setProcessCallback(callback, cast(void*) this);
 Can somebody help me in figuring out why this happens?
The reason is a class this in D is already a pointer (just a hidden one) so when you do &this in a class, it is like a ClientImplementation** in C - a pointer to a (temporary) pointer. So by the time the callback runs, it is pointing to nonsense. In general, remember any class reference in D is already equivalent to a pointer in C or C++ and can be casted straight to void* without needing to take its address.
Jul 05 2014
next sibling parent "Marco Cosentino" <cosentino.ma gmail.com> writes:
On Saturday, 5 July 2014 at 22:28:48 UTC, Adam D. Ruppe wrote:

 In general, remember any class reference in D is already 
 equivalent to a pointer in C or C++ and can be casted straight 
 to void* without needing to take its address.
Thanks Adam, you're a life saver ;). It works like a charme.
Jul 05 2014
prev sibling parent "Marco Cosentino" <cosentino.ma gmail.com> writes:
Hey Adam,
an interesting aspect of what I'd like to achieve is to use
compile-time reflection to generate the wrapper functions for all
the delegates (there are ~ 10).

The pattern is like what I presented eariler and in addition to
that there are some delegates which have no return type (void).

I managed to write a template like this (D is awesome):

alias ProcessDelegate             = int delegate(NFrames nframes);

import std.traits;
private  template CallbackWrapper(alias T) if(isDelegate!T) {
    extern(C) static auto wrapper(ParameterTypeTuple!T params, void
* data) {
      auto client = cast(ClientImplementation) data;
      return mixin("client." ~ __traits(identifier, T) ~
"(params)");
    }
}

    void setProcessDelegate(ProcessDelegate deleg) {
      processDelegate = deleg;
      setProcessCallback( &
CallbackWrapper!(processDelegate).wrapper, cast(void *) this);
    }
Jul 06 2014