将非静态方法或 std::function<void(...)> 作为 void (*) 参数传递

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

我正在尝试定义一个 function/method(void MyClass::myDispatcher(int, int, void *)std::function<void(int, int, void *)> myDispatcher),它将成为主机 class.

的成员

但是,我还需要通过一个外部库将这个函数传递给它作为参数:void (*dispatcher) (int, int, void *).

有没有办法将非静态函数转换为所需的 void (*)(int, int, void *) 类型?

我对候选人 void MyClass::myDispatcher(int, int, void *) 的第一次尝试是使用 std::bind() 来绑定 MyClass::myDispatcherthis。尽管我无法将 std::bind 的 return 类型转换为匹配 void (*).

第二次尝试是使用 std::function<void(int, int, void *)> myDispatcher 完成的,为此我尝试使用 reinterpret_cast<void (*)(int, int, void *)>(&myDispatcher) 来适应请求的参数。它确实编译了,但我最终在外部库中遇到了段错误。

我可以用这个例子总结我的两次尝试:

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;

};

编辑:

正如你们中的一些人所指出的:这个 C 风格 external_function 有一个额外的 void * 参数,它作为最后一个参数传递给提供的函数!

总而言之:没有办法做这样的事情,因为函数指针和非静态成员函数之间存在根本区别。但是,C 风格的库通常会提供一种变通方法,即对提供的静态函数执行回调:

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);
}

一旦我们知道这一点,就可以将 info 指针转换回我们想要的 class。

不,您不能从 non-static 成员函数中获取 void (*)(int, int, void *)。 Non-static 成员函数与自由函数有根本的不同:​​您需要一个对象来调用该方法。

但是,在您的代码中,您已经在使用 std::function 并且 std::function 具有将 void (Foo::*)(int, int,void*) 转换为 std::function<void(Foo*,int,int,void*)> 的构造函数。

外部库需要一个 C 风格的函数指针。我不会不接受 C++ 可调用对象(例如 std::function 或 std::bind)。您必须将 C++ 可调用对象包装在具有所需签名的普通函数中。像这样:

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

您可能希望将 C++ 实例的地址作为第三个参数传递,例如:

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

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

不,没有。函数指针不能指向 non-static 成员函数,也不能指向函数包装对象。

您可以做的是指向一个普通函数,该函数又调用成员函数。挑战在于将实例放入该函数中。大多数 C 回调 API 允许将用户定义的数据以指向 void 的指针的形式传递到回调中,在这种情况下这不是问题。如果没有这样的选项,那么你只能使用全局可访问的实例,或者在回调中创建的实例。

您需要阅读 API 的文档以了解是否可以以及如何传递用户数据。回调中有一个void*参数,提示可能就是你需要的

如上所述,这是不可能的,因为无法从(非静态)方法、闭包或 std::function 对象有意义地构造普通函数指针。

粗略地说,上面的每个构造在逻辑上都由两部分组成:一些固定代码(指向固定的静态已知函数的指针)和可变数据(手头的对象,或闭包的捕获) ).我们不能简单地扔掉第二部分。

也就是说,我建议检查库是否会调用 dispatcher 并为其 void * 参数传递一个 user-defined 指针。看到 C 风格的库函数有点常见

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

这是一个允许在 C 中模拟闭包的技巧,分别传递闭包的两个“部分”。

如果是这样,而不是

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

你可以写

// 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);

前提是你已经定义了

// 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);
}

如果您使用 new 使指针保持足够长的有效时间,请记住在不再需要回调时 delete 它。