【问题标题】:How to declare an extern C function inside a function in C++?如何在 C++ 中的函数内声明外部 C 函数?
【发布时间】:2018-05-08 19:30:23
【问题描述】:

我有一个内联函数,大致是这样的:

inline void SomeFunction() {
    extern void SomeOtherFunction();
    SomeOtherFunction();
}

这是一个简化:我的函数确实有参数和返回值。

但是,我希望此标头在 C 和 C++ 文件中都可以使用。目前,链接失败是因为 C++ 文件尝试使用 C++ 链接查找 SomeOtherFunction 的实现。我想我可以通过使用 extern "C" 来解决这个问题:

inline void SomeFunction() {
#ifdef __cplusplus
    extern "C" void SomeOtherFunction();
#else
    extern void SomeOtherFunction();
#endif
    SomeOtherFunction();
}

这会导致 Clang 失败:

error: expected unqualified-id
    extern "C" void SomeOtherFunction();
           ^

我怎样才能正确地做到这一点?

【问题讨论】:

  • 如果你的函数依赖于另一个只在链接时可用的函数,它永远不会被内联。
  • 实际的错误信息是什么?
  • @melpomene "Expected unqualified-id" 是完整的错误信息(还有文件名)。
  • @wham3 不,不是。至少它缺少文件名和行号,但我怀疑您遗漏了更多内容。
  • test.mm:3:12: 错误:预期不合格-id extern "C" void SomeOtherFunction(); ^

标签: c++ c


【解决方案1】:

extern "C" 是一个链接规范。 C++ 标准部分 7.5 链接规范 paragraph 4 声明:

linkage-specification 只能出现在命名空间范围 (3.3) 中。

例如您可以在全局命名空间或某些特定命名空间中说 extern "C"。在命名空间之外是非法的。

函数声明虽然在较小的范围内是可能的。如果您删除链接规范,您的代码将编译(但不是链接):

inline void SomeFunction() {
    extern void SomeOtherFunction();
    SomeOtherFunction();
}

如果您确实需要在较小范围内声明SomeOtherFunction(例如从全局范围中隐藏),您可以将您的声明放入一个专用范围的头文件中,然后在您的函数中使用:

标题:

namespace other {
    extern "C" void SomeOtherFunction();
}//namespace other

代码:

void SomeFunction()
{
    other::SomeOtherFunction();
}

归功于 stackoverflow 上的这两个答案:herehere

【讨论】:

    【解决方案2】:

    来自 C++11 标准(在 [dcl.link],强调我的):

    4 联动规范嵌套。当链接规范嵌套时,最里面的一个确定语言链接。链接规范不建立范围。 链接规范只能出现在命名空间范围内

    linkage-specification 指的是extern <em>string-literal</em> ...,在你的情况下是extern "C"。)

    这意味着您不能在类或函数中包含extern "C"

    SomeFunction 中声明SomeOtherFunction 有什么意义?它仍然必须是全局符号并且对链接器可见。

    那么为什么不这样做呢?

    #ifdef __cplusplus
        extern "C" 
    #endif
    void SomeOtherFunction();
    
    inline void SomeFunction() {
        SomeOtherFunction();
    }
    

    以下似乎也有效:

    extern "C" {
        inline void SomeFunction() {
            extern void SomeOtherFunction();
            SomeOtherFunction();
        }    
    }
    

    但它会产生使SomeFunction 也使用 C 链接的副作用(希望这没问题,因为(根据您的要求)它也需要从 C 中使用)。

    【讨论】:

    • 虽然这是解决问题的好方法,但它不能回答为什么extern "C" ... 在函数内部不合法。
    • 我猜如果你在 C++ 的“私有”等价函数中声明函数。链接器会找到它,但是如果您编写库,则库的用户将看不到它。我想这就是 OP 的重点。
    • @melpomene,我同意这不是 OP 的问题。这是好奇心的问题:)
    • @melpomene 您看到它不起作用,不是吗?这样做有什么意义?
    • @BJovke 我从来没有声称extern "C" 在函数中是可能的。您的回答声称 extern (如在任何外部符号的声明中)不允许在函数内部,这是不正确的:“只能在块内声明匿名函数(lambdas 和函数对象)。" 而且我知道我的第二个示例使SomeFunction 具有C 链接;我的回答明确表示。
    【解决方案3】:

    你可以这样做。我假设你有一个头文件、一个 C 源代码和一个 C++ 源代码。

    头文件:

    inline void SomeFunction()
    {
        void SomeOtherFunction_Bridge();
        SomeOtherFunction_Bridge();
    }
    

    C++ 源代码:

    extern "C" 
    {
        void SomeOtherFunction();
    }
    
    void SomeOtherFunction_Bridge()
    {
        SomeOtherFunction();
    }
    

    C源码:

    void SomeOtherFunction()
    {
        // Function with a code doing something
    }
    
    void SomeOtherFunction_Bridge()
    {
        SomeOtherFunction();
    }
    

    在 GCC 上检查,它可以编译。

    【讨论】:

      【解决方案4】:

      extern 告诉编译器在其他模块中定义了一个符号(函数、变量)。编译此模块(C++ 文件)时,目标文件包含它需要的外部符号列表。所以这是在 C++ 文件的级别上,它不能在更小的范围内(函数、块)。

      【讨论】:

      • 那是完全错误的。您可以在块中声明函数。
      • @melpomene 否。只有匿名函数(lambdas 和函数对象)可以在块内声明。给我一个可编译的例子,以其他方式显示。
      • @BJoke 试试这个:void foo(){extern bar();}
      • @melpomene 这不是问题的重点。 extern "C"extern 的使用取决于定义符号的模块,而不是使用它的模块。因此,如果定义符号的模块是使用 C 链接编译的,则您必须在使用它的模块周围使用相同的东西。它不能有时是“C”,有时不是。
      • @BJovke 对不起,你把我弄丢了。您可以在同一个模块中定义具有 C 链接的符号和具有 C++ 链接的符号。同样,另一个模块可以引用和使用具有 C 链接的符号和具有 C++ 链接的符号。我不明白你在这里想说什么。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-12
      • 2010-11-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多