www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - wrapping a C style delegate

reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
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?
Aug 23
parent reply Kagamin <spam here.lot> writes:
You're not specific enough. What would be semantics of such 
wrapper?
Aug 25
parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
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.
Aug 25
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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
Aug 25
parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
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.
Aug 25
parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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
Aug 26