Modules are a relatively new C++ feature. Ian Bruntlett documents what he did to get them working with an older gcc compiler.
Motivation
Bjarne Stroustrup’s current C++ primer, Programming: Principles and Practice Using C++ (PPP3) [Stroustrup24] uses C++ modules and my system’s gcc compiler does not support them. Bjarne has a habit of referring to very new facilities in his books – TC++PL2 (exceptions on Sun Workstations), I’m thinking of you! This article documents what I had to do to get ‘Hello World’ from PPP3 to build successfully on a different system’s Ubuntu Linux 24.04.3 (or later) LTS x86_64 with not much additional software installed.
Learners using PPP3 who are using the Gnu Compiler Collection (gcc) either have to resort to using #includes or somehow get to grips with using modules. The problem with #includes is that they can be really inefficient. This is because when a #include is encountered, that file’s contents – and the contents of any nested #includes – are compiled for each C++ source file (aka translation unit) being built that includes them. Modules provide a way to reduce that workload on the compiler whilst increasing the demands made on compiler authors.
This article relies on a collection of files to work. They will be made available on Github [Bruntlett].
Other build systems
There are all sorts of ways to optimise builds other than make. CMake is apparently the most popular build system generator but I am completely unfamiliar with it and my current goal is to work my way through Bjarne Stroustrup’s book (PPP3), so I used GNU Make.
Before you start
By the time you read this, your compiler might already support modules and so most of this article will be redundant to you. Try building greetings.cpp, included as Listing 5 in this article. If it fails and you get told to install a newer compiler, to misquote Lestat “I’m going to give you the article I never had”.
Getting import std; to work
For this article, initially I used Jonathan Wakely’s ‘Binary packages for GCC snapshots’ [Wakely] web page to get a more up to date, if somewhat experimental, compiler. To get access to the compiler, you download the latest snapshot .deb file from the web page.
I had to install a package, make, as well as the compiler. To install make, I used these commands:
# get information on current packages sudo apt update # update the system with current packages sudo apt upgrade sudo apt install make
I created a makefile to automate building programmes and perform other tasks as well. In make, these tasks are called targets. For the sake of convenience, I added some extra targets in the makefile. If you’re not sure what targets are, simply view these extra targets as commands you can request from the makefile at the command line. (See Figure 1 for the usage of make.)
$ make help makefile to build executables and also a bit of housework. make hello-std - make Hello World programme that does an import std; make greetings - a more modern Hello World programme. make hello-ppp3 - make Hello World programme that uses the PPP module. make all - make module files and the example programmes. make std.o - make the standard library module files. make PPP.o - make utility module for the book PPP3 \(Stroustrup\). make from the book PPP3. make clean - delete the executable files and object files, typically done after updating the compiler. make look-for-compiler - see what the current compiler version is make get-the-compiler - download the most recent compiler module_std = /opt/gcc-latest/include/c++/16.0.0/bits/std.cc g++ (GCC) 16.0.0 20260111 (experimental) |
| Figure 1 |
The command make look-for-compiler looks at the gcc-latest web page and tells you what the latest version is.
$ make look-for-compiler Checking the current version of the compiler ================================================= wget --spider --content-disposition https:// kayari.org/gcc-latest/gcc-latest.deb 2>&1 | grep Location Location: https://kayari.org/gcc-latest/ gcc-latest_16.0.1-20260222git6e35a5d5b297.deb [following]
The command make get-the-compiler downloads the latest gcc-latest .deb file only if it hasn’t been downloaded into the current directory already. (See Figure 2.)
$ make get-the-compiler Only downloading the compiler if we don't already have it. ================================================= wget --no-clobber --content-disposition https://kayari.org/gcc-latest/gcc-latest.deb --2026-02-26 09:17:08-- https://kayari.org/gcc-latest/gcc-latest.deb Resolving kayari.org (kayari.org)... 176.126.242.131 Connecting to kayari.org (kayari.org)|176.126.242.131|:443... connected. HTTP request sent, awaiting response... 302 Found Location: https://kayari.org/gcc-latest/gcc-latest_16.0.1-20260222git6e35a5d5b297.deb [following] --2026-02-26 09:17:08-- https://kayari.org/gcc-latest/gcc-latest_16.0.1-20260222git6e35a5d5b297.deb Reusing existing connection to kayari.org:443. HTTP request sent, awaiting response... 200 OK Length: 463517736 (442M) [application/vnd.debian.binary-package] Saving to: 'gcc-latest_16.0.1-20260222git6e35a5d5b297.deb' gcc-latest_16.0.1-20260222git6e35a5d5b2 100%[========================================================================>] 442.04M 11.2MB/s in 40s 2026-02-26 09:17:48 (11.2 MB/s) - 'gcc-latest_16.0.1-20260222git6e35a5d5b297.deb' saved [463517736/463517736] |
| Figure 2 |
The command to install the compiler, where ‘name-of-gcc-latest-file.deb’is replaced by the name of the .deb file you downloaded is:
sudo apt install ./name-of-gcc-latest-file.deb
The makefile used in this article doesn’t need /opt/gcc-latest/bin to be on the PATH. However, I found it convenient. I used the shell script in Listing 1 to do so and to change to a working directory.
#!/bin/bash
if ! echo $PATH | grep gcc-latest/bin: - ; then
echo gcc-latest not on path... putting it on
set -x
PATH=/opt/gcc-latest/bin:$PATH
cd ~/gcc-new
set +x
else
echo gcc-latest already on path
fi
|
| Listing 1 |
I invoke it on the command line with:
source prepare-for-gcc-latest
or the briefer:
. prepare-for-gcc-latest
I do this at the command line rather than in .bashrc because I don’t want to hide the standard gcc, which is relied upon by vlc and other packages.
You can use ‘TAB-completion’ for the command line as well. Type in the shortest unique prefix and then press tab for the command line to be auto-completed.
You can compile your own code (e.g. for another-example.cpp that uses the PPP module) by editing the makefile (see Listing 2) and adding something like this line:
another-example: std.o PPP.o another-example.cpp
# makefile to use when working on examples for # Bjarne Stroustrup's book, "Programming # Principles and Practice Using C++" (3rd # Edition), working on examples using Ubuntu # 24.04.4 LTS x86_64. # # Ian Bruntlett, January 2026 - 26th February # 2026 9:50ish # with help on the accu-general mailing list from # Jonathan Wakely # and on the accu-members for people to test # these example programmes. # Tell the C++ compiler to include debugging # information (-g), have # some useful warnings, # that modules are in use (-fmodules), and # that we are using a bleeding edge version of # the C++ language. # tell make where to find the C++ compiler CXX = /opt/gcc-latest/bin/g++ # flags for the C++ compiler CXXFLAGS = -g -Wall -Wshadow -Wconversion -fmodules -std=c++23 # the names of the programmes built using this # makefile executables = hello-std hello-ppp3 greetings # website address for gcc-latest, so we can check # its version - $ make look-for-compiler # or download it - make get-the-compiler compiler_link = https://kayari.org/gcc-latest/gcc-latest.deb module_std=$(shell find /opt/gcc-latest/ -iname std.cc) # linker flags, to locate library files without # having to set: # export LD_LIBRARY_PATH=/opt/gcc-latest/lib64 LDFLAGS += -Wl,-rpath,/opt/gcc-latest/lib64 # if you want to create a linker map file for # extra info on your executables then uncomment # this line below: # LDFLAGS += -Wl,-Map=$@.map all : $(executables) # Note that commands are preceded by a TAB # character and not a sequence of space # characters. # Build a module for using the Standard C++ # Library # gcm.cache/std.gcm - the # 'compiled module interface' # std.o - contains the modules definitions. std.o: $(module_std) $(CXX) $(CXXFLAGS) -c $(module_std) # Build a module for using the examples from the # book PPP # gcm.cache/ppp.gcm - the ‘compiled module # interface' for PPP extensions # PPP.o - contains the PPP extensions definitions. PPP.o: PPP.ixx $(CXX) $(CXXFLAGS) -c PPP.ixx # build an executable file that uses the Standard # Library module hello-std: std.o hello-std.cpp # build an executable file that greets the user # using modern C++ facilities greetings : std.o greetings.cpp # build an executable file that uses the PPP # module from the book PPP3 hello-ppp3: std.o PPP.o hello-ppp3.cpp .PHONY: clean clean: rm -rfv gcm.cache/ rm -fv $(executables) *.o *.map # check to see the version information of the # current compiler .PHONY: look-for-compiler look-for-compiler: @echo Checking the current version of the compiler @echo ==================================== wget --spider --content-disposition $(compiler_link) 2>&1 | grep Location # download the current compiler .PHONY: get-the-compiler get-the-compiler: @echo Only downloading the compiler if we don\'t already have it. @echo ========================================= wget --no-clobber --content-disposition $(compiler_link) # display help message .PHONY: help help: @echo makefile to build executables and also a bit of housework. @echo "make hello-std - make Hello World programme that does an import std;" @echo "make greetings - a more modern Hello World programme." @echo "make hello-ppp3 - make Hello World programme that uses the PPP module." @echo "make all - make module files and the example programmes." @echo "make std.o - make the standard library module files." @echo "make PPP.o - make utility module for the book PPP3 \(Stroustrup\)." @echo "make from the book PPP3." @echo "make clean - delete the executable files and object files" @echo " typically done after updating the compiler." @echo "make look-for-compiler - see what the current compiler version is" @echo "make get-the-compiler - download the most recent compiler" @echo module_std = $(module_std) @$(CXX) --version | head -1 # PPP files from # https://stroustrup.com/programming.html # a few programmes by me, a makefile, a # supporting shell script, and my draft article module-article.tar.gz : PPP.ixx PPP.h PPP_support.h PPPheaders.h\ hello-std.cpp hello-ppp3.cpp greetings.cpp \ makefile prepare-for-gcc-latest \ irb-cpp-stdlib-a-matter-of-import.odt \ irb-delete-elves tar -czf $@ $^ |
| Listing 2 |
Listing 3 contains is the code (hello-std.cpp) I used to test my environment.
/* hello-std.cpp
compile with:
make hello-std
Whilst Bjarne Stroustrup likes to use this in his examples:
using namespace std;
I prefer not to do so. This is because in medium-sized programmes and larger, this can cause name collisions and headaches so I have to prefix names from the standard library with std::
*/
import std;
int main()
{
std::cout << "System : " ;
std::flush(std::cout);
std::system ("hostname");
std::cout << "FILE : " __FILE__ " "
__DATE__ " " __TIME__ "\n";
std::cout << "gcc : " << __GNUC__ << '.'
<< __GNUC_MINOR__ << '.'
<< __GNUC_PATCHLEVEL__ << '\n';
std::cout << "standard: " << __cplusplus
<< '\n';
}
|
| Listing 3 |
g++ currently implements modules by creating a gcm.cache directory and creates an ELF (Executable and Linker Format) file named after the module it contains – in this case std.gcm. To get extra information about the ELF file, you can use the command:
$ readelf -a std.gcm | less
Getting PPP3 examples to build
This article only covers the building of text mode modules. Those PPP3 examples that use graphics with Qt will have to wait. I went to https://stroustrup.com/programming.html and downloaded PPP.ixx. PPP_support.h, and PPP.h – this gives you the means to build the PPP module. However, I got a compilation failure for PPP_support as size_t was not declared. To get this to work, I edited PPP.ixx to read as follows:
export module PPP;
export import std;
using std::size_t; // added by me to get things
// to build
#define PPP_EXPORT export
#include "PPP_support.h"
using namespace PPP;
To see if I could access the PPP module, I then created a new file, hello-ppp3.cpp (Listing 4).
/* hello-ppp3.cpp
Programme to use some of PPP3's code - in this
case, a checked string which will complain at
runtime when abused.
compile with:
make hello-ppp3
Bjarne Stroustrup likes to use this in PPP.h:
using namespace PPP;
using namespace std;
This makes code easier for a novice reader.
I prefer not to do so. This is because in
medium-sized programmes and larger, this can
cause name collisions and headaches.
However, PPP.h is used by this program which
means I can write cout instead of std::cout.
*/
#include "PPP.h" // Use Bjarne's PPP3 tutorial
// module
int main()
{
cout << "Hello from : " __FILE__ "\n";
// this line use's Bjarne's PPP::Checked_string
// because PPP.h does this preprocessor
// trickery:
// #define string Checked_string
string astring="Hello\n";
cout << astring;
cout << "About to get a deliberate runtime "
"error\n";
// get a runtime error, to show that
// PPP::Checked_string is working.
astring[100]='!'; // bang! deliberately exceed
// the string's text
}
|
| Listing 4 |
A more modern Hello World
As recommended by Lieven de Cock on the accu-members email mailing list, Listing 5 is a more modern take on “Hello, World”, using std::println and std::source_location.
/* greetings.cpp
compile with:
make greetings
Sources:
https://en.cppreference.com/w/cpp/utility/source_location.html
https://en.cppreference.com/w/cpp/io/println.html
Whilst Bjarne Stroustrup likes to use this in his examples:
using namespace std;
I prefer not to do so. This is because in medium-sized programmes and larger, this can cause name collisions and headaches so I have to prefix names from the standard library with std::
*/
import std;
int main()
{
std::source_location here
= std::source_location::current();
std::println("Greetings from {},
function {} @ {}:{}",
here.file_name(),
here.function_name(),
here.line(),
here.column()
);
}
|
| Listing 5 |
Compiling your own code
As you write your C++ code, you could modify the makefile to build your code OR, if you are dealing with individual source files (e.g. I wrote a small programme for the exercise on page 50 of PPP3, called: 50-ex-11-how-many-coins.cpp. To get it to compile and link, all I had to do was:
$ make std.io PPP.o 50-ex-11-how-many-coins
and make used its knowledge to execute this command line:
/opt/gcc-latest/bin/g++ -g -Wall -Wshadow -Wconversion -fmodules -std=c++23 -c /opt/gcc-latest/include/c++/16.0.0/bits/std.cc /opt/gcc-latest/bin/g++ -g -Wall -Wshadow -Wconversion -fmodules -std=c++23 -c PPP.ixx /opt/gcc-latest/bin/g++ -g -Wall -Wshadow -Wconversion -fmodules -std=c++23 -Wl,-rpath,/opt/gcc-latest/lib64 50-ex-11-how-many-coins.cpp -o 50-ex-11-how-many-coins
If you have already built std.o or PPP.o, make will refrain from re-building those files as they are up to date.
Updating your compiler
When your compiler gets updated, I’d advise you to delete your object files and executables and then rebuild the executables, typically done via a ‘make clean all’ command. At the moment, this requires looking at Jonathan Wakely’s webpage, waiting for a new release, and then downloading and installing as previously discussed.
GNU Software manuals
- C++ Standards support in GCC: https://gcc.gnu.org/projects/cxx-status.html
- GNU Bash manual: https://www.gnu.org/software/bash/manual/
- GNU Make manual: https://www.gnu.org/software/make/manual/
- GNU Compiler & library manuals: https://www.gnu.org/software/gcc/onlinedocs/
- GNU Linker manual: https://sourceware.org/binutils/docs/
More resources
A fair amount of articles have been written and presentations given on modules; here are a couple.
- ‘C++ Modules: A Brief Tour’ by Nathan Sidwell, in Overload 159, available from the archive at: https://accu.org/journals/overload/28/159/sidwell/
- ‘Modules (since C++20), available from C++ reference at: https://en.cppreference.com/w/cpp/language/modules.html
References
[Bruntlett] Files associated with this article: https://github.com/ian-bruntlett/studies/tree/main/cpp/PPP3/gcc-latest
[Stroustrup24] Bjarne Stroustrup (2024) Programming: Principles and Practice Using C++ (3rd edition), Addison-Wesley Professional.
[Wakely] Jonathan Wakely, ‘Binary packages for GCC snapshots’, at https://jwakely.github.io/pkg-gcc-latest/
Since getting to grips with Git, Ian has been re-learning C++ using Programming: Principles and Practice Using C++ and also runs sessions of the sci-fi TTRPG (Mongoose) Traveller.









