我刚才注意到C++标准说C和C++函数有不同的和不兼容的类型,即使它们的类型签名是相同的(更多信息请参阅这个问题).这意味着您在技术上不允许将C++函数传递给C函数pthread_create().
我很好奇是否有任何平台在使用两个ABI实际上是不同的(除了显而易见的名称差异).具体来说,有没有人知道这个C++程序无法编译和运行的任何平台?
#include <assert.h>
extern "C" int run(int (*f)(int), int x) { return f(x); }
int times2(int x) { return x * 2; }
int main(int argc, char *argv[]) {
int a = times2(argc);
// This is undefined behavior according to C++ because I am passing an
// "extern C++" function pointer to an "extern C" function.
int b = run(×2, argc);
assert(a == b);
return a;
}
Run Code Online (Sandbox Code Playgroud) 因此,在接受James Kanze和Loki Astari关于C联动的培训时,我对此感到疑惑:
extern "C" int foo1 (void (*)());
extern "C" { int foo2 (void (*)()); }
Run Code Online (Sandbox Code Playgroud)
在我上学之后,我认为必须foo1只使用带有C++链接foo2的函数指针,而只需要带有C链接的函数指针.我的理解是否正确?C++标准中是否有特定的参考资料来解释上述示例中的差异?
编辑:为了让每个人都能更方便地遵循这里的一个带有C++ 11草案标准相关部分的pastebin.
假设我正在使用一个C API,它允许您注册带有void*闭包的回调:
void register_callback(void (*func)(void*), void *closure);
Run Code Online (Sandbox Code Playgroud)
在C++中,拥有更强大的类型是很好的,void*所以我想创建一个包装器,让我注册强类型的C++回调:
template <typename T, void F(T*)>
void CallbackWrapper(void *p) {
return F(static_cast<T*>(p));
}
void MyCallback(int* param) {}
void f(void *closure) {
register_callback(CallbackWrapper<int, MyCallback>, closure);
}
Run Code Online (Sandbox Code Playgroud)
这没关系.这个解决方案的一个很好的属性是它可以将我的回调内联到包装器中,所以这个包装方案没有开销.我认为这是一个要求.
但是如果我能让API看起来更像这样会很好:
void f2() {
RegisterCallback(MyCallback, closure);
}
Run Code Online (Sandbox Code Playgroud)
我希望通过推断模板参数来实现上述目的.但我无法弄清楚如何让它发挥作用.我到目前为止的尝试是:
template <typename T>
void RegisterCallback(void (*f)(T*), T* closure) {
register_callback(CallbackWrapper<T, f>, closure);
}
Run Code Online (Sandbox Code Playgroud)
但这不起作用.任何人都有一个神奇的咒语,可以使f2()上面的工作,同时保持零开销性能特征?我想要一些适用于C++ 98的东西.