代码之家  ›  专栏  ›  技术社区  ›  Methos

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

  •  39
  • Methos  · 技术社区  · 16 年前

    我有一个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() 
    {
    
    }
    

    编译器引发以下错误:

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

    我的问题:

    1. 首先,有可能注册一个C++类MeMeBER函数,就像我正在尝试做的那样,如果是这样怎么办? (我读了32.8 http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html . 但在我看来,这并不能解决问题。)
    2. 有没有其他/更好的方法来解决这个问题?
    5 回复  |  直到 10 年前
        1
  •  40
  •   sharptooth    16 年前

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

    类A的非静态成员函数具有类型为的隐式第一个参数 class A* 对应于 指针。这就是为什么只有当回调的签名还具有 A类* 类型。

        2
  •  14
  •   Community CDub    8 年前

    如果成员函数不是静态的,也可以这样做,但它需要更多的工作(另请参见 Convert C++ function pointer to c function pointer ):

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

    此示例在编译时是完整的:

    g++ test.cpp -std=c++11 -o test
    

    你需要 c++11 旗帜。在代码中你可以看到 register_with_library(func) 在哪里打电话 func 是动态绑定到成员函数的静态函数 e .

        3
  •  7
  •   Rapptz    10 年前

    问题是这个方法!=函数。编译器会将您的方法转换为如下类型:

    int e( A *this, int *k, int *j );
    

    所以,它肯定不能传递它,因为类实例不能作为参数传递。解决方法之一是使方法成为静态的,这样它将具有良好的类型。但它不会有任何类实例,也不会访问非静态类成员。

    另一种方法是声明一个带有静态指针的函数,该指针指向第一次初始化的。函数只将调用重定向到类:

    int callback( int *j, int *k )
    {
        static A  *obj = new A();
        a->(j, k);
    }
    

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

        4
  •  5
  •   TimW    16 年前

    嗯……如果你在一个Win32平台上,总会有一种令人讨厌的雷鸣般的方式……

    Thunking in Win32: Simplifying callbacks to non-static member functions

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

        5
  •  1
  •   pauljwilliams    16 年前

    使用成员函数的问题在于,它需要一个对象来对其进行操作,而C不知道对象。

    最简单的方法是:

    //In a header file:
    extern "C" int e(int * k, int * e);
    
    //In your implementation: 
    int e(int * k, int * e) { return 0; }