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

发布于 2024-10-02 17:38:56 字数 623 浏览 9 评论 0原文

我正在创建一个带有 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 导出吗?

I am creating a C++ library with a C-ABI interface.

This is how GCC treats the extern "C" qualifier with regards to mangling:

namespace x {

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

    int chimpanzee(int x) {
        return 1;
    }
}

The relevant nm output:

00000000004005cd T _ZN1x10chimpanzeeEi
00000000004005bf T monkey

Question:
I want to leave functions which are involved in the C-ABI inside a namespace, for maximum flexibility for reuse. Important Note: Once the library has been compiled I will give the linker a map file (GCC) or a module definition file (MSVC).

  1. Is the mangling output standard behaviour -- will other major compilers (MSVC in specific) strip mangling as well ?
  2. are their any pitfalls or best-practises regarding placing functions in a name-space when they are involved in an external ABI?
  3. Will this interfere with the C-ABI export of the de-mangled functions during link time ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

妖妓 2024-10-09 17:38:56

你所做的一切都很好,并且会给你带来你想要的效果。来自The C++ 编程语言,第 3 版,第 208 页:“具有 C 链接的名称可以在名称空间中声明。名称空间将影响在 C++ 程序中访问名称的方式,但不会影响名称空间。链接器看待它的方式是 std 中的 printf() ......即使使用 std::printf() 调用,它仍然是旧的 C printf()。”

What you're doing is fine and will give you the effects that you want. From The C++ Programming Language, 3rd Edition, page 208: "A name with C linkage can be declared in a namespace. The namespace will affect the way the name is accessed in a C++ program, but not the way a linker sees it. The printf() from std is a typical example. … Even when called with std::printf(), it is still the same old C printf()."

余生一个溪 2024-10-09 17:38:56

这是针对 MSVC 的。

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

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

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 } 

—结束示例]

最终这意味着,由于 C 没有命名空间的概念,如果 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()
{
}

这将发出链接器错误,因为 x::foo()y 的 extern "C" 版本::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();
};

This is for MSVC.

The namespace itself is not name-mangled, but the name of the namespace is incorporated in to the function's (or object's) name when name mangling occurs. This process is undocumented, but described here.

Answering your specific questions by jumping around:

1) There is no Standard-defined behavior regarding name mangling. What the Standard actually says is that implementations provides a C-compatible linkage for extern "C" constructs:

7.5.3 [Linkage specifications]

Every implementation shall provide for
linkage to functions written in the C
programming language, "C", and linkage
to C + + functions, "C++". [Example:

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

—end example]

Ultimately what this means is that since C has no concept of namespaces, if extern "C" functions or objects in namespaces, your exported names will lose the namespace qualification. This leads to...

3) Yes, you can have a linkage problem. Try this:

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

This will emit a linker error because the extern "C"-ed versions of x::foo() and y::foo() have lost their namespace identification, so they end up with exactly the same name: foo()

2) Best practices regarding this. If you must export a C-ABI for functions in namespaces, you have to be careful that the names you end up exporting are not the same. To some degree, this defeats the purpose of using a namespace in the first place. But you can do something like this:

#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();
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文