Wrapping a C function pointer as a parameter with std::function<> for an old API

I am trying to communicate between applications with NATS.io. In order to asynchronously get a message when a publisher pushes something to a subject, you can do

 
natsConnection_Subscribe(&subscription, connection, subjectName, callback, closure);


The callback parameter is a `natsMsgHandler` which essentially is a

typedef void (*natsMsgHandler)(natsConnection *nc, natsSubscription *sub, natsMsg *msg, void *closure);

I have wrapped this logic in a class to hold the connection and subscription objects and added a wrapper method:


1
2
3
4
5
6
natsStatus adNatsSubAsyncWithCallback(const std::string& processName, const std::string& subName, lambda callback)
{
(...)
natsConnection_Subscribe(&sub->m_natsSubscription, nc, subName.c_str(), callback, nullptr);
(...)
}


Now, when I need to use this, let's say in my main, I can do:


1
2
3
4
5
6
    nats_interface ni1;
    ni1.adNatsSubAsyncWithCallback("onIdRequest", sendingSubj, [](natsConnection *nc,
                                                                  natsSubscription *sub,
                                                                  natsMsg *msg,
                                                                  void *closure) { *DO STUFF* });


My problem is that I cannot for the love of god figure out how to best approach the scenario of having the callback be a method of a class.

Let's say I have a class

1
2
3
4
5
6
7
class test1
{
  public:
    void onIdRequest(natsConnection *nc, natsSubscription *sub, natsMsg *msg, void *closure) const {
        natsMsg_Destroy(msg);
    }
}


If I try something like


1
2
3
4
5
6
7
8
    test1 t1;
    auto temp = [t1](natsConnection *nc,
                     natsSubscription *sub,
                     natsMsg *msg,
                     void *closure) {
        t1.onIdRequest(nc, sub, msg, closure);
    };


and pass temp to the function call instead of a lambda directly, I get an error.

I also kind of tried to std::bind a method of class test1 with a matching definition to `natsMsgHandler` and pass that function<> object instead with absolutely catastrophic results.

How would I implement something like this?



EDIT:

The bind attempt was like this:

1
2
    auto callback = std::bind(&test1::onIdRequest,&t1,_1,_2,_3,_4);
    ni1.adNatsSubAsyncWithCallback("onIdRequest", sendingSubj, callback);


Last edited on
natsMsgHandler is a C function pointer. You can only pass a pointer to a global function or to a static member function (lambdas that don't bind anything are allowed, but only because the compiler can rewrite them into global functions and then take their pointer).
You will never be able to pass an std::function, a binding lambda, a functor, or any other C++ object to a C function pointer in a way that will work correctly.

The last parameter to the callback is a void *closure. This is presumably an opaque pointer that the library forwards untouched from the last parameter to the corresponding natsConnection_Subscribe() call.
If you have this class:
1
2
3
4
class test1{
public:
    void onIdRequest(natsConnection *nc, natsSubscription *sub, natsMsg *msg) const;
};
you can add
1
2
3
4
5
6
7
class test1{
public:
    void onIdRequest(natsConnection *nc, natsSubscription *sub, natsMsg *msg) const;
    static void static_onIdRequest(natsConnection *nc, natsSubscription *sub, natsMsg *msg, void *closure){
		((test1 *)closure)->onIdRequest(nc, sub, msg);
	}
};
then when you subscribe:
 
natsConnection_Subscribe(&sub->m_natsSubscription, nc, subName.c_str(), test1::static_onIdRequest, &test1_instance);
Last edited on
I am not sure I understand this part

1
2
static void static_onIdRequest(natsConnection *nc, natsSubscription *sub, natsMsg *msg, void *closure){
		((test1 *)closure)->onIdRequest(nc, sub, msg);


What I get is that this is a valid static function that we can pass to natsConnection_Subscribe since it is a static function. Then I am lost on what it will actually do. Is the body of a function a function pointer that will return a test1 pointer which will call its method onIdRequest(nc, sub, msg); ?

edit: Now that I wrote it down I get it. You cast the closure to test1 and call the non static version.
Last edited on
Topic archived. No new replies allowed.