gcc - 如何在结构定义中结合 __attribute__((dllexport)) 和 [[nodiscard]]?

Posted

技术标签:

【中文标题】gcc - 如何在结构定义中结合 __attribute__((dllexport)) 和 [[nodiscard]]?【英文标题】:gcc - How to combine __attribute__((dllexport)) and [[nodiscard]] in a struct definition? 【发布时间】:2019-09-18 13:22:54 【问题描述】:

我有一个标有 C++17 的 [[nodiscard]] 属性的结构。它是这样定义的:

struct [[nodiscard]] MyObject final

    explicit MyObject(int id);
    ~MyObject();

    MyObject(const MyObject&) = delete;
    MyObject& operator=(const MyObject&) = delete;

    int id;
;

现在我想从我的动态库中导出它。

在 MSVC 上,语法 struct __declspec(dllexport) [[nodiscard]] MyObject final 按预期工作。

但是 GCC 无法同时编译 struct __attribute__((dllexport)) [[nodiscard]] MyObject finalstruct [[nodiscard]] __attribute__((dllexport)) MyObject final:编译器无法处理这样的语法。

语法__attribute__((dllexport)) struct [[nodiscard]] MyObject final 编译但似乎没有做我想要的,因为它会产生以下警告:

:1:49: 警告: 'struct 声明中忽略的属性 MyObject' [-Wattributes]

1 | __attribute__((dllexport)) struct [[nodiscard]] MyObject final

  |                                                 ^~~~~~~~

:1:49: 注意:'struct MyObject' 的属性必须遵循 “结构”关键字

那么,如何从 GCC 上的动态库中导出 [[nodiscard]] 结构?

【问题讨论】:

考虑到 Clang handles 是 struct __attribute__((dllexport)) [[nodiscard]] MyObject 版本,它可能是 GCC 中的一个错误(或者更确切地说是缺少某个功能)。另请注意,Windows 上的 GCC handles __declspec(dllexport) @andrey 您提供的链接向我显示了带有__attribute__((visibility("default"))) 而不是__attribute__((dllexport)) 的代码。如果我用__attribute__((dllexport)) 替换它,我会收到以下警告:unknown attribute 'dllexport' ignored [-Wunknown-attributes] 那是因为带有 Clang 的 Compiler Explorer 节点正在运行 Linux。 dllexport 用于 DLL,它们是 Windows 共享库。 Linux(也可能是 MacOS)上共享库的等效属性是visibility("default"),在命令行上是-fvisibility=hidden。在为 Linux 构建时,编译器自然无法识别 dllexport 属性(它是如何编写的),但重要的是该属性被正确解析(即使它随后被忽略)。 @andrey 是的,我在 Linux 上测试了该代码,但我对在 Linux 上创建共享库知之甚少,所以我认为 Linux 编译器也能够以某种方式识别 dllexport .好的,所以我应该改用__attribute__((visibility("default")))。这很清楚。那么,事实上,它很可能是一个(又一个)GCC 错误。如果您将其写为答案,我会将其标记为已接受。 【参考方案1】:

所有这些都适用于dllexportdllimport

这似乎是 GCC 解析器中的一个错误。

Clang manages to parse struct __attribute__((dllexport)) [[nodiscard]] MyObject ... 版本。

正如@Artyer 所述,GCC(和 Clang)支持 dllexport - [[gnu::dllexport]] 的 C++ 语法。

还应该注意的是,Windows 上的 GCC (MinGW) 支持 __declspec(dllexport) 以与 Visual C++ 兼容,并且实际上可以正确解析 class __declspec(dllexport) [[nodiscard]] Test ...(使用 GCC 8.1.0 测试)。


以上所有内容都假设您正在为 Windows 编译,dllexport 实际上意味着什么。在其他平台上,编译器会简单地忽略它(通常会发出警告)。

在 Linux 上,应该使用 -fvisibility=hidden 隐藏所有符号,除了由属性 visibility("default") 选择的符号之外。没有“导入”替代方案 - 在构建和使用库时都使用 "default"。在 Linux 上导出类时,您不想导出的任何内容都可以标记为 visibility("hidden") 以覆盖类的属性。

GCC 和 Clang 支持 visibility 的两种语法:__attribute__((visibility("default")))[[gnu::visibility("default")]]

更多关于 GCC 可见性的信息可以在HERE找到。

我不确定从共享库中导出符号在 MacOS 上的工作方式(可能与在 Linux 上相同?)。

【讨论】:

很好的答案,谢谢。我还想补充一点,__attribute__((visibility("default")))[[gnu::visibility("default")]] 不能完全互换。 __attribute((visibility("default"))) int foo();int __attribute((visibility("default"))) foo(); 都有效,但 C++ 的标准属性语法更严格:[[gnu::visibility("default")]] int foo(); 可以正常工作,而 int [[gnu::visibility("default")]] foo(); 不能正常工作:(【参考方案2】:

尝试使用 C++ 属性而不是使用__attribute__

struct [[gnu::dllexport]] [[nodiscard]] MyObject final

也可以使用定义来处理 MSVC

【讨论】:

我认为这行不通。我认为[[gnu::dllexport]] 可能存在,并且在在*** 上提出这个问题之前专门搜索了它。但那时我什么也没发现。现在,当我尝试编译您的代码时,我收到警告 'gnu::dllexport' scoped attribute directive ignored,这意味着 [[gnu::dllexport]] 是 GCC 的未知属性。 @Taras 我用 GCC 8.1.0 尝试了 MinGW,它识别了 [[gnu::dllexport]][[gnu::dllimport]](并且该符号实际上已导出)。也许您不在 Windows 上? @Taras 或者您使用的是不支持 [[gnu::dllexport]] 的旧版 GCC? @andrey 在上面的 cmets 中回复了你。 @Taras 有。这是[[gnu::visibility("default")]] :)【参考方案3】:

截至 2022 年,这仍然是 gcc 中的一个问题。用于此的 gcc bugzilla 条目是 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69585

【讨论】:

以上是关于gcc - 如何在结构定义中结合 __attribute__((dllexport)) 和 [[nodiscard]]?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 gcc 中静态初始化 __m128i 数组?

GCC 中有固定大小的整数吗?

avr-gcc:如何将 __attribute__((address)) 与 EEMEM 一起使用?

gcc中预定义的宏__GNUC__

如何在 avr-gcc 中定义定时器

gcc 结构中的内存对齐