C++20: Module Support of the Big Three
I have written almost 100 posts about C++20 in the last four years, but I’m not done. This post continues my story about C++20.
Modules are one of the Big Four in C++20. In my C++20 classes, they are one of the main topics. Sadly, the implementation in GCC and Clang was way behind the Microsoft Compiler. Consequentially, I usually used the Microsoft Compiler in my classes, my talks, and books to present modules. I’m happy to say that the module support of GCC and Clang significantly improved. I will, therefore, present the current module implementation state (10/2023) of the Big Three GCC, Clang, and MSVC in this post.
If you are not familiar with modules in C++20, here is a simple example:
A Simple Module
This is the module math
.
// math1.ixx module; // (1) #include <numeric> #include <vector> export module math; // (2) export int add(int fir, int sec){ return fir + sec; } export int getProduct(const std::vector<int>& vec) { return std::accumulate(vec.begin(), vec.end(), 1, std::multiplies<int>()); }
The global module fragment starts with the keyword module
(line 1) and ends with the exporting module declaration (line 3). The global module fragment is the place to use preprocessor directives such as #include
so that the module can compile. Preprocessor entities used inside the global module fragment are only visible inside the module. The module math
exports the two functions add
and getProduct
.
The client imports the module math
(line 1) and uses its functionality:
// client1.cpp #include <iostream> #include <vector> import math; // (1) int main() { std::cout << '\n'; std::cout << "add(2000, 20): " << add(2000, 20) << '\n'; std::vector<int> myVec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; std::cout << "getProduct(myVec): " << getProduct(myVec) << '\n'; std::cout << '\n'; }
Finally, this is the output of the program:
Of course, there is a lot more to modules you should know. I suggest that you read the following posts:
- The Advantages of Modules
- A Simple math Modul
- Module Interface Unit and Module Implementation Unit
- Structure Modules
- Open Questions to Modules
- Private Module Fragment and Header Units
I started with a simple module, and I will make it even more simple when I discuss the module implementation state of GCC, Clang, and MSVC.
Implementation State
math.ixx
defines the module math
, I will use it in the following comparison.
Modernes C++ Mentoring
Do you want to stay informed: Subscribe.
// math.ixx export module math; // (1) export int add(int fir, int sec){ return fir + sec; }
Additionally, here is the client program client.cpp
importing the module math
.
// client.cpp import math; // (2) int main() { add(2000, 20); }
Line (1) is the exporting module declaration, and line (2) imports the module. For obvious reasons, I will not show you the program’s output.
First, you may wonder why I called the module declaration file math.ixx
. Here is the first irritating point.
Module Declaration File
- The Microsoft compiler uses the extension
ixx
. The suffixixx
stands for a module interface source. - The Clang compiler uses the extension
cppm
. The m in the suffix probably stands for module. - The GCC compiler uses no special extension.
These are only the defaults that you can change. Now, let me compile and use the module math
.
Compile and Use the Module
First, I start with Microsofts cl.exe 19.29.30133 for x64 compiler.
Microsoft Compiler
These are the steps to compile and use the module with the Microsoft compiler. I only show the minimal command line. As promised, more will follow in the next post. Additionally, with an older Microsoft compiler, you must use the flag /std:c++latest
instead of the flag /std:c++20
.
cl.exe /std:c++20 /c math.ixx cl.exe /std:c++20 client.cpp math.obj
- Line 1 creates an obj file
math.obj
and an IFC filemath.ifc
. The IFC is the module and contains the metadata description of the module interface. The binary format of the IFC is modeled after the Internal Program Representation by Gabriel Dos Reis and Bjarne Stroustrup. - Line 2 creates the executable
client.exe
. The compiler implicitly finds the compiledmath.ifc
from the first step.
Now, let’s continue with the Clang compiler
Clang Compiler
I use the Clang 16.0.5 compiler.
The Clang compiler expects a module with the extension cppm. Consequently, I must rename the math.ixx
file to math.cppm
. On the contrary, the file client.cpp
is unchanged. Finally, here are the corresponding build and use steps:
clang++ -std=c++20 -c math.cppm --precompile -o math.pcm clang++ -std=c++20 client.cpp -fprebuilt-module-path=. math.pcm -o client.exe
- Line 1 creates the module
math.pcm
. The suffixpcm
stands for precompiled module and is equivalent to theifc
file extension of the Microsoft Visual Compiler. Additionally, the produced module already includes the module definition. Consequentially, the Clang compiler does not produce an object filemath.o
. The option--precompile
is necessary for creating the precompiled module. - Line 2 creates the executable
client.exe
, which uses the modulemath.pcm
. The Clang compiler requires that you specify the path to the module with the-fprebuilt-module-path
flag. If not, the link process fails:
Finally, let me do it with the GCC compiler.
GCC Compiler
I use the GCC 11.1.0 compiler.
The GCC Compiler neither expected Window’s ixx
nor Clang’s cppm
suffix. Consequently, I rename the math.ixx
file into a cpp file: math.cxx
. The client.cpp
is identical to the one I used with the Microsoft and the Clang compiler.
g++ -c -std=c++20 -fmodules-ts math.cxx g++ -std=c++20 -fmodules-ts client.cpp math.o -o client
- Line 1 creates the module
math.gcm
and the object filemath.o
. I have to specify-fmodules-ts
. The extension –fmodules-ts
irritates me becausets
usually stands for technical specification. The modulemath.gcm
is in the subdirectorygcm.cache
.math.gcm
is the compiled module interface. Presumably,gcm
stands for GCC compiled module. - Line 2 creates the executable
client
. It uses the modulemath.gcm
implicitly.
What’s Next?
This post gave you the first steps of how to build a module with the Big Three. In my next post, I will drill deeper.
Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Stephen Kelley, Kyle Dean, Tusar Palauri, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, Philipp Lenk, Charles-Jianye Chen, Keith Jeffery,and Matt Godbolt.
Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.
My special thanks to Embarcadero | |
My special thanks to PVS-Studio | |
My special thanks to Tipi.build | |
My special thanks to Take Up Code | |
My special thanks to SHAVEDYAKS |
Modernes C++ GmbH
Modernes C++ Mentoring (English)
Rainer Grimm
Yalovastraße 20
72108 Rottenburg
Mail: schulung@ModernesCpp.de
Mentoring: www.ModernesCpp.org
Modernes C++ Mentoring,