2 minute read

Old school libraries use splitting into hpp/cpp files. Mayor drawbacks: leaking of macros (hpp can be different at library's consumer side than at library producer side), parsing in each translation unit, ...

Two new alternatives:

  1. Header units: Instead of #include <foo> one writes import <foo>;) which are not yet well supported.
  2. Named modules: A file containing export module myModule;. Well supported by uptodate compilers and in CMake starting at 3.28.

We will look only into Named modules. Simple Example:

// file: myModule.cpp
export module myModule;
export int foo() { return 42; }

// file: main.cpp
import myModule;
int main() { return foo; }

Use from CMake via:

cmake_minimum_required(VERSION 3.28)
add_executable(main)
target_sources(main
  PRIVATE
    main.cpp
  PRIVATE
    FILE_SET CXX_MODULES
    myModule.cpp
  )

Background information: Compiling the myModule.cpp file yields two artifacts: a usual object file (i.e. myModule.o) needed for linking as well a new file: built module interface (BMI). That BMI represents the AST of that module which can readily used when other cpp files (translation units) import myModule. This is the reason why build systems had mayor problems as there might be dependencies between cpp files which was not the case beforehand (hpp/cpp files can be compiled trivially in parallel).

(wip: I will add the following at some point: further resources, examples for more involved module examples, e.g. splitting of module implementation, importing std, etc.)