带有C语言绑定的静态成员函数?

Posted

技术标签:

【中文标题】带有C语言绑定的静态成员函数?【英文标题】:static member function with C language binding? 【发布时间】:2016-03-06 08:49:49 【问题描述】:

以下 C++ 代码使用 Visual C++ 和 g++ 编译:

struct S

    static void foo();
;

extern "C"
void S::foo() 

struct T

    static void foo();
;

extern "C"
void T::foo() 

auto main() -> int

    S().foo();
    T().foo();

有效吗?

如果它是有效的,因为实现可能在一个单独的翻译单元中,这是否意味着静态成员函数总是具有与 C 函数相同的调用约定(如果不是,它如何不暗示)?

【问题讨论】:

有趣...seems to compile,如果我正确理解了this answer 的第 7 点,在这种情况下 extern "C" 将被忽略。另外,在cppreference.com 上,提到“当类成员声明和成员函数类型声明出现在“C”语言块中时,它们的链接仍然是“C++”” 【参考方案1】:

静态成员函数与 C 函数具有相同的调用约定。但是,名称修改适用。因此,即使您成功地将静态成员声明为 extern "C",当您尝试将其与调用该函数的 C 代码链接时,链接器也可能找不到它。

可以轻松地做的是声明一个包装器/存根,该包装器/存根从普通函数调用静态成员。此外,您可以将静态成员函数的地址分配给普通函数指针。

【讨论】:

请注意,虽然在实践中静态成员函数通常具有与 C 函数相同的调用约定,但标准并不能保证这一点。虽然我不知道详细信息,但请参阅此 SO 答案中的 cmets,以了解显然是这种情况的一个实例:***.com/a/1738425/12711 您引用的示例试图将非静态成员函数转换为普通函数。由于成员函数中隐含的 this 指针,这将不起作用。 我的意思是说,“不幸的是,我被静态方法所困扰,没有与'C'函数相同的调用约定......”。该评论是针对先前对答案的编辑而发布的,该答案提到静态成员函数通常与extern "C" 函数一样有效。【参考方案2】:

C++11 7.5/4《联动规范》

在确定语言链接时忽略 C 语言链接 类成员的名称和类成员的函数类型 功能。

因此,您的示例在没有格式错误或错误的意义上是有效的,但extern "C" 应该对S::foo()T::foo() 没有影响。

【讨论】:

【参考方案3】:

不,它被忽略了,问题是 name mangling(链接阶段的函数命名)。所以诀窍是定义一个 C 函数并使用您的 C++ 静态方法作为存根来调用它,如下所示:

struct S

    static void foo();
;

extern "C" void S_foo_impl();
void S::foo()  S_foo_impl(); 

auto main() -> int

    S::foo();

当然,S_foo_impl 应该在外部 C 模块中定义。

【讨论】:

鉴于问题中的代码不仅可以编译,而且可以链接,我不太明白名称修改可能是个问题。对我来说,标准的措辞“在确定类成员名称的语言链接和类成员函数的函数类型时忽略了 C 语言链接”。 (在another answer 中给出)表示extern "C" 中唯一受尊重的部分是extern,无论如何这是默认值。我错过了什么吗? 为了能够与重载正确链接,C++ 通过在名称中编码类型来破坏函数名称,即像 int f(float) 这样的 C++ 函数在对象模块中被命名为 f_int_float 之类的东西,这是为了能够检索到正确的功能。请记住,链接器通常对类型一无所知。在 C++ 中声明 extern "C",防止名称混淆。当你定义一个 C++ 方法时,根据定义它应该被破坏(extern "C" 被忽略)。这个技巧是使用声明为 C 链接的全局函数(不是任何类型的方法)。这更清楚了吗? 谢谢,我知道 mangling 是什么名字。如前所述,上述内容没有链接问题。只是为了确保我使用单独的编译和函数返回值进行了测试,并且它适用于 Visual C++ 和 g++,这与没有名称修改的情况相反。 它之所以有效,是因为您使用 C++ 编译所有内容。您不能在 C 中编译名为 S::foo 的函数。 extern "C" 是混合 C++ 和 C。如果你用 C++ 编译所有代码;就像您从未定义过extern "C" 一样。我错过了什么吗? 哦,我明白了。好的,您错过了extern "C" 的最常见原因,即使用 C 语言绑定(和调用约定,尽管实际上是相同的)定义回调。我什至没有想过让 C 代码按名称调用该函数,但现在我明白这就是您所说的。对不起。

以上是关于带有C语言绑定的静态成员函数?的主要内容,如果未能解决你的问题,请参考以下文章

c/c++静态函数和静态成员函数

msvc visual c++从静态成员函数中不正确地形成绑定成员函数表达式

提升 C++。将带有签名指针的成员函数指针传递给函数

使用非静态成员函数的 C++ 排序向量

联合变量宏-C语言

C++ - 如何从同一个类的静态函数访问一个类的私有成员?