背景
声明模块接口或模块分区的翻译单元将被视为模块单元,并且在编译时将生成目标文件和二进制模块接口 (BMI)。
BMI是抽象语法树的二进制表示,即表示程序语法和数据类型的数据结构。我们有传统的 C++ 编译管道:
程序 -> 预编译器 -> 词法分析器 -> 解析器 -> 汇编器 -> 链接器
使用 GCC,我们应该添加编译器标志-c,它告诉编译器编译和汇编但不链接。
但是共享库是由链接器通过一起读取几个编译的目标文件并创建一个共享对象来构建的。所以这发生在 BMI 建立之后。并且 BMI 可以在不将它们连接在一起的情况下构建,因为这是两个不同的阶段。
模块可见性
在 C# 中构建 DLL 时,我们在类级别具有可见性属性,即。 public,private,internal。在 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)。