Hands on Modules in C++ 20

Xin
4 min readOct 24, 2020

C++ 20 introduces modules, a feature that eliminates the problems introduced by traditional header files. The syntax of using a module: import , is quite similar to modern scripting languages like Python and JavaScript. I’m very curious about how import works under C++ and what changes does this new feature bring to modern C++. In this article, I’ll try a simple example with modules.

Setup

The modules feature is quite new, so we need the latest compilers and tools supporting C++ 20 standards.

Here I’d recommend Clang 10. Just download the pre-built binaries and make sure you could use clang++ tools. If you’re a Windows user, enabling WSL2 would help setting up the compiler tools.

$ sudo apt install build-essential libncurses5$ bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)"

Once you’ve installed Clang 10, make sure it could be found in your path: which clang++

Hello Module!

Great! Let’s now create our first module hellowolrd.cpp :

/// helloworld.cpp
export module helloworld;
export auto hello() { return "Hello C++ 20!"; }

Then, let’s create our main.cpp , we can simply import the helloworld module we just created! By default, all the exported functions can be directly used once we import the module:

/// main.cpp
#include <iostream>
import helloworld;
int main() {
std::cout << hello() << std::endl;
return 0;
}

Now, let’s compile it. There are two steps in compiling this program:

  1. Compile the module into .pcm , which stands for Pre-compiled Module
  2. Compile main.cpp with the .pcm file

In Clang 10, we have to specify the flag -std=c++2a with other flags. Refer to the Clang Modules page for detailed explanation.

$ clang++ -std=c++2a -c helloworld.cpp -Xclang -emit-module-interface -o helloworld.pcm$ clang++ -std=c++2a -stdlib=libc++ -fprebuilt-module-path=. main.cpp helloworld.cpp

Run the compiled program, and we shall see the “Hello C++ 20!” output

$ ./a.out
Hello C++ 20!

Import Standard Library

We notice that in the above main.cpp , we use both #include and import , which is kinda ugly. Could we also import the standard library? Yes. All we have to do is simply replace the #includewith import . Don’t forget to add the semicolon at the end of the line.

// main.cpp
import <iostream>;
import helloworld;

To compile it, we have to add another two flags: -fimplicit-modules and -fimplicit-module-maps . Currently, Clang doesn’t support Semantic Import, which means you couldn’t do something like: import std.io (but MSVC supports). So we could compile the main.cpp like this:

$ clang++ -std=c++2a -stdlib=libc++ -fimplicit-modules -fimplicit-module-maps -fprebuilt-module-path=. main.cpp helloworld.cp

Use namespace in Module

We can further modify the above example to export functions within a namespace. Therefore, the helloworld.cpp would be:

/// helloworld.cpp
module;
import <cstdio>;
export module helloworld;
export namespace helloworld {
int global_data;
void say_hello() {
std::printf("Hello Module! Data is %d\n", global_data);
}
}

Use helloworld namespace in main.cpp

/// main.cpp
import helloworld;
int main() {
helloworld::global_data = 123;
helloworld::say_hello();
}

Here, we used import <cstdio> in our module, to successfully compile it, we need to specify -stdlib=libc++

$ clang++ -std=c++2a -stdlib=libc++ -fimplicit-modules -fimplicit-module-maps -c helloworld.cpp -Xclang -emit-module-interface -o helloworld.pcm
$ clang++ -std=c++2a -stdlib=libc++ -fimplicit-modules -fimplicit-module-maps -fprebuilt-module-path=. main.cpp helloworld.cpp
$ ./a.out
Hello Module! Data is 123

Good Practice of Module

Modules the beginner’s guide — Daniela Engert — Meeting C++ 2019 is a really good tutorial talking about modules in C++ 20. The speaker gives a good practice of writing a module:

module;
#include <standard library header>
#include "library not ready for modularization"
...
export module top.middle.bottom;
import modularized.standard.library.component;
import other.modularized.library;
...
#include "module internal header" // beware!non-exported declarations;
...
export namespace top {
namespace middle {
namespace bottom {
exported declarations;
...
}}}

Takeaways

The import syntax and module seems promising, but wait, do we really need to use module now in 2020?

  • Module is still at very early stage for most compilers
  • Different compilers have their own implementation of modules. Not all the features of modules are supported yet

Such new features in C++ 20 will become popular someday when the standards, compilers, IDEs are ready for the new changes.

I think modules are fun to play with. Even though we won’t see modules being widely used this or next years, who knows if it will replace header files in five to ten years, right?

Fun fact: I first learnt about modules back in 2017, when Bjarne Stroustrup visited our school and gave a talk about it.

--

--

Xin

Software Engineer at Bloomberg | Cornell University Alumni | Ex-Intern at Uber, Microsoft | Open Source Enthusiast | GitHub: imfing