使用需要函数指针的C库时的C ++习惯用法?


C++ idiom when using a C library which expects a function pointer?

本问题已经有最佳答案,请猛点这里访问。

我在C++应用程序中使用C库。c sdk具有将回调函数指针作为参数的函数。这些函数的签名通常如下:

1
typedef int (* Func) (type1 c, type2 d);

我的代码是用C++中的类构造的。但是,我不能将任何成员函数作为回调传递给这个函数,因为它不接受int (MyClass::*)(type1 c, type2 d),只接受int (*)(type1 c, type2 d)

我通过在不同的类中将所有回调定义为static,然后将它们传递给C库,然后C库就可以工作了。

我对C++仍然是新的,所以我不确定这是不是正确的解决方案?代码有效,但如果我做错了,我很想听听。


在您的代码可能遇到的大多数平台上,您的解决方案都很好。但是,严格来说,可以有一个C调用约定和C++调用约定不同的平台。在这个平台上,您的代码将不起作用。

100%正确的解决方案(与您使用的99.9%正确的解决方案相反)是定义一个带有C语言链接的函数作为回调。如果您需要此函数让成员访问您的类,那么它可以调用您的类中的static成员函数:

1
2
3
4
5
6
7
8
class MyClass
{
  static int secret;
public:
  static void callback() { secret = 42; }
};

extern"C" void callback() { MyClass::callback(); }

请注意,对于回调注册点的作者(即将回拨您的库的作者)来说,这是一个很好的实践,可以为用户提供一种将void*数据与回调关联的方法,该方法将在用户调用您时传入。如果你的库允许,你可以使用这个EDCOX1 3来传递一个指向C++类的指针。有了这样一个精心设计的回调库,它可以看起来像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MyClass
{
  int secret;

public:
  void setSecret() { secret = 42; }
};

extern"C" void callback(void *userData) { static_cast<MyClass*>(userData)->setSecret(); }

MyClass mc;

int main()
{
  cLibrary_registerCallback(&callback, &mc);
}


成员函数始终隐式获取"this"作为其第一个参数。静态函数的使用通常是因为静态函数不需要这个隐式参数。请参阅Angew对extern"C"事物的回答,以获得100%的可移植性。


要将非静态成员函数作为回调调用,需要定义一个以实例为参数的非成员函数,并在该实例上调用成员函数。使用函数指针回调的此类C API还允许注册指向用户定义数据的空指针,该数据将被转发到回调。使用此指针传递实例。

通常使用lambda而不是命名函数,因为这种简单的包装器通常不可重用,也不需要名称。