Modules 是 C++ 20 标准里面非常重要的一个新特性,引进了在其它编程语言中常用的 import
关键词,让 C++ 在几十年前就有的 #include
基础上支持更现代的模块设计,减轻传统的头文件带来的一些问题,提升编译的速度。于是我尝试了一下用 Modules 写一个简单的 Hello World。
安装 Clang 10
由于 Modules 是 C++ 20 的标准,因而较老的编译器并不一定能够支持这一特性,推荐最新的 Clang 10.0.0。首先安装必要的运行时与编译库:
|
|
可以到 LLVM 的 下载页面 选择自己的平台下载,解压后即可在 bin
目录中找到 Clang 及 LLVM 的相关可执行文件。建议使用 Linux 平台以避免一些使用问题。也可以直接用 LLVM 提供的安装脚本:
|
|
Hello World Module
接着我们创建一个简单的 helloworld.cpp
文件,实现一个最简单的 helloworld
Module,通过 export
关键词将函数变量提供给外部程序使用:
|
|
接着创建 main.cpp
,使用 import
关键词导入 helloworld
Module:
|
|
编译分为两步,首先我们需要将用到的模块编译成 BMI(Binary Module Interface),即先把 helloworld.cpp
模块编译成 .pcm
(precompiled module)文件,再编译 main.cpp
。注意这里需要 -std=c++2a
,用 -std=c++20
就有问题,迷…… 用 g++10 编译命令似乎更简单,估计等 Clang 对 C++ 20 的标准支持更完善了之后,编译会更方便。
用 Clang 10 编译和运行:
|
|
用 import 代替 #include
进一步,我们可以将上面的 #include
也用 import
来代替。注意最后需要添加分号 ;
。
|
|
这时候我们在编译的时候需要额外两个 flag:-fimplicit-modules
和 -fimplicit-module-maps
,具体可以参考 Clang 的文档。现在 Clang 还不支持 Semantic import,也就是无法使用 import std.io
语法(MSVC 支持)。这里我们直接用 import
替换 #include
,去除 #include
使语法暂时保持一致。
由于之前的 helloworld
模块并没有任何变化,因此只需要重新编译一下第二步即可:
|
|
Module 中使用 namespace
在一个模块里,我们也能 export
命名空间,下面的例子演示了如何导出和使用 helloworld
的 namespace
,避免带来全局函数的干扰。
|
|
|
|
编译命令和之前基本相同,只不过因为模块里面用到了 import <cstdio>
,因此在编译模块的时候需要指定 -stdlib=libc++
以及 -fimplicit-modules
和 -fimplicit-module-maps
:
|
|
Module 的规范
一个推荐的 Module 规范结构如下
|
|
写在后面
Module 还有一系列的特性,比如将接口(Interface)和实现(Implementation)分离,模块分割(Module Partitions)等等,更多高级的用法可以参考视频:Modules the beginner's guide - Daniela Engert - Meeting C++ 2019。
但是目前主流的编译器对于 C++ 20 的标准还没有支持完备,因此很多功能也只能先“尝鲜”,等到一段时间后或许这些新的标准会像 C++ 11 一样让 C++ 再次焕发新的活力。
参考:
- Modules: The Beginner's Guide
- Hello World with C++2a modules
- Understanding C++ Modules: Part 1: Hello Modules, and Module Units
- Overview of modules in C++ | Microsoft Docs
- C++20: Module Interface Unit and Module Implementation Unit
- C++20 Modules | PureCPP
- Modules Are Not Precompiled Headers
- Modules in Clang 11