代码之家  ›  专栏  ›  技术社区  ›  Hassan Syed

命名空间嵌套函数的最佳实践和语义以及外部“C”的使用

  •  3
  • Hassan Syed  · 技术社区  · 14 年前

    我正在创建一个带有C-ABI接口的C++库。

    这就是GCC如何处理外部“C”限定符的方式:

    namespace x {
    
        extern "C" int monkey(int x) {
            return 1;
        }
    
        int chimpanzee(int x) {
            return 1;
        }
    }
    

    相关的 nm 输出:

    00000000004005cd T _ZN1x10chimpanzeeEi
    00000000004005bf T monkey
    

    问题: 我希望将涉及到C-ABI的函数留在命名空间内,以最大限度地重用。 重要提示: 编译完库后,我将给链接器一个映射文件(GCC)或模块定义文件(MSVC)。

    1. 损坏输出是否是标准行为——其他主要编译器(具体来说是MSVC)是否也会损坏输出?
    2. 当涉及外部ABI时,它们在名称空间中放置函数时是否存在任何陷阱或最佳实践?
    3. 这是否会干扰链路期间已损坏功能的C-ABI导出?
    2 回复  |  直到 11 年前
        1
  •  2
  •   John Dibling    14 年前

    这是给MSVC的。

    名称空间本身没有名称损坏,但是当名称损坏发生时,名称空间的名称会合并到函数(或对象)的名称中。此过程未记录,但已描述 here .

    通过跳转回答你的具体问题:

    1) 没有关于名称损坏的标准定义行为。标准实际上说的是实现为 extern "C" 结构:

    7.5.3[连杆规范]

    每项实施均应规定 链接到C中编写的函数 程序设计语言“C”与链接 C++函数,“C++”。[示例:

    complex sqrt(complex); // C + + linkage by default 
    extern "C" { double sqrt(double); // C linkage } 
    

    –结束示例]

    归根结底,这意味着因为C没有 namespace s、 如果 外部“C” 函数或对象在命名空间中,导出的名称将丢失命名空间限定。这会导致。。。

    3) 是的,你可能有连接问题。试试这个:

    主.h

    #ifndef MAIN_API
    #   define MAIN_API __declspec(dllexport)
    #endif
    
    namespace x
    {
        extern "C" MAIN_API void foo();
    };
    
    namespace y
    {
        extern "C" MAIN_API void foo();
    };
    

    主.cpp

    #include <cstdlib>
    #include <iostream>
    using namespace std;
    #define MAIN_API __declspec(dllexport)
    #include "main.h"
    
    void x::foo()
    {
        cout << "x::foo()\n";
    }
    
    void y::foo()
    {
        cout << "y::foo()\n";
    }
    
    int main()
    {
    }
    

    这将发出链接器错误,因为 外部“C” -教育版 x::foo() y::foo() 已丢失其命名空间标识,因此它们最终的名称完全相同: foo()

    2) 与此相关的最佳实践。如果必须为命名空间中的函数导出C-ABI,则必须注意最终导出的名称不相同。在某种程度上,这违背了使用 命名空间 首先。但你可以这样做:

    #ifndef MAIN_API
    #   define MAIN_API __declspec(dllexport)
    #endif
    
    namespace x
    {
        extern "C" MAIN_API void x_foo();
    };
    
    namespace y
    {
        extern "C" MAIN_API void y_foo();
    };
    
        2
  •  5
  •   Steve M    14 年前

    你所做的一切都很好,会给你想要的效果。从 C++程序设计语言,第三版 ,第208页:“可以在命名空间中声明具有C链接的名称。命名空间将影响在C++程序中访问名称的方式,而不是链接器看到它的方式。这个 printf() std 是一个典型的例子。即使打电话来 std::printf() ,还是老C 打印() ."