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

How to use a C++ member function as the callback function for a C framework

本文关键字:函数 框架 回调 何使用 c++ 成员      更新时间:2023-10-16

有一个C库(我不能更改)支持类型为

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

我想设置一个c++函数作为回调。

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

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

  2. 成员函数必须是静态的才能成为回调函数吗?是否可以使用非静态成员函数?如果是,怎么做?什么时候推荐使用非静态成员函数?

  3. 函数是否是模板函数有关系吗?

  4. 非C类风格的函数比类成员函数有什么优势吗?

我正在一个旧的vc++编译器上尝试这些变体,它不支持最新的c++标准。但是代码需要与平台无关,并且应该在大多数c++编译器上工作。我想知道关于回调的推荐做法是什么?

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

。extern "C"仅在直接从C调用c++函数而不使用函数指针时是必需的。如果使用函数指针,则不需要extern "C"。

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

。类A的非静态成员函数有一个隐式的第一个形参,对应于这个指针。

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

YES,只要签名与回调的签名匹配。

函数是否是模板函数有关系吗?

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

假设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"作为要求。这是完全错误的。extern "C"仅在直接从c++调用C函数时是必需的。它用于告诉c++编译器"在为该函数生成符号名时不要应用名称混淆"。你正在传递一个c++函数指针给一个C函数。只要调用约定匹配,它就可以正常工作。不涉及函数名

如果你的成员函数是静态的,这应该可以工作。

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

我要使用的术语是微软特有的,但同样的规则也应该适用于其他平台。

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

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

  1. 确保它在全局作用域中

  2. 使用extern "C"

  3. 使用__cdecl,如果需要:void (_cdecl *callback)

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

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