It’s Time to Use CXX Modules on Modern C++

Igor Machado
8 min readMar 8, 2025

--

After several years of experimentation, on early 2025 we are officially able to use CXX Modules with latest c++20 and c++23 standards on major open-source compilers, like GCC and Clang. This post won’t explore MSVC compiler, so one may try this post on MSVC with C++23 and import std for CMake. This brief post explores basic examples of this groundbreaking feature, specially the C++23 “import std”, for manual builds and also for CMake build system.

What are CXX Modules and what they try to solve?

Basically, we will see some newer file extensions on C++, for those used with .cpp file extension, typical C++ Source files, now we will also have some .cppm (some using .cxx and .ixx too), called Module Interface units. The CXX Modules are able to solve hard diamond problems and cyclic dependencies on C++, as they are independently built into binary objects and also into newer CMIs, Compiled Module Interfaces, with extensions .gcm or .pcm. These are cached artifacts, so one must build on its own system when necessary and not redistribute it. An advantage of CMIs is that they simplify the inclusion of thirdparty code without the legacy copy-and-paste #include<> macro behavior, as an alternative to header-only libraries, that can now be replaced with an import keyword. The import is able to accelerate build times and eventually reduce the application binary size, only importing what is really necessary.

An amazing C++23 Hello World

Now, we can have a beautifully simple hello world with C++23:

// file: main.cpp
import std;
int main() {
std::print("Hello {}", "World");
return 0;
}

We start with the std::print, that finally replaces legacy and unsafe printf and std::cout print systems. It typically resides in #include<print> header, but wait, now it’s all automatically imported with “import std;”. If you need other STL dependencies like std::string, or std::vector, don’t worry, it’s all there. Time to build this first example.

Manually building the hello world on GCC and Clang — Installation

We demonstrate how to build this on a linux Ubuntu 24.04, with clang 19 and GCC 15. At this moment, Clang 19 is already fully supported on Ubuntu 24.04, just install it with:

sudo apt install clang-19 libc++-19-dev clangd-19 clang-format-19 clang-tidy-19 

For gcc 15, it’s not fully released yet, so this example requires adding some future repo from Ubuntu 25.04… just edit /etc/apt/sources.list and add:

deb http://cz.archive.ubuntu.com/ubuntu plucky main universe

Then, just update apt and install it:

sudo apt update
sudo apt install gcc-15

After that, I recommend removing the repo from sources.list, to prevent upgrading some other unnecessary package. Current gcc-15 version in this tutorial is gcc-15 (Ubuntu 15–20250213–1ubuntu1) 15.0.1 20250213 (experimental) [master r15–7502-g26baa2c09b3], but we expect that in few weeks there will be improvements here, as things are evolving really fast for CXX Modules.

Manual build with GCC 15

To build the example on GCC or Clang, one must also build the std module dependency, named std.cc on GCC and std.cppm on Clang. On GCC, this is done with -fsearch-include-path bits/std.cc:

g++-15 -std=c++23 -O3 -fmodules -fsearch-include-path bits/std.cc main.cpp

With this command, we create a folder with CMI for std, located on file ./gcm.cache/std.gcm (around 29MB) in local directory. So, when rebuilding the example with folder gcm.cache, module std does not need to be rebuilt, thus accelerating the build process:

g++-15 -std=c++23 -O3 -fmodules main.cpp -o example

It automatically looks for gcm.cache folder. If you want to store std.gcm elsewhere, one may create a module.mapper file and indicate the name and location of each module (one per line):

std  gcm.cache/std.gcm

Then, you can indicate the module mapper file when rebuilding:

g++-15 -std=c++23 -O3 -fmodules -fmodule-mapper=module.mapper main.cpp -o example

Note that we consider -O3 in this example. Recently, we filed a bug on GCC-15 since -Ofast was not working (see Bug 119102, which is likely to be fixed soon). Another bug (see Bug 119154) was related to FORTIFY_SOURCE option, so if you plan to disable it after building the module, it’s best to build the std module without it:

g++-15 -U_FORTIFY_SOURCE -std=c++23 -O3 -fmodules -fsearch-include-path bits/std.cc main.cpp -o example

Manual build with Clang 19

Now we show how to build example with Clang. Process is similar, one needs to build CMIs (usually .pcm extension, not .gcm) and then use it (note the usage of libc++ instead of libstdc++ from GCC):

clang++-19 -O3 -std=c++23 -stdlib=libc++  --precompile -o std.pcm /usr/lib/llvm-19/share/libc++/v1/std.cppm 

Then, a local std.pcm file is created (around 30MB), and clang can reuse it as a module file:

clang++-19 -O3 -std=c++23 -stdlib=libc++ -fmodule-file=std=std.pcm -o example std.pcm main.cpp

An extended Hello World with other modules

Now we explore an example that includes a thirdparty module called demo. We have two source files: main2.cpp (implementation that uses modules) and demo.cppm (module interface unit).

// demo.cppm
module;

#include <stdio.h>

export module demo;

import std;

export void func(std::string s) { std::print("s = {}\n", s); }

export template <class X> class A {
public:
void f(X x) { printf("old C...\n"); }
};
// main2.cpp
import demo;
import std;

int main() {
func("hello"); // imported 'func' from 'demo'
A<std::string> a; // imported 'A' from 'demo' and 'string' from 'std'
a.f("x");
return 0;
}

From the example above, one can see that ‘demo’ module is exported from demo.cppm, also exporting templated class A and global function func. We also included some C header in the Global Module Fragment part (between ‘module;’ and ‘export module’). The std::string is used from std module in both files. Note that if we comment “import std;” from main2.cpp, it would complain since it’s not being exported from module demo. An alternative solution could be to do “export import std;” on module demo, so that every module that depends on it, will also import std automatically.

Building this example manually in GCC is also easy:

g++-15 -std=c++23 -O3 -fmodules -fsearch-include-path bits/std.cc main2.cpp demo.cppm -o example2

This generates two CMIs on gcm.cache/ folder: std.gcm (around 29M) and demo.gcm (around 25KB). Eventually, the build may fail in the first round…

$ g++-15 -std=c++23 -O3 -fmodules -fsearch-include-path bits/std.cc main2.cpp demo.cppm -o exTest2
In module imported at ./main2.cpp:1:1:
demo: error: failed to read compiled module: No such file or directory
demo: note: compiled module file is ‘gcm.cache/demo.gcm’
demo: note: imports must be built before being imported
demo: fatal error: returning to the gate for a mechanical issue
compilation terminated.

But a second round will succeed, after all dependencies are already build. For this reason, it is recommended to use some automated build system, like CMake.

Using CMake to build CXX Modules

CMake is the most well known build system for C++, and it currently supports CXX Modules for major compilers. However, it is important to have a recent version of CMake installed… we will consider CMake 4.0 (rc3) in this tutorial.

To install it, just download it from from CMake website or cmake github. On linux, we just uncompress it on ~/.local/cmake user folder, put its binaries on folder ~/.local/bin, and copy share data to ~/.local/share:

cd ~/.local
wget https://github.com/Kitware/CMake/releases/download/v4.0.0-rc3/cmake-4.0.0-rc3-linux-x86_64.tar.gz
tar -zxvf cmake-4.0.0-rc3-linux-x86_64.tar.gz
mv cmake-4.0.0-rc3-linux-x86_64 cmake
cp cmake/bin/cmake bin/
cp -r cmake/share/cmake-4.0/ share/

My preferred way of installing cmake is using Python pip tool, however, its current version is still 3.31 (but soon may become 4.0):

pip install cmake --break-system-packages

Finally, important std.cc headers from GCC 15 seem to be located on /usr/include/c++/15/bits/std.cc but CMake 4.0 expects them on /usr/lib/gcc/x86_64-linux-gnu/include/c++/15/bits/std.cc, so a fix can be easily done with symbolic link (opened Issue 26758 on Kitware for this):

sudo mkdir -p /usr/lib/gcc/x86_64-linux-gnu/include/
sudo ln -s /usr/include/c++/ /usr/lib/gcc/x86_64-linux-gnu/include/c++

All set, just create a CMakeLists.txt file:

cmake_minimum_required(VERSION 4.0.0)
set(CMAKE_CXX_COMPILER /usr/bin/g++-15)
# set(CMAKE_CXX_COMPILER /usr/bin/clang++-19)
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "a9e1cf81-9932-4810-974b-6eccaf14e457")
set(CMAKE_CXX_MODULE_STD 1)

project(example2 VERSION 0.1.0 LANGUAGES CXX)

add_library(demo)

target_sources(demo PUBLIC FILE_SET CXX_MODULES FILES demo.cppm)

add_executable(example2 main2.cpp)

target_link_libraries(example2 PRIVATE demo)

This example is ready for GCC 15, and if Clang 19 is preferred, then just uncomment two lines on top and comment the line from g++-15. We observe that CMAKE_EXPERIMENTAL_CXX_IMPORT_STD key (check for latest experimental CMake keys here) is still necessary on CMake 4.0 and we also require enabling the CMAKE_CXX_MODULE_STD support. Another important point is that .cppm files require the special attributes FILE_SET CXX_MODULES FILES to be recognized as c++ module units.

Finally, it is also important to install Ninja build system. On Ubuntu, just use apt (current version of ninja is 1.11.1):

sudo apt install ninja-build

To build the project with CMake and Ninja, it’s very simple:

rm -rf build
mkdir -p build
cd build && cmake .. -GNinja
ninja

IDE support on VSCode with CMake

If you prefer to use some IDE like Visual Studio Code, then CMake is very helpful together with clangd tool. The CMake is able to generate a compile_commands.json file that currently works fine with CXX Modules (do not forget the line set(CMAKE_EXPORT_COMPILE_COMMANDS ON) on CMakeLists.txt). In this case, you need to enable Clang support instead of GCC.

To make it work on VSCode, just remove de Microsoft C/C++ extension (ID ms-vscode.cpptools) and install the clangd extension (ID llvm-vs-code-extensions.vscode-clangd). You will also need the CMake Tools extension (ID ms-vscode.cmake-tools).

On VSCode, using clangd extension from LLVM instead of default C/C++ from Microsoft

Just create or edit a .vscode/settings.json file in your VSCode project with the following contents:

{
"editor.formatOnSave": true,
"C_Cpp.intelliSenseEngine": "disabled",
"clangd.path": "/usr/bin/clangd-19",
"clangd.arguments": [
"-log=verbose",
"-pretty",
"--background-index",
"--compile-commands-dir=${workspaceFolder}/build/",
],
"cmake.sourceDirectory": "${workspaceFolder}/.",
}

And that’s it! The intellisense will finally work for your CXX Modules on VSCode.

Final words

CXX Modules is a fundamental feature on modern C++ that will certainly allow a better development for future packages and applications. So, it’s time to use it! Thanks to the support from GCC and Clang, together with CMake, it is already possible to start smaller projects with modules, and also begin migrating bigger projects. Other major compilers like MSVC also support modules, but are not covered in this tutorial. Finally, other interesting build systems, like Bazel, seem to partially support modules as well, although I tried and couldn’t succeed to use them at the time of this tutorial. For Bazel, it seems to have advanced a lot recently (see Issue 22553) and some guys (see rburn and PikachuHyA) seem to be able to use them experimentally already. Future versions of this tutorial will certainly cover Bazel and newer advances in modern C++. Special thanks for these answers on Stackoverflow that helped understanding the state-of-the-art of CXX Modules for GCC. Good Luck and have a nice programming!

Igor Machado Coelho is adjunct professor at the Computing Institute of Fluminense Federal University, Niterói, Rio de Janeiro, Brazil.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

No responses yet

Write a response