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