使用C++类成员函数作为C回调函数

Met*_*hos 45 c c++ interop callback

我有一个C库,需要注册回调函数来定制一些处理.回调函数的类型是int a(int *, int *).

我正在编写类似于以下内容的C++代码,并尝试将C++类函数注册为回调函数:

class A {
  public:
   A();
   ~A();
   int e(int *k, int *j);
};

A::A()
{
   register_with_library(e)
}

int
A::e(int *k, int *e)
{
  return 0;
}

A::~A() 
{

}
Run Code Online (Sandbox Code Playgroud)

编译器抛出以下错误:

In constructor 'A::A()',
error:
 argument of type ‘int (A::)(int*, int*)’ does not match ‘int (*)(int*, int*)’.
Run Code Online (Sandbox Code Playgroud)

我的问题:

  1. 首先是可以注册一个C++类的memeber函数,就像我想要做的那样,如果是这样的话怎么样?(我在http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html上阅读了32.8 .但在我看来它并没有解决问题)
  2. 是否有替代/更好的方法来解决这个问题?

sha*_*oth 41

如果成员函数是静态的,则可以这样做.

类A的非静态成员函数具有隐式的第一个类型参数,class A*该参数对应于指针.这就是为什么如果回调的签名也有第一个class A*类型的参数,你只能注册它们.

  • @Tom,标准称它为"隐式对象参数",它的类型为A&非const成员函数,A const&用于const成员函数,A volatile和volatile等等.它是一个参考,而"this"是一个指针 - 主要是因为历史.调用成员函数的对象称为"隐含对象参数".出于重载解析的目的,隐式对象参数被视为隐藏的第一个参数 - 但这只是概念性的,没有必要真正存在 (2认同)

Ann*_*sum 19

如果成员函数不是静态的,你也可以这样做,但它需要更多的工作(参见将C++函数指针转换为c函数指针):

#include <stdio.h>
#include <functional>

template <typename T>
struct Callback;

template <typename Ret, typename... Params>
struct Callback<Ret(Params...)> {
   template <typename... Args> 
   static Ret callback(Args... args) {                    
      return func(args...);  
   }
   static std::function<Ret(Params...)> func; 
};

template <typename Ret, typename... Params>
std::function<Ret(Params...)> Callback<Ret(Params...)>::func;

void register_with_library(int (*func)(int *k, int *e)) {
   int x = 0, y = 1;
   int o = func(&x, &y);
   printf("Value: %i\n", o);
}

class A {
   public:
      A();
      ~A();
      int e(int *k, int *j);
};

typedef int (*callback_t)(int*,int*);

A::A() {
   Callback<int(int*,int*)>::func = std::bind(&A::e, this, std::placeholders::_1, std::placeholders::_2);
   callback_t func = static_cast<callback_t>(Callback<int(int*,int*)>::callback);      
   register_with_library(func);      
}

int A::e(int *k, int *j) {
   return *k - *j;
}

A::~A() { }

int main() {
   A a;
}
Run Code Online (Sandbox Code Playgroud)

这个例子在它编译的意义上是完整的:

g++ test.cpp -std=c++11 -o test
Run Code Online (Sandbox Code Playgroud)

你需要c++11国旗.在您看到的代码中,register_with_library(func)调用func了动态绑定到成员函数的静态函数e.

  • @AnnevanRossum你的解决方案很棒,但我遇到了尝试创建两个这样的回调并且第二个重写第一个的问题。我在 /sf/ask/4653223501/ 上发布了关于必须像这样分离“静态包装器”所需的最小更改。 (2认同)

Rao*_*ter 6

问题是方法!=功能.编译器会将您的方法转换为类似的方法:

int e( A *this, int *k, int *j );
Run Code Online (Sandbox Code Playgroud)

因此,它确定您无法传递它,因为类实例不能作为参数传递.解决方法的一种方法是将方法设置为静态,这样就可以获得良好的类型.但它不会有任何类实例,也不会访问非静态类成员.

另一种方法是使用第一次初始化的A的静态指针声明一个函数.该函数仅将调用重定向到类:

int callback( int *j, int *k )
{
    static A  *obj = new A();
    a->(j, k);
}
Run Code Online (Sandbox Code Playgroud)

然后您可以注册回调函数.


Tim*_*imW 5

好吧......如果你在win32平台上总是有令人讨厌的Thunking方式......

Win32中的Thunking:简化非静态成员函数的回调

这是一个解决方案,但我不建议使用它.
它有一个很好的解释,很高兴知道它存在.