在C ++中,对C API回调使用静态成员函数指针是否安全/可移植?

在C ++中,对C API回调使用静态成员函数指针是否安全/可移植? 静态成员函数的ABI是否与C函数相同?


每个C ++标准都不安全。 正如此SO帖子所述:

用C ++实现的AC回调函数必须是extern“C”。 它可能看起来像一个类中的静态函数,因为类静态函数通常使用与C函数相同的调用约定。 但是,这样做是一个等待发生的错误(请参阅下面的注释),所以请不要 - 通过外部“C”包装来代替。

根据Martin York在答案中提出的意见,在某些平台上尝试这样做存在真实世界的问题。

使您的C ABI回调extern "C"


编辑:从标准添加一些支持报价(重点是我的):

3.5“计划和联系”:

在所有的类型调整(typedef(7.1.3)被它们的定义替换)之后, 由所有引用给定对象或函数的声明指定的类型应该是相同的 ,除了数组对象的声明可以指定数组类型由于存在或不存在主要数组绑定(8.3.4)而不同。 在类型标识上违反此规则不需要诊断。 [3.5 / 10]

[注意:可以使用链接规范(7.5)来实现与非C ++声明的链接。 ] [3.5 / 11]

7.5“连接规格”:

...具有不同语言链接的两种功能类型是不同类型的,即使它们在其他方面是相同的。 [7.5 / 1]

因此,如果回调代码使用C语言绑定进行回调,则回调目标(在C ++程序中)也必须如此。


在搜索和几次休息的同时攻击其他问题后,我发现了一个清晰而简洁的答案(无论如何都是标准的):

通过函数类型具有与被调用函数定义的函数类型的语言链接不同的语言链接的表达式来调用函数是未定义的。 [5.2.2 / 1]

我仍然认为从基本层面上使用C ++标准中的文本来定义用C编译器编译的C库的行为,以及中介语互操作性如何工作与实现相关的问题仍然存在问题; 然而,这是我认为或者标准可以(当前)希望定义这种交互的最接近的。

特别是,这是未定义的行为(并且不使用C库,因此不会出现问题):

void call(void (*pf)()) { pf(); } // pf() is the UB
extern "C" void f();
int main() { call(f); }
// though I'm unsure if a diagnostic is required for call(f)

Comeau确实在call(f)中给出了诊断(尽管即使诊断不是必需的,也可以这样做)。

这不是未定义的行为,并且显示了如何在函数指针类型(它通过typedef)中包含语言链接:

extern "C" typedef void F();
void call(F* pf) { pf(); }
extern "C" void f();
int main() { call(f); }

或者可以写成:

extern "C" {
typedef void F();
void f();
}
void call(F* pf) { pf(); }
int main() { call(f); }

对于我所知道的所有Windows C ++编译器,答案是肯定的,但语言标准中没有任何内容保证这一点。 但是,我不会让它阻止你,但这是使用C ++实现回调的一种非常常见的方式 - 但是您可能会发现需要将静态函数声明为WINAPI。 这是从我自己的旧线程库中提取的:

class Thread {
   ...
   static DWORD WINAPI ThreadFunction( void * args );
};

这是te Windows线程API使用的回调函数。

链接地址: http://www.djcxy.com/p/96363.html

上一篇: In C++, is it safe/portable to use static member function pointer for C API callbacks?

下一篇: Declaring a static member function const or virtual