Thread overview
wrapping a C style delegate
Aug 24, 2017
Nicholas Wilson
Aug 25, 2017
Kagamin
Aug 25, 2017
Nicholas Wilson
Aug 26, 2017
Ali Çehreli
Aug 26, 2017
Nicholas Wilson
Aug 26, 2017
Ali Çehreli
August 24, 2017
I want to wrap:

ErrorEnum function(Struct* s, void function(Struct*, ErrorEnum status, void *userData) callback, void *userData, uint flags);

as a member of a wrapping struct

struct Mystruct
{
   Struct* s; // wrapped

     ErrorEnum addCallback(void delegate(Struct*, ErrorEnum status))
     {
         //...
     }
}


How do I wrap that?

August 25, 2017
You're not specific enough. What would be semantics of such wrapper?
August 25, 2017
On Friday, 25 August 2017 at 13:49:20 UTC, Kagamin wrote:
> You're not specific enough. What would be semantics of such wrapper?

The C function I'm trying to wrap takes a function pointer which is essentially a delegate, but not quite:

ErrorEnum function(Struct* s, void function(Struct*, ErrorEnum status, void *userData) callback, void *userData, uint flags) SomeAPIaddCallback;

I want to make it a member function of a wrapping struct so I can call it like

MyStruct ms = ...

ms.addCallback((ErrorEnum ee) { ... });

instead of

SomeAPIaddCallback(ms.s,(Struct*, ErrorEnum status, void *userData) { ... } /*doesn't become a delegate */,null,0);

I'm not sure how to do it.


August 25, 2017
On 08/25/2017 04:00 PM, Nicholas Wilson wrote:
> On Friday, 25 August 2017 at 13:49:20 UTC, Kagamin wrote:
>> You're not specific enough. What would be semantics of such wrapper?
>
> The C function I'm trying to wrap takes a function pointer which is
> essentially a delegate, but not quite:
>
> ErrorEnum function(Struct* s, void function(Struct*, ErrorEnum status,
> void *userData) callback, void *userData, uint flags) SomeAPIaddCallback;
>
> I want to make it a member function of a wrapping struct so I can call
> it like
>
> MyStruct ms = ...
>
> ms.addCallback((ErrorEnum ee) { ... });
>
> instead of
>
> SomeAPIaddCallback(ms.s,(Struct*, ErrorEnum status, void *userData) {
> ... } /*doesn't become a delegate */,null,0);
>
> I'm not sure how to do it.
>
>

I think you need a variation of intermediateCallback() below. I passed the address of the delegate as userData but you can construct any context that contains everything that you need (e.g. the address of ms).

import std.stdio;

// The C struct
struct Struct {
    int i;
}

// Some C type
enum ErrorEnum {
    zero
}

// Some C function taking a callback
ErrorEnum SomeAPIaddCallback(Struct* s, void function(Struct*, ErrorEnum status, void *userData) callback, void *userData, uint flags) {
    writeln("SomeAPIaddCallback called for object ", s);
    writeln("Calling the callback...");
    callback(s, ErrorEnum.zero, userData);
    return ErrorEnum.zero;
}

// The callback to pass to the C function
void intermediateCallback(Struct * s, ErrorEnum status, void *userData) {
    writeln("intermediateCallback called");
    auto cb = cast(void delegate(ErrorEnum)*)userData;
    (*cb)(status);
}

// The D wrapper always passes intermediateCallback to the C function
struct MyStruct {
    Struct * s;
    void addCallback(void delegate(ErrorEnum ee) callback) {
        SomeAPIaddCallback(s, &intermediateCallback, &callback, 0);
    }
}

void main() {
    auto s = Struct(42);
    auto ms = MyStruct(&s);
    ms.addCallback((ErrorEnum ee) {
            writefln("The callback is called with %s for %s", ee, ms.s);
    });
}

Ali

August 26, 2017
On Saturday, 26 August 2017 at 00:27:47 UTC, Ali Çehreli wrote:
> I think you need a variation of intermediateCallback() below. I passed the address of the delegate as userData but you can construct any context that contains everything that you need (e.g. the address of ms).
>
> import std.stdio;
>
> // The C struct
> struct Struct {
>     int i;
> }
>
> // Some C type
> enum ErrorEnum {
>     zero
> }
>
> // Some C function taking a callback
> ErrorEnum SomeAPIaddCallback(Struct* s, void function(Struct*, ErrorEnum status, void *userData) callback, void *userData, uint flags) {
>     writeln("SomeAPIaddCallback called for object ", s);
>     writeln("Calling the callback...");
>     callback(s, ErrorEnum.zero, userData);
>     return ErrorEnum.zero;
> }
>
> // The callback to pass to the C function
> void intermediateCallback(Struct * s, ErrorEnum status, void *userData) {
>     writeln("intermediateCallback called");
>     auto cb = cast(void delegate(ErrorEnum)*)userData;
>     (*cb)(status);
> }
>
> // The D wrapper always passes intermediateCallback to the C function
> struct MyStruct {
>     Struct * s;
>     void addCallback(void delegate(ErrorEnum ee) callback) {
>         SomeAPIaddCallback(s, &intermediateCallback, &callback, 0);
>     }
> }
>
> void main() {
>     auto s = Struct(42);
>     auto ms = MyStruct(&s);
>     ms.addCallback((ErrorEnum ee) {
>             writefln("The callback is called with %s for %s", ee, ms.s);
>     });
> }
>
> Ali

I was thinking of something along those lines: but what about the lifetime of the passed delegate (the address of a local variable)? How can I ensure that it won't segfault when callback goes out of scope?
I could new it with the GC but
a) I'd rather have the wrapper be @nogc and
b) i'd have to hold a reference to it or pin it because SomeAPIaddCallback will be in a driver somewhere and wouldn't get scanned by the GC.

August 26, 2017
On 08/25/2017 05:49 PM, Nicholas Wilson wrote:

> I was thinking of something along those lines: but what about the
> lifetime of the passed delegate (the address of a local variable)? How
> can I ensure that it won't segfault when callback goes out of scope?
> I could new it with the GC but
> a) I'd rather have the wrapper be @nogc and
> b) i'd have to hold a reference to it or pin it because
> SomeAPIaddCallback will be in a driver somewhere and wouldn't get
> scanned by the GC.

Good points. I think you have to store a copy of the delegate in MyStruct and pass e.g. the array index of it as userData to intermediateCallback(). I think a Mallocator-based array would make the wrapper nogc.

Ali