3

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

这就是 GCC 如何处理关于修饰的 extern "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 导出吗?
4

2 回答 2

5

你所做的很好,会给你你想要的效果。摘自The C++ Programming Language,第 3 版,第 208 页:“可以在命名空间中声明具有 C 链接的名称。命名空间将影响 C++ 程序中访问名称的方式,但不会影响链接器看到它的方式。printf()fromstd是一个典型的例子。......即使用 调用std::printf(),它仍然是相同的旧 C printf()

于 2010-11-15T16:12:19.240 回答
2

这是为 MSVC 准备的。

命名空间本身没有进行名称修改,但是当发生名称修改时,命名空间的名称会合并到函数(或对象)的名称中。此过程未记录在案,但在此处进行了描述。

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

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

7.5.3 【联动规格】

每个实现都应提供与用 C 编程语言“C”编写的函数的链接,以及与 C++ 函数“C++”的链接。[例子:

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

——结束示例]

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

3) 是的,您可能会遇到联动问题。试试这个:

主文件

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

主文件

#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()
{
}

这将发出链接器错误,因为 和 的-edextern "C"版本丢失了它们的命名空间标识,因此它们最终具有完全相同的名称: x::foo()y::foo()foo()

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

#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();
};
于 2010-11-15T16:21:05.137 回答