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

时间:2010-11-15 15:58:35

标签: c++ portability abi

我正在创建一个带有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 个答案:

答案 0 :(得分:5)

你正在做的事情很好,会给你想要的效果。从 C ++编程语言,第3版,第208页:“可以在命名空间中声明具有C链接的名称。命名空间将影响在C ++程序中访问名称的方式,但不会影响链接器看到它的方式。来自printf()的{​​{1}}是一个典型的例子....即使用std调用,它仍然是旧的C std::printf()。“

答案 1 :(得分:2)

这适用于MSVC。

名称空间本身不受名称限制,但在发生名称重整时,名称空间的名称会合并到函数(或对象)的名称中。此过程未记录,但描述为here

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

1)没有关于名称重整的标准定义行为。标准实际上说的是实现为extern "C"构造提供了C兼容的链接:

7.5.3 [链接规范]

  

每个实施都应规定   链接到C中写的函数   编程语言,“C”和链接   到C ++函数,“C ++”。 [实施例:

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

-end example]

最终这意味着由于C没有namespace的概念,如果extern "C"函数或命名空间中的对象,导出的名称将失去名称空间限定。这导致......

3)是的,你可能会遇到链接问题。试试这个:

main.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();
};

的main.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()
{
}

这将发出链接器错误,因为extern "C"版本的x::foo()y::foo()已丢失其名称空间标识,因此它们最终具有完全相同的名称:{{1 }}

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

namespace