如何使用C ++成员函数作为C框架的回调函数

时间:2011-07-19 15:20:19

标签: c++ c callback

有一个C库(我无法更改)支持类型

的回调函数
void (*callback)(void *appContext, int eventid)

我想将C ++函数设置为回调。

具体来说,我有以下问题吗?

  1. 我是否需要在"extern C"块下声明回调函数?

  2. 成员函数是否需要是静态的才能成为回调函数?是否可以使用非静态成员函数?如果有,怎么样?什么时候建议使用非静态成员函数?

  3. 函数是否为模板函数是否重要?

  4. 非类C样式函数是否比类成员函数有任何优势?

  5. 我在旧的VC ++编译器上尝试这些变体,它不支持最新的C ++标准。但代码需要独立于平台,并且应该适用于大多数C ++编译器。我想知道回调的推荐做法是什么?

6 个答案:

答案 0 :(得分:15)

是否需要在extern“C”下声明回调函数?

NO。只有在不使用函数指针的情况下直接调用C ++函数时才需要使用extern“C”。如果使用函数指针,则不需要extern“C”。

我可以使用非静态成员函数作为回调吗?

NO。类A的非静态成员函数具有与该指针对应的隐式第一参数。

我可以使用静态成员函数作为回调吗?

是,只要签名与回调的匹配即可。

该函数是否为模板函数是否重要?

不,只要实例化模板的签名与回调匹配,模板函数就可以用作回调。

答案 1 :(得分:6)

如果您的成员函数是静态的,这应该有效。

答案 2 :(得分:6)

假设appContext是一个不透明的指针,你传递给进行回调的函数,你可以回调一个特定对象的成员函数,如下所示:

class myclass {

  void do_something() {
     // call function making the callback using _event_handler
     // as the callback function and the "this" pointer as appContext
  }

  // make sure the raw callback uses the correct calling convention (cdecl, stdcall, etc.)
  static void _handle_event(void* appContext, int eventid) {
    // forward the event to the actual object
    static_cast<myclass *>(appContext)->handle_event(eventid);
  }

  void handle_event(int eventid) {
     // do object-specific event handling
  }

};

有几个答案提到extern "C"作为要求。这完全是错误的。 只有在直接从C ++调用C函数时才需要extern "C"。它用于告诉C ++编译器“在为此函数生成符号名时不应用名称修改”。您正在将C ++函数指针传递给C函数。只要调用约定匹配,它就可以正常工作。函数的名称永远不会涉及。

答案 3 :(得分:3)

这并不像在extern "C"块下声明回调函数那么简单。您需要弄清楚C库对其函数使用的调用约定。

我将使用的术语是Microsoft特定的,但同样的规则也适用于其他平台。

默认情况下,Visual C ++编译器使C函数__stdcall和C ++函数__cdecl。您不能混合和匹配这些调用约定,因为每个调用约定都会对谁清理堆栈做出不同的假设。 Here's更详细的解释。

一旦匹配调用约定,最简单的方法是将C ++回调函数声明为命名空间作用域独立函数;或者如果它需要是成员函数,那么就是静态成员函数。在这两种情况下,您都应该能够使用std::bind绑定指向该类实例的指针。您甚至可以使用std::bind绑定非静态成员函数,但我无法回想起我的语法。

答案 4 :(得分:2)

  1. 确保它在全球范围内

  2. 使用extern "C"

  3. 如果需要,请使用__cdeclvoid (_cdecl *callback)

答案 5 :(得分:1)

严格来说,你不能。必须指定C回调extern "C",这对于成员函数是不可能的。只能使用独立功能。您可以将调用从那里转发到任何C ++函数,包括静态或非静态成员函数。

根据平台的不同,您有时可能会跳过extern "C",但我不会测试我的运气。