使用 clang 将标准库导入为模块
Import std lib as modules with clang
我正在尝试使用 clang 中的模块,并希望将标准库作为模块而不是包含包含在内。
目前我这样做
#include <iostream>
#include <string>
It seems that you in msvc 应该能够导入标准库,例如
import std.core;
然而,当使用 clang 时,这似乎没有实现,或者以其他方式实现。
我的问题是:是否可以像微软建议的那样导入 stl-include,或者是否可以将标准 lib include 映射到模块 somhow。
注意:我不能使用 #include <...>
或 #import <...>
的原因是因为其他错误可能会产生自己的问题。所以我认为如果可能的话,获得 import std.core
或类似的是现在的方法。
ModernesCpp 也提到了 std.core.
C++20 标准不包括 C++ 标准库的模块定义。 Visual Studio 确实 (不幸的是),并且那里的许多不良网站都会像标准一样行事。但事实并非如此;这只是微软的事。
如果您想通过跨平台模块包含 C++ 标准库,则必须使用 import <header-name>
语法或编写您自己的标准库模块来导入 headers 并导出特定的C++ 声明。
我找到了我的想法,llvms documentation。有一节说
"例如,C 标准库的模块映射文件可能看起来有点像这样:"
module std [system] [extern_c] {
module assert {
textual header "assert.h"
header "bits/assert-decls.h"
export *
}
module complex {
header "complex.h"
export *
}
module ctype {
header "ctype.h"
export *
}
module errno {
header "errno.h"
header "sys/errno.h"
export *
}
module fenv {
header "fenv.h"
export *
}
// ...more headers follow...
}
看来您随后将此文件命名为 something.modulemap
并将其发送给编译器。快速谷歌搜索没有找到任何类似的 msvc 解决方案(除了前面讨论的 std.io)。
我还没有尝试过,我想我会接受我自己的答案,或者如果其他人想出更好的答案。
我解决了你的任务。以下是执行此操作的说明。我使用当前版本的 LLVM 12.0 中的 CLang 在我的 Win 10 64 位上执行此操作(取自 here), also I have MSVC 2019 v16.9.4 Community installed (taken from here)。
注。这个答案只针对CLang,我也写了similar answer for MSVC.
首先创建以下文件:
module.modulemap:
module std_mod {
requires cplusplus17
header "std_mod.hpp"
export *
}
std_mod.hpp:
#include <iostream>
#include <map>
#include <set>
#include <vector>
use.cpp:
import std_mod;
int main() {
std::cout << "Hello, World!" << std::endl;
}
在上面的文件 std_mod.hpp
中,您可以放置任何您需要的标准 header。您应该将您在所有项目中使用的所有可能的 STD header 放在一起,以便能够在任何地方共享相同的预编译 STD 模块。
然后执行命令:
clang++ -### use.cpp -c -std=c++20 -m64 -g -O3 >use.txt 2>&1
在这里您可以使用项目所需的任何选项而不是 -std=c++20 -m64 -g -O3
。每个预编译模块都应具有与其他 .cpp 文件相同的编译选项,以便能够链接到最终二进制文件。
上面的命令将生成 use.txt
以及您需要复制的选项。在此选项中,您应该删除 -emit-obj
选项、-o
选项(及其后的路径),同时删除 use.cpp
。然后添加到此命令选项字符串 module.modulemap -o std_mod.pcm -emit-module -fmodules -fmodule-name=std_mod
。在我的系统上,我得到了以下结果命令:
"D:\bin\llvm\bin\clang++.exe" "-cc1" module.modulemap -o std_mod.pcm -emit-module -fmodules -fmodule-name=std_mod "-triple" "x86_64-pc-windows-msvc19.28.29914" "-mincremental-linker-compatible" "--mrelax-relocations" "-disable-free" "-disable-llvm-verifier" "-discard-value-names" "-main-file-name" "use.cpp" "-mrelocation-model" "pic" "-pic-level" "2" "-mframe-pointer=none" "-fmath-errno" "-fno-rounding-math" "-mconstructor-aliases" "-munwind-tables" "-target-cpu" "x86-64" "-tune-cpu" "generic" "-gno-column-info" "-gcodeview" "-debug-info-kind=limited" "-resource-dir" "D:\bin\llvm\lib\clang\12.0.0" "-internal-isystem" "D:\bin\llvm\lib\clang\12.0.0\include" "-internal-isystem" "d:\bin2\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.28.29910\include" "-internal-isystem" "d:\bin2\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.28.29910\atlmfc\include" "-internal-isystem" "D:\Windows Kits\10\Include\10.0.19041.0\ucrt" "-internal-isystem" "D:\Windows Kits\10\include\10.0.19041.0\shared" "-internal-isystem" "D:\Windows Kits\10\include\10.0.19041.0\um" "-internal-isystem" "D:\Windows Kits\10\include\10.0.19041.0\winrt" "-O3" "-std=c++20" "-fdeprecated-macro" "-fdebug-compilation-dir" "D:\t\t4" "-ferror-limit" "19" "-fno-use-cxa-atexit" "-fms-extensions" "-fms-compatibility" "-fms-compatibility-version=19.28.29914" "-fdelayed-template-parsing" "-fno-implicit-modules" "-fcxx-exceptions" "-fexceptions" "-vectorize-loops" "-vectorize-slp" "-faddrsig" "-x" "c++"
如您所见,此命令包含完整的包含路径,它们是必需的。执行上面的命令,它会生成 std_mod.pcm
,你可以在你的项目的任何地方使用相同的编译选项。
为什么需要上面的长命令?因为只能通过 -cc1
命令使用 .modulemap
文件,它执行 low-level CLang 前端而不是简化的 CLang driver(driver 没有 -cc1
选项)。这个低水平 front-end 可以做很多 driver 做不到的技巧。
现在你可以编译你的最终程序 use.cpp
执行 import std_mod;
下一个命令:
clang++ use.cpp -o use.exe -std=c++20 -m64 -g -O3 -fmodule-file=std_mod.pcm
看到我添加了 -fmodule-file=std_mod.pcm
- 每个导入的模块都需要这样的选项。作为替代方案,您可以使用 -fprebuilt-module-path=<directory>
指定在何处搜索所有预构建模块。
不久前,我还创建了关于如何在 CLang 中使用 header 制作模块的问答 here。
有关模块的更多说明,请参阅 CLang 的 Modules Doc and CommandLine Doc。
PS。为什么我在上面实施了很长的解决方案?因为至少在 Windows' CLang 下一个简单的程序
import <iostream>;
int main() {}
不编译,它说 use.cpp:1:8: error: header file <iostream> (aka 'd:\bin2\Microsoft Visual Studio19\Community\VC\Tools\MSVC.28.29910\include\iostream') cannot be imported because it is not known to be a header unit
。所以至少在 Win 上需要一个特殊的解决方案,import <header-name>;
的解决方案在这里不起作用。
所有通过import <header>;
或import "header";
语法导入的header 应该有特殊编译的header 单元模块放在特殊文件夹中以便能够使用。并且在 Win STD headers 上没有相应的已编译 header 单元模块。此外,在花了很多时间后,我没有在 CLang 中找到如何在 Win 上创建这些 so-called header 单元的方法。只有上面的解决方案解决了我将 headers 作为模块导入的任务。
我正在尝试使用 clang 中的模块,并希望将标准库作为模块而不是包含包含在内。
目前我这样做
#include <iostream>
#include <string>
It seems that you in msvc 应该能够导入标准库,例如
import std.core;
然而,当使用 clang 时,这似乎没有实现,或者以其他方式实现。
我的问题是:是否可以像微软建议的那样导入 stl-include,或者是否可以将标准 lib include 映射到模块 somhow。
注意:我不能使用 #include <...>
或 #import <...>
的原因是因为其他错误可能会产生自己的问题。所以我认为如果可能的话,获得 import std.core
或类似的是现在的方法。
ModernesCpp 也提到了 std.core.
C++20 标准不包括 C++ 标准库的模块定义。 Visual Studio 确实 (不幸的是),并且那里的许多不良网站都会像标准一样行事。但事实并非如此;这只是微软的事。
如果您想通过跨平台模块包含 C++ 标准库,则必须使用 import <header-name>
语法或编写您自己的标准库模块来导入 headers 并导出特定的C++ 声明。
我找到了我的想法,llvms documentation。有一节说
"例如,C 标准库的模块映射文件可能看起来有点像这样:"
module std [system] [extern_c] {
module assert {
textual header "assert.h"
header "bits/assert-decls.h"
export *
}
module complex {
header "complex.h"
export *
}
module ctype {
header "ctype.h"
export *
}
module errno {
header "errno.h"
header "sys/errno.h"
export *
}
module fenv {
header "fenv.h"
export *
}
// ...more headers follow...
}
看来您随后将此文件命名为 something.modulemap
并将其发送给编译器。快速谷歌搜索没有找到任何类似的 msvc 解决方案(除了前面讨论的 std.io)。
我还没有尝试过,我想我会接受我自己的答案,或者如果其他人想出更好的答案。
我解决了你的任务。以下是执行此操作的说明。我使用当前版本的 LLVM 12.0 中的 CLang 在我的 Win 10 64 位上执行此操作(取自 here), also I have MSVC 2019 v16.9.4 Community installed (taken from here)。
注。这个答案只针对CLang,我也写了similar answer for MSVC.
首先创建以下文件:
module.modulemap:
module std_mod {
requires cplusplus17
header "std_mod.hpp"
export *
}
std_mod.hpp:
#include <iostream>
#include <map>
#include <set>
#include <vector>
use.cpp:
import std_mod;
int main() {
std::cout << "Hello, World!" << std::endl;
}
在上面的文件 std_mod.hpp
中,您可以放置任何您需要的标准 header。您应该将您在所有项目中使用的所有可能的 STD header 放在一起,以便能够在任何地方共享相同的预编译 STD 模块。
然后执行命令:
clang++ -### use.cpp -c -std=c++20 -m64 -g -O3 >use.txt 2>&1
在这里您可以使用项目所需的任何选项而不是 -std=c++20 -m64 -g -O3
。每个预编译模块都应具有与其他 .cpp 文件相同的编译选项,以便能够链接到最终二进制文件。
上面的命令将生成 use.txt
以及您需要复制的选项。在此选项中,您应该删除 -emit-obj
选项、-o
选项(及其后的路径),同时删除 use.cpp
。然后添加到此命令选项字符串 module.modulemap -o std_mod.pcm -emit-module -fmodules -fmodule-name=std_mod
。在我的系统上,我得到了以下结果命令:
"D:\bin\llvm\bin\clang++.exe" "-cc1" module.modulemap -o std_mod.pcm -emit-module -fmodules -fmodule-name=std_mod "-triple" "x86_64-pc-windows-msvc19.28.29914" "-mincremental-linker-compatible" "--mrelax-relocations" "-disable-free" "-disable-llvm-verifier" "-discard-value-names" "-main-file-name" "use.cpp" "-mrelocation-model" "pic" "-pic-level" "2" "-mframe-pointer=none" "-fmath-errno" "-fno-rounding-math" "-mconstructor-aliases" "-munwind-tables" "-target-cpu" "x86-64" "-tune-cpu" "generic" "-gno-column-info" "-gcodeview" "-debug-info-kind=limited" "-resource-dir" "D:\bin\llvm\lib\clang\12.0.0" "-internal-isystem" "D:\bin\llvm\lib\clang\12.0.0\include" "-internal-isystem" "d:\bin2\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.28.29910\include" "-internal-isystem" "d:\bin2\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.28.29910\atlmfc\include" "-internal-isystem" "D:\Windows Kits\10\Include\10.0.19041.0\ucrt" "-internal-isystem" "D:\Windows Kits\10\include\10.0.19041.0\shared" "-internal-isystem" "D:\Windows Kits\10\include\10.0.19041.0\um" "-internal-isystem" "D:\Windows Kits\10\include\10.0.19041.0\winrt" "-O3" "-std=c++20" "-fdeprecated-macro" "-fdebug-compilation-dir" "D:\t\t4" "-ferror-limit" "19" "-fno-use-cxa-atexit" "-fms-extensions" "-fms-compatibility" "-fms-compatibility-version=19.28.29914" "-fdelayed-template-parsing" "-fno-implicit-modules" "-fcxx-exceptions" "-fexceptions" "-vectorize-loops" "-vectorize-slp" "-faddrsig" "-x" "c++"
如您所见,此命令包含完整的包含路径,它们是必需的。执行上面的命令,它会生成 std_mod.pcm
,你可以在你的项目的任何地方使用相同的编译选项。
为什么需要上面的长命令?因为只能通过 -cc1
命令使用 .modulemap
文件,它执行 low-level CLang 前端而不是简化的 CLang driver(driver 没有 -cc1
选项)。这个低水平 front-end 可以做很多 driver 做不到的技巧。
现在你可以编译你的最终程序 use.cpp
执行 import std_mod;
下一个命令:
clang++ use.cpp -o use.exe -std=c++20 -m64 -g -O3 -fmodule-file=std_mod.pcm
看到我添加了 -fmodule-file=std_mod.pcm
- 每个导入的模块都需要这样的选项。作为替代方案,您可以使用 -fprebuilt-module-path=<directory>
指定在何处搜索所有预构建模块。
不久前,我还创建了关于如何在 CLang 中使用 header 制作模块的问答 here。
有关模块的更多说明,请参阅 CLang 的 Modules Doc and CommandLine Doc。
PS。为什么我在上面实施了很长的解决方案?因为至少在 Windows' CLang 下一个简单的程序
import <iostream>;
int main() {}
不编译,它说 use.cpp:1:8: error: header file <iostream> (aka 'd:\bin2\Microsoft Visual Studio19\Community\VC\Tools\MSVC.28.29910\include\iostream') cannot be imported because it is not known to be a header unit
。所以至少在 Win 上需要一个特殊的解决方案,import <header-name>;
的解决方案在这里不起作用。
所有通过import <header>;
或import "header";
语法导入的header 应该有特殊编译的header 单元模块放在特殊文件夹中以便能够使用。并且在 Win STD headers 上没有相应的已编译 header 单元模块。此外,在花了很多时间后,我没有在 CLang 中找到如何在 Win 上创建这些 so-called header 单元的方法。只有上面的解决方案解决了我将 headers 作为模块导入的任务。