Home > Back-end >  Passing a non-static method or std::function<void(...)> as a void (*) argument
Passing a non-static method or std::function<void(...)> as a void (*) argument

Time:03-01

I'm trying to define a function/method (void MyClass::myDispatcher(int, int, void *) or std::function<void(int, int, void *)> myDispatcher) which would be a member of a host class.

However, I also need to pass this function to through an external library which takes as argument: void (*dispatcher) (int, int, void *).

Is there a way I can convert a non-static function to the required void (*)(int, int, void *) type ?

My first attempt with the candidate void MyClass::myDispatcher(int, int, void *) was using the std::bind() in order to bind MyClass::myDispatcher with this. Although I could not convert the return type of std::bind to match the void (*).

The second attempt was done using std::function<void(int, int, void *)> myDispatcher for which I tried to use the reinterpret_cast<void (*)(int, int, void *)>(&myDispatcher) to fit in the requested argument. It did compile but I ultimatly ended up in a segfault within the external library.

I could summarized my two attempts with this example:

class MyClass{
  MyClass() = default;
  
  void myDispatcher(int, int, void *){
    // do something
  }

  void init(){
    myFct = [this](int, int, void*){
      // do something
    };
    
    external_function(reinterpret_cast<void (*)(int, int, void *)>(&myFct));

    // or the non-compiling
    // external_function(std::bind(&MyClass::myDispatcher, this));
  }

private:
  std::function<void(int, int, void*)> myFct;

};

Edit:

As some of you pointed out: this C-style external_function has an extra void * argumement which is passed as the last arg to the provided function!

To summarise: there is no way to do such a thing since there is a fundamental difference between a function pointer and non-static member functions. However, C-style library usually provides a workaround which performs a callback to the provided static function:

void external_function(void (*dispatcher) (int, int, void *), void* info){
  // a bunch of instructions (meaningless for this example)
  
  int a,b;

  dispatcher(a,b,info);
}

Once we know that, one can cast back the info pointer to the class we want.

CodePudding user response:

As stated, this is not possible since there is no way to meaningfully construct a plain function pointer from a (non static) method, a closure, or a std::function object.

Roughly speaking, each of the constructs above are logically formed by two parts: some fixed code (a pointer to a fixed, statically known function), and variable data (the object at hand, or the captures for a closure). We can not simply throw away the second part.

That being said, I would recommend to check if the library will call the dispatcher passing a user-defined pointer for its void * argument. It's somewhat common to see C style library functions like

void register_callback(void (*dispatcher)(int,int,void *), void *user_data);

This is a trick to allow to simulate closures in C, passing the two "parts" of the closure separately.

If that's the case, instead of

// not working, just for example
std::function<void(int, int)> f;
register_callback(f);

You can write

// Make sure f will live long enough.
// I'm using new to stress the point, but you can use anything
// else provided that the pointer will be valid when f is
// called.
std::function<void(int, int)> *pf = new ...;
register_callback(call_the_function, pf);

provided you have defined

// fixed function, independent from the std::function object
void call_the_function(int x, int y, void *p) {
   std::function<void(int,int)> *pf =
      (std::function<void(int,int)>*) p;
   (*pf)(x,y);
}

If you used new to keep the pointer valid long enough, remember to delete it when the callback is no longer needed.

CodePudding user response:

Is there a way I can convert a non-static function to the required void (*)(int, int, void *) type ?

No, there isn't. A function pointer cannot point to non-static member functions, nor to function wrapper objects.

What you can do, is point to a normal function that in turn calls the member function. The challenge is getting the instance into that function. Most C callback API allow passing user defined data into the callback in form of a pointer to void, in which case this isn't a problem. If there is no such option, then you can only use a globally accessible instance, or an instance that is created within the callback.

You will need to read the documentation of the API to find if and how passing user data is possible. There is a void* parameter in the callback, which hints that it may be what you need.

CodePudding user response:

No you can't get a void (*)(int, int, void *) from a non-static member function. Non-static member function are fundametally different from free functions: You need an object to call the method.

However, in your code you are already using std::function and std::function has a constructor to convert a void (Foo::*)(int, int,void*) to a std::function<void(Foo*,int,int,void*)>.

CodePudding user response:

The external library expects a C style function pointer. I't won't accept C callable objects (e.g. std::function or std::bind). You have to wrap the C callable objects inside a plain function with the required signature. Something like this:

void dispatcher(int theInt1, int theInt2, void *theData) {
   theInstanceOfMyClass.myDispatcher(theInt1, theInt2, data);
}

You might want to pass the address of your C instance as the third argument, e.g.:

void dispatcher(int theInt1, int theInt2, void *theData) {
   static_cast<MyClass *>(theData)->myDispatcher(theInt1, theInt2, data);
}
  • Related