C++20: More Details about Module Support of the Big Three
In my last post, “C++20: Module Support of the Big Three“, I compiled a simple module with the Big Three. Today, I drill deeper using the GCC, Clang, and Microsoft Compiler.
Honestly, this post will be pretty technical and end with a curious note. Additionally, it requires basic modules knowledge. If you don’t have it, read my previous post about modules:
- 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
- C++20: Module Support of the Big Three
Compiler Support of Modules
I use the Microsofts cl.exe 19.29.20133 for x64, the Clang 16.0.5, and the GCC 11.1.0 compiler for my experiments.
Microsoft Visual Compiler
Let me start with the Microsoft Compiler.
The Microsoft Visual Compiler provides various options for the using of modules.
Additionally, there are a few common cl.exe
compiler options.
I use various compiler options for the ifc file in the following command lines. The ifc file is the module and contains the metadata description of the module interface.
- Use the module
math.cppm
to create the obj and ifc file.
cl.exe /c /std:c++latest /interface /TP math.cppm
- Use the module
math.cppm
to create only the ifc file.
cl.exe /c /std:c++latest /ifcOnly /interface /TP math.cppm
- Use the module
math.cppm
to create the obj filemath.obj
and the ifc filemathematic.ifc
.
cl.exe /c /std:c++latest /interface /TP math.cppm /ifcOutput mathematic.ifc
- Create the executable
client.exe
and use the ifc filemath.inter
.
cl.exe /std:c++latest client.cpp math.obj /reference math.inter
- Create the executable
client.exe
and explicitly use the ifc filemath.inter
that is in the directoryifcFiles
.
cl.exe /std:c++latest client.cpp math.obj /ifcSearchDir ifcFiles /reference math.inter
Let’s continue with the Clang compiler.
Clang Compiler
The Clang compiler provides various options for the creation of modules.
Modernes C++ Mentoring
Do you want to stay informed: Subscribe.
For more details, refer to the official Standard C++ Modules documentation. In the following command lines, I use the compiler options for the module and the ifc file.
- Use the module declaration file
math.cppm
to create the pcm filemath.pcm
.
clang++ -c -std=c++20 -fmodule-output math.cppm -o math.pcm
- Use the module with the extension ixx (
math.ixx
) to create the pcm filemath.pcm
.
clang++ -std=c++20 --precompile -x c++-module math.ixx -o math.pcm
- Create the pcm file and use it.
clang++ -std=c++20 -c math.pcm -o math.o clang++ -std=c++20 -fprebuilt-module-path=. math.o client.cpp -o client.exe
- Use the pcm file
other.pcm
and compile it.
clang++ -std=c++20 -c client.cpp -fmodule-file=math=other.pcm -o client.o
Finally, here is the GCC compiler.
GCC Compiler
The following table shows the few GCC options.
Many options of the Big Three are about header units.
Header Units
Header units are a binary representation of header files and represent a transition from headers to modules. You must replace the #include
directive with the new import
statement and add a semicolon (;
).
#include <vector> => import <vector>; #include "myHeader.h" => import "myHeader.h";
For more information about header units, read my previous post “Private Module Fragment and Header Units“. In the following lines, I play with header units and use the following files:
- The header file
head.h
// head.h #include <iostream> void hello();
- The source file
head.cpp
importing the header unit
// head.cpp import "head.h"; void hello() { std::cout << '\n'; std::cout << "Hello World: header units\n"; std::cout << '\n'; }
- The main program
helloWorld3.cpp
importing the header unit
// helloWorld3.cpp import "head.h"; int main() { hello(); }
I will create a header unit from the header file head.h for
the Microsoft Visual Compiler and the GCC Compiler. In contrast to the official documentation Standard C++ Modules, I could not master header units with the Clang Compiler.
Microsoft Visual Compiler
These are the necessary steps to use header units.
cl.exe /std:c++latest /EHsc /exportHeader head.h cl.exe /c /std:c++latest /EHsc /headerUnit head.h=head.h.ifc head.cpp cl.exe /std:c++latest /EHsc /headerUnit head.h=head.h.ifc helloWorld3.cpp head.obj
- The flag
/exportHeader
in line 1 causes the creation of the ifc filehead.h.ifc
from the header filehead.h
. - The implementation file
head.cpp
(line 2) and the client filehelloWordl3.cpp
(line 3) use the header unit. The flag/headerUnit head.h=head.h.ifc
imports the header unit and tells the compiler the name of the ifc file for the specified header.
GCC Compiler
Creating and using the module consists of three steps.
g++ -fmodules-ts -fmodule-header head.h -std=c++20 g++ -fmodules-ts -c -std=c++20 head.cpp g++ -fmodules-ts -std=c++20 head.o helloWorld3.cpp -o helloWorld3
- Line 1 creates the module
head.gcm
. The flag-fmodule-header
specifies the creation of a header unit from the headerhead.h
. - The following line creates the object file
head.o
. - Finally, line 3 creates the executable that implicitly refers to the module
head.gcm
.
As promised, here is a curious note.
Reachability versus Visibility
With modules, you have to distinguish between reachability and visibility. When a module exports some entity, an importing client can see and use it. Non-exported entities are not visible but may be reachable.
// bar.cppm module; #include <iostream> export module bar; struct Foo { void writeName() { std::cout << "\nFoo\n"; } }; export struct Bar { Foo getFoo() { return Foo{}; } };
The module bar
exports the class Bar
. Bar
is visible and reachable. On the contrary, Foo
is not visible.
// bar.cpp #include <utility> import bar; int main() { Bar b; // Foo f; // (1) auto f = b.getFoo(); f.writeName(); // (2) using FooAlias = decltype(std::declval<Bar>().getFoo()); // (3) FooAlias f2; // (4) f2.writeName(); // (5) }
The class Foo
is not exported and, therefore, not visible. Its usage in line (1) would cause a linker error. On the contrary, Foo
is reachable because the member function getFoo
(in bar.cppm
) returns it. Consequentially, the function writeName
(line 2) can be invoked. Furthermore, I can create a type alias to Foo
(line 3), use it to instantiate Foo
(line 4), and invoke writeName
(line 5) on it. The expression std::declval<Bar>().getFoo()
(line 3) returns the object that a call Bar.getFoo()
would return. Finally, decltype
returns the type of this hypothetical object.
What’s Next?
In my next post, I will dive deeper into the ranges library in C++20.
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, Matt Godbolt, and Honey Sukesan.
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,