C++ Modules (Using the GNU Compiler Collection (GCC))

### 3.23 C++ Modules ¶

Modules are a C++20 language feature. As the name suggests, they provide a modular compilation system, intending to provide both faster builds and better library isolation. The “Merging Modules” paper https://wg21.link/p1103, provides the easiest to read set of changes to the standard, although it does not capture later changes.

G++’s modules support is not complete. Other than bugs, the known missing pieces are:

Private Module Fragment

  The Private Module Fragment is recognized, but an error is emitted.

Partition definition visibility rules

  Entities may be defined in implementation partitions, and those definitions are not available outside of the module. This is not implemented, and the definitions are available to extra-module use.

Textual merging of reachable GM entities

  Entities may be multiply defined across different header-units. These must be de-duplicated, and this is implemented across imports, or when an import redefines a textually-defined entity. However the reverse is not implemented—textually redefining an entity that has been defined in an imported header-unit. A redefinition error is emitted.

Modular compilation is not enabled with just the `-std=c++20` option. You must explicitly enable it with the `-fmodules` option. It is independent of the language version selected, although in pre-C++20 versions, it is of course an extension.

No new source file suffixes are required. A few suffixes preferred for module interface units by other compilers (e.g. ‘`.ixx`’, ‘`.cppm`’) are supported, but files with these suffixes are treated the same as any other C++ source file.

Compiling a module interface unit produces an additional output (to the assembly or object file), called a Compiled Module Interface (CMI). This encodes the exported declarations of the module. Importing a module reads in the CMI. The import graph is a Directed Acyclic Graph (DAG). You must build imports before the importer.

Header files may themselves be compiled to header units, which are a transitional ability aiming at faster compilation. The `-fmodule-header` option is used to enable this, and implies the `-fmodules` option. These CMIs are named by the fully resolved underlying header file, and thus may be a complete pathname containing subdirectories. If the header file is found at an absolute pathname, the CMI location is still relative to a CMI root directory.

As header files often have no suffix, you commonly have to specify a `-x` option to tell the compiler the source is a header file. You may use `-x c++-header`, `-x c++-user-header` or `-x c++-system-header`. When used in conjunction with `-fmodules`, these all imply an appropriate `-fmodule-header` option. The latter two variants use the user or system include path to search for the file specified. This allows you to, for instance, compile standard library header files as header units, without needing to know exactly where they are installed. Specifying the language as one of these variants also inhibits output of the object file, as header files have no associated object file.

Alternately, or for a module interface unit in an installed location, you can use `-fsearch-include-path` to specify that the main source file should be found on the include path rather than the current directory.

Header units can be used in much the same way as precompiled headers (see Using Precompiled Headers), but with fewer restrictions: an #include that is translated to a header unit import can appear at any point in the source file, and multiple header units can be used together. In particular, the `-include` strategy works: with the bits/stdc++.h header used for libstdc++ precompiled headers you can

    g++ -fmodules -x c++-system-header -c bits/stdc++.h
    g++ -fmodules -include bits/stdc++.h mycode.C

and any standard library #includes in mycode.C will be skipped, because the import brought in the whole library. This can be a simple way to use modules to speed up compilation without any code changes.

But for the standard library in particular this is unnecessary: if a header unit has been built for the libstdc++ ‘`bits/stdc++.h`’ header, the compiler will translate an ‘`#include`’ of any importable standard library header into an import of that header unit, speeding up compilation without needing to specify ‘`-include`’. Note that the ‘`bits/stdc++.h`’ header unit is also built by the `--compile-std-module` option.

The `-fmodule-only` option disables generation of the associated object file for compiling a module interface. Only the CMI is generated. This option is implied when using the `-fmodule-header` option.

The `-flang-info-include-translate` and `-flang-info-include-translate-not` options notes whether include translation occurs or not. With no argument, the first will note all include translation. The second will note all non-translations of include files not known to intentionally be textual. With an argument, queries about include translation of a header files with that particular trailing pathname are noted. You may repeat this form to cover several different header files. This option may be helpful in determining whether include translation is happening—if it is working correctly, it behaves as if it isn’t there at all.

The `-flang-info-module-cmi` option can be used to determine where the compiler is reading a CMI from. Without the option, the compiler is silent when such a read is successful. This option has an optional argument, which will restrict the notification to just the set of named modules or header units specified.

The `-Winvalid-imported-macros` option causes all imported macros to be resolved at the end of compilation. Without this, imported macros are only resolved when expanded or (re)defined. This option detects conflicting import definitions for all macros.

For details of the `-fmodule-mapper` family of options, see Module Mapper.