如何使用C ++成员函数作为C框架的回调函数
有一个C库(我无法更改)支持该类型的回调函数
void (*callback)(void *appContext, int eventid)
我想设置一个C ++函数作为回调。
具体来说,我有以下问题?
-
我是否需要在
"extern C"
块下声明回调函数? -
成员函数是否需要是静态的才能成为回调函数? 是否可以使用非静态成员函数? 如果有,怎么样? 什么时候建议使用非静态成员函数?
-
函数是否是模板函数是否重要?
-
非类C风格函数是否比类成员函数有任何优势?
我在旧的VC ++编译器上尝试这些变体,它不支持最新的C ++标准。 但代码需要独立于平台,并且应该适用于大多数C ++编译器。 我想知道回调的推荐做法是什么?
回调函数是否需要在extern“C”下声明?
没有。 只有在不使用函数指针的情况下直接调用C ++函数时才需要extern“C”。如果使用函数指针,则不需要extern“C”。
我可以使用非静态成员函数作为回调吗?
没有。 类A的非静态成员函数具有与该指针对应的隐式第一参数。
我可以使用静态成员函数作为回调吗?
是的,只要签名与回调的匹配即可。
函数是否是模板函数是否重要?
否,只要实例化模板的签名与回调匹配,模板函数就可以用作回调。
如果您的成员函数是静态的,这应该有效。
假设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(appContext)->handle_event(eventid); } void handle_event(int eventid) { // do object-specific event handling } };
几个答案提到extern "C"
作为要求。 这完全是错误的。 只有在直接从C ++调用C函数时才需要extern "C"
。 它用于告诉C ++编译器“在为此函数生成符号名时不应用名称修改”。 您正在将C ++函数指针传递给C函数。 只要调用约定匹配,它就可以正常工作。 函数的名称从不涉及。
它并不像在extern "C"
块下声明回调函数那么简单。 您需要弄清楚C库对其函数使用的调用约定。
我将要使用的术语是Microsoft特定的,但同样的规则也应适用于其他平台。
默认情况下,Visual C ++编译器使C函数__stdcall
和C ++函数__cdecl
。 您不能混合和匹配这些调用约定,因为每个调用约定都会对谁清理堆栈做出不同的假设。 这是一个更详细的解释。
一旦匹配了调用约定,最简单的方法是将C ++回调函数声明为命名空间范围的独立函数; 或者如果它需要是成员函数,那么就是静态成员函数。 在这两种情况下,您都应该能够使用std::bind
指向该类实例的指针。 您甚至可以使用std::bind
来绑定非静态成员函数,但我无法回想起我的头脑中的语法。
-
确保它在全球范围内
-
使用
extern "C"
-
如果需要,请使用
__cdecl
:void (_cdecl *callback)
严格来说,你不能。 AC回调必须指定为extern "C"
,这对于成员函数是不可能的。 只能使用独立function。 您可以将呼叫从那里转发到任何C ++函数,包括静态或非静态成员函数。
根据平台的不同,您有时可能会跳过extern "C"
,但我不会测试我的运气。