代码之家  ›  专栏  ›  技术社区  ›  Emile Cormier

在C++中,使用静态成员函数指针来实现C/API回调是安全的/可移植的吗?

  •  26
  • Emile Cormier  · 技术社区  · 15 年前

    4 回复  |  直到 15 年前
        1
  •  20
  •   Community CDub    4 年前

    它是不安全的,每一个C++标准。如中所述 this SO posting :

    C++中实现的C回调函数必须是外部的“C”。它似乎在类中作为静态函数工作,因为类静态函数通常使用与C函数相同的调用约定。然而,这样做是一个等待发生的bug(请参阅下面的注释),所以请不要使用外部“C”包装。

    根据 Martin York 在这个答案中,在某些平台上尝试这样做存在现实问题。

    进行你的C ABI回调 extern "C" .


    编辑:从标准中添加一些支持性引用(emphasis mine):

    3.5“程序和链接”:

    引用给定对象或函数的所有声明所指定的类型应相同

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

    ... 有两种具有不同语言连接的函数类型 不同类型 即使它们在其他方面是相同的[7.5/1]

        2
  •  6
  •   Roger Pate Roger Pate    15 年前

    在搜索和解决其他问题时,我找到了一个清晰简洁的答案(无论如何,对于standardese):

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

    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); }
    
        3
  •  5
  •   anon anon    15 年前

    对于所有我知道的Windows C++编译器,答案是肯定的,但是语言标准中没有任何东西保证这一点。但是,我不会让你停止,这是一种非常普遍的使用C++实现回调的方法,但是你可能需要声明静态函数WiAPI。这是从我自己的旧线程库中获取的:

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

    其中,这是te Windows线程API使用的回调。

        4
  •  3
  •   Roger Pate Roger Pate    15 年前

    ABI不被C或C++标准覆盖,即使C++确实通过“语言链接”来实现。 extern "C" . 因此,ABI基本上是特定于编译器/平台的。这两个标准都让很多很多事情有待于实现,这就是其中之一。


    据我所知,ISO的规则不允许一个标准超过每10年一次(但可以有各种出版物,如TC1和TR1 for C++)。此外,还有一个想法(我不确定这是否来自ISO,是否是从C委员会,甚至是从其他地方继承过来的)是“提炼/标准化现有实践,而不是进入左边的领域,还有 许多的 现有做法,其中一些做法相互冲突。