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

时间:2009-06-16 10:24:08

标签: 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() 
{

}

编译器抛出以下错误:

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

我的问题:

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

6 个答案:

答案 0 :(得分:41)

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

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

答案 1 :(得分:15)

如果成员函数不是静态的,你也可以这样做,但它需要更多的工作(参见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的静态函数。

答案 2 :(得分:6)

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

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

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

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

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

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

答案 3 :(得分:5)

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

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

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

答案 4 :(得分:1)

使用成员函数的问题是它需要一个可以作用的对象 - 而且C不知道对象。

最简单的方法是执行以下操作:

//In a header file:
extern "C" int e(int * k, int * e);

//In your implementation: 
int e(int * k, int * e) { return 0; }

答案 5 :(得分:0)

在此解决方案中,我们有一个模板类 并将静态方法作为回调提供给“ c函数”。 此类包含一个“普通”对象(带有名为callback()的成员函数,该函数最终将被调用)。

一旦定义了您的班级(此处为A),就可以轻松使用它:

int main() {

  Holder<A> o ( A(23, 23) );

  std::cout << o().getN() << "\n";

  callACFunctionPtr( fun );

  callACFunctionPtr( o.callback );

} // ()

完整示例:

#include <iostream>

// ----------------------------------------------------------
// library class: Holder
// ----------------------------------------------------------
template< typename HeldObjectType >
class Holder {
public:
  static inline HeldObjectType object;

  static void callback( ) {
    object.callback();
  } // ()

  HeldObjectType &  operator() ( ) {
    return object;
  }

  Holder( HeldObjectType && obj )
  {
    object = obj;
  }

  Holder() = delete;

}; // class

// ----------------------------------------------------------
// "old" C function receivin a ptr to function as a callback
// ----------------------------------------------------------
using Callback = void (*) (void);

// ..........................................................
// ..........................................................
void callACFunctionPtr( Callback f ) {
  f();
} // ()

// ----------------------------------------------------------
// ----------------------------------------------------------
void fun() {
  std::cout << "I'm fun\n";
} // 

// ----------------------------------------------------------
// 
// Common class where we want to write the
// callback to be called from callACFunctionPtr.
// Name this function: callback
// 
// ----------------------------------------------------------
class A {
private:
  int n;

public:

  A(  ) : n( 0 ) { }

  A( int a, int b ) : n( a+b ) { }

  void callback( ) {
    std::cout << "A's callback(): " << n << "\n";
  }

  int getN() {
    return n;
  }

}; // class

// ----------------------------------------------------------
// ----------------------------------------------------------
int main() {

  Holder<A> o ( A(23, 23) );

  std::cout << o().getN() << "\n";

  callACFunctionPtr( fun );

  callACFunctionPtr( o.callback );

} // ()