【问题标题】:Shared libraries and c++20 modules共享库和 c++20 模块
【发布时间】:2021-11-25 20:49:42
【问题描述】:

关于在共享库中正确使用 C++20 模块的在线文档很少。很多人areclearlyinterested,但我一直没能找到明确的解决方案。

在 MSVC 中,编译库时需要使用dllexport,使用符号时需要使用dllimport。这可以使用“旧版 C++”中的宏来完成,但这不适用于 C++20 模块,因为无论预处理器指令如何,代码都只编译一次。

This post 建议您现在只需要使用dllexport,而dllimport 将由编译器自动处理。然而,这来自一个现在已被删除的评论,我找不到任何关于该主题的可靠来源。

如何使用 C++20 模块创建共享库?

【问题讨论】:

  • 即使您可能正在使用 C++20 标准,也可能想要做一个feature test 以确保您的编译器支持模块
  • 我一直在玩弄 MSVC 16.11.1,一切都运行得相当顺利。共享库是迄今为止的第一个“主要”障碍。

标签: c++ shared-libraries c++20 c++-modules


【解决方案1】:

C++20 模块与共享库没有特殊关系。它们主要是头文件的替代品。

这意味着您将使用 C++20 模块以与使用 C++20 之前的头文件类似的方式开发共享库,至少以我目前的理解。您设计了一些导出的 API(不幸的是仍然使用供应商特定的属性,如 __declspec(dllexport)__attribute__((visibility("default"))))并实现它。您构建您的共享库文件 (.dll/.so) 和一个用于分发的导入库,方法与以前相同。然而,不是分发头文件,而是分发模块接口单元。模块接口单元是在顶部包含 export module ABC; 声明的文件。

然后,使用该共享库的可执行文件将使用 import ABC; 导入该模块,而不是使用 #include 导入头文件。

【讨论】:

  • 这没有回答问题。模块接口单元是在这些声明中使用__declspec(dllexport) 构建的。但是要导入一个着色器库函数,你需要一个声明__declspec(dllimport)。模块系统是否理解并在导入时自动将dllexport 转换为dllimport,还是必须构建具有不同声明的新模块?
  • @NicolBolas 是的,我应该解决这个问题。据我所知,答案是肯定的,MSVC 在导入模块时必须将 declspec-dllexports 视为 dllimports。据我所知,它是这样做的。
  • 谢谢。如果能有一些来自 Microsoft 的官方文档来确认这一点,那就太好了。
【解决方案2】:

背景

声明模块接口或模块分区的翻译单元将被视为模块单元,并且在编译时将生成目标文件和二进制模块接口 (BMI)。 BMI是抽象语法树的二进制表示,即表示程序语法和数据类型的数据结构。我们有传统的 C++ 编译管道:

程序 -> 预编译器 -> 词法分析器 -> 解析器 -> 汇编器 -> 链接器

使用 GCC,我们应该添加编译器标志-c,它告诉编译器编译和汇编但不链接。

但是共享库是由链接器通过一起读取几个编译的目标文件并创建一个共享对象来构建的。所以这发生在 BMI 建立之后。并且 BMI 可以在不将它们连接在一起的情况下构建,因为这是两个不同的阶段。

模块可见性

在 C# 中构建 DLL 时,我们在类级别具有可见性属性,即。 publicprivateinternal。在 C++ 中,我们可以通过模块分区获得相同的功能。

使用module <module> : <partition>; 声明的模块分区将在声明export module <module>; 的编译单元内完全可见,但在该模块外不可见。这让我想起了 C# 中的 internal 模式。但是,如果我们使用export module <module> : <partition>; 导出分区,那么它的声明将是公开可见的。阅读更多cppreference

示例

我已经用 GCC (g++-11) 解决了这个问题,请参阅here

本质上,您不需要 DLL 导入/导出,因为(可能)不涉及任何标头。我已经尝试插入这些可见性属性,但我的编译器抱怨,所以我想我们可能根本不需要它们。除此之外,这是标准程序。我也在这里复制/粘贴我的示例:

主要

import <iostream>;
import mathlib;


int main()
{
    int a = 5;
    int b = 6;
    std::cout << "a = " << a << ", b = " << b << '\n';

    std::cout << "a+b = " << mathlib::add(a, b) << '\n';
    std::cout << "a-b = " << mathlib::sub(a, b) << '\n';
    std::cout << "a*b = " << mathlib::mul(a, b) << '\n';
    std::cout << "a/b = " << mathlib::div(a, b) << '\n';

    return 0;
}

图书馆

export module mathlib;

export namespace mathlib
{
    int add(int a, int b)
    {
        return a + b;
    }

    int sub(int a, int b)
    {
        return a - b;
    }

    int mul(int a, int b)
    {
        return a * b;
    }

    int div(int a, int b)
    {
        return a / b;
    }
}

生成文件

GCC=g++-11 -std=c++20 -fmodules-ts
APP=app

build: std_headers mathlib main

std_headers:
    $(GCC) -xc++-system-header iostream

mathlib: mathlib.cpp
    $(GCC) -c $< -o $@.o
    $(GCC) -shared $@.o -o libmathlib.so

main: main.cpp
    $(GCC) $< -o $(APP) -Xlinker ./libmathlib.so

clean:
    @rm -rf gcm.cache/
    @rm -f *.o
    @rm -f $(APP)
    @rm -f *.so

跑步

g++-11 -std=c++20 -fmodules-ts -xc++-system-header iostream
g++-11 -std=c++20 -fmodules-ts -c mathlib.cpp -o mathlib.o
g++-11 -std=c++20 -fmodules-ts -shared mathlib.o -o libmathlib.so
g++-11 -std=c++20 -fmodules-ts main.cpp -o app -Xlinker ./libmathlib.so
./app
a = 5, b = 6
a+b = 11
a-b = -1
a*b = 30
a/b = 0

现在这显然是特定于平台的,但该方法应该适用于其他平台。我也用 Clang 测试了类似的东西(与链接相同的 repo)。

【讨论】:

    猜你喜欢
    • 2021-01-14
    • 1970-01-01
    • 2011-06-18
    • 1970-01-01
    • 1970-01-01
    • 2012-04-24
    • 2020-04-04
    • 1970-01-01
    • 2015-02-27
    相关资源
    最近更新 更多