【问题标题】:extern "C" linkage inside C++ namespace?C++ 命名空间内的外部“C”链接?
【发布时间】:2015-05-13 20:27:02
【问题描述】:
namespace someNameSpace {
    extern "C" void doSomething()
        {
             someOperations();
        }
}

我想在 C++ 和 C 环境中运行 doSomething()

如果我将 someNameSpace 暴露给 extern "C" 链接,someNameSpace 是否仍然封装 doSomething()

有没有一种好方法可以在 C++ 和 C 之间共享函数,同时避免污染 C++ 端的全局命名空间?

编辑:因为这段代码主要用于C++模式,而C链接仅供测试使用,我想这是一个更好的方法。

namespace someNameSpace {
    #ifdef COMPILE_FOR_C_LINKAGE
    extern "C"
    #else
    extern "C++"
    #endif
    { 
        void doSomething()
            {
                 someOperations();
            }
    }
}

【问题讨论】:

  • 您是否尝试在 C 程序中使用它?如果有,发生了什么?
  • @RyanJ 是的,我试过了,它在 C++ 和 C 中都能很好地编译和链接。我还没有运行它。

标签: c++ c namespaces


【解决方案1】:

您的代码有效,但您应该注意所有具有extern "C" 链接的函数共享相同的名称空间,但不要与“命名空间”的 C++ 概念混淆:您的函数实际上是 someNameSpace::doSomething ,但您不能在任何其他命名空间中拥有任何其他具有非限定名称 doSomethingextern "C" 函数。

参见 7.5/6:

至多一个具有特定名称的函数可以具有 C 语言链接。函数的两个声明 与具有相同函数名称(忽略限定它的命名空间名称)的 C 语言链接 出现在不同的命名空间作用域指的是同一个函数。 C 变量的两个声明 具有相同名称的语言链接(忽略限定它的命名空间名称)出现在不同的 命名空间范围指的是同一个变量。具有 C 语言链接的实体不得声明为 与全局范围内的变量同名,除非两个声明都表示同一个实体;没有诊断是 如果声明出现在不同的翻译单元中,则需要。具有 C 语言链接的变量不应 与具有 C 语言链接的函数同名声明(忽略命名空间名称 限定各自的名称);如果声明出现在不同的翻译中,则不需要诊断 单位。 [注意:对于具有给定名称和 C 语言链接的实体,只有一个定义可以出现在 程序(见 3.2);这意味着这样一个实体不能在多个命名空间中定义 范围。 — 尾注]

贵公司或项目的全球风格仲裁员应该能够就适合您的代码库的命名策略向您提供建议。

【讨论】:

  • 具有 C 语言链接的实体不得声明为与全局范围内的变量同名,除非两个声明都表示同一个实体; ---- 这是一个“实体与变量”的陈述吗?我用 g++ 4.3.3 进行了实验,这似乎是真的。仅当我重新声明像“extern int doSomething=1;”这样的全局变量时才会发生冲突但如果我定义另一个“extern void doSomething(){}”也没关系,只要它不是另一个外部 C 链接。
  • 您在第一段中的最后陈述是错误的——您可以在其他命名空间中定义另一个doSomething,只要它不是extern "C"
  • @ChrisDodd:是的,我就是这个意思。已编辑。谢谢!
【解决方案2】:

只是一段代码来说明 Kerrek SB 答案中所述的行为

#include <iostream>

namespace C{
    void Hello(){
        std::cout<<"Hello"<<std::endl;
    }
    extern "C" void HelloThere(){
        std::cout<<"Hello There from extern \"C\""<<std::endl;
    }
}

extern "C" void HelloThere();

int main() {
    C::Hello();
    C::HelloThere(); //Compiles
    //Hello(); <--- does not compile
    HelloThere(); //Also compiles and prints the same as C::HelloThere() !!!

    return 0;
}

直播http://ideone.com/X26wfR

【讨论】:

    【解决方案3】:

    在 C++ ABI 中,命名空间必须被破坏。所以这个:

    namespace foo
    {
        void bar(int){}
    }
    

    或多或少翻译成这样的符号:

    foo::bar(int)
    

    当你强制编译器使用 C ABI 时,类似符号

    namespace foo
    {
        extern "C" void bazz(int){}
    }
    

    编译后如下图:

    bazz
    

    你可以看到神螺栓的区别: https://godbolt.org/z/BmVpSQ

    在 C ABI 中,函数没有名称空间或参数列表,因此整个代码中只能有 1 个这样的符号。定义两次:

    namespace foo
    {
        extern "C" void bazz(int){}
    }
    
    namespace foo2
    {
        extern "C" void bazz(int){}
    }
    
    int main()
    {
        foo2::bazz(5);
        return 0;
    }
    

    ...是非法的。在这种情况下,clang 编译器只会发出简单的编译错误:

    https://wandbox.org/permlink/r5CUXm7OKePtG35L

    gcc 编译器也会发出错误,但看起来更加神秘:

    https://wandbox.org/permlink/BiN0auna9klBg5GE

    【讨论】:

    • 不确定在发布此答案时是否有所不同,但是当我单击链接时,gcc 在编译期间似乎发出了错误(有趣的是,在组装阶段,我认为我第一次在编译的那个阶段看到错误)
    • @DominickPastore 你是对的 - 虽然编辑错误消息很奇怪。我已经编辑了答案。
    猜你喜欢
    • 2011-05-11
    • 1970-01-01
    • 2018-09-10
    • 1970-01-01
    • 1970-01-01
    • 2021-12-23
    • 1970-01-01
    • 2011-08-27
    • 1970-01-01
    相关资源
    最近更新 更多