如何使用 C++ 成员函数作为 C 框架的回调函数
Posted
技术标签:
【中文标题】如何使用 C++ 成员函数作为 C 框架的回调函数【英文标题】:How to use a C++ member function as the callback function for a C framework 【发布时间】:2011-07-19 15:20:18 【问题描述】:有一个支持回调函数类型的 C 库(我无法更改)
void (*callback)(void *appContext, int eventid)
我想设置一个 C++ 函数作为回调。
具体我有以下问题?
需要在"extern C"
块下声明回调函数吗?
成员函数是否必须是静态的才能成为回调函数?是否可以使用非静态成员函数?如果是,如何?什么时候推荐使用非静态成员函数?
函数是否为模板函数是否重要?
非类 C 风格的函数比类成员函数有什么优势吗?
我正在旧的 VC++ 编译器上尝试这些变体,它不支持最新的 C++ 标准。但是代码需要独立于平台,并且应该适用于大多数 C++ 编译器。我想知道回调的推荐做法是什么?
【问题讨论】:
您使用的是什么版本的 C/C++? (即 MSVC、GCC) 我现在正在使用 MSVC。但是代码需要独立于平台,因为它也将被移植到 linux 平台。 【参考方案1】:回调函数需要在extern "C"下声明吗?
没有。仅当您从 C 中直接调用 C++ 函数而不使用函数指针时才需要 extern "C"。如果使用函数指针,则不需要 extern "C"。
我可以使用非静态成员函数作为回调吗?
没有。 A类的非静态成员函数有一个隐含的第一个参数对应于这个指针。
我可以使用静态成员函数作为回调吗?
是的,只要签名与回调的签名匹配。
函数是否为模板函数有关系吗?
不,只要实例化模板的签名与回调匹配,模板函数就可以用作回调。
【讨论】:
【参考方案2】:假设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】:如果你的成员函数是静态的,这应该可以工作。
【讨论】:
我需要在extern C块下声明函数吗? 我认为如果您的成员函数是静态的,它将起作用。试试看。 只要成员是静态的,调用约定是一样的,参数列表匹配就行。【参考方案4】:这并不像在extern "C"
块下声明回调函数那么简单。您需要弄清楚 C 库对其函数使用的调用约定。
我将使用的术语是特定于 Microsoft 的,但同样的规则也应该适用于其他平台。
默认情况下,Visual C++ 编译器生成 C 函数 __stdcall
和 C++ 函数 __cdecl
。您不能混合和匹配这些调用约定,因为它们中的每一个都对谁清理堆栈做出了不同的假设。 Here's更详细的解释。
匹配调用约定后,最简单的方法是将 C++ 回调函数声明为命名空间范围的独立函数;或者如果它需要是一个成员函数,那么一个静态成员函数。在这两种情况下,您都应该能够使用std::bind
将指针绑定到类的实例。您甚至可以使用 std::bind
绑定非静态成员函数,但我想不起来语法。
【讨论】:
extern "C" 与 stdcall/cdecl 问题完全正交。无论 C 的默认约定是什么,extern "C" 都会根据标准自动使用它。 @n.m.:谢谢,我不知道。但是,OP 仍然需要弄清楚调用约定以使用成员函数作为回调,因为不能指定extern "C"
这就是为什么不能将成员函数直接用作 C 回调的原因。
这不是真的,如果它是静态成员函数,你很容易做到。
这不是标准的,可能是不可移植的。【参考方案5】:
确保它在全局范围内
使用extern "C"
如果需要,请使用__cdecl
:void (_cdecl *callback)
【讨论】:
(1) 和 (2) 不是必需的,也不需要。您通过指针而不是名称传递函数。 (3) 与调用约定有关,这确实很重要。它必须与对方的期望相匹配,但可以是“__stdcall”以及“__cdecl”或您平台上的任何其他调用约定。【参考方案6】:严格来说,你不能。必须将 C 回调指定为 extern "C"
,这对于成员函数是不可能的。只能使用独立功能。您可以从那里将调用转发到任何 C++ 函数,包括静态或非静态成员函数。
根据平台的不同,您有时可以跳过extern "C"
而侥幸逃脱,但我不会测试我的运气。
【讨论】:
以上是关于如何使用 C++ 成员函数作为 C 框架的回调函数的主要内容,如果未能解决你的问题,请参考以下文章