3.23 C++ Modules

Modules are a C++20 language feature. As the name suggests, they provides 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.

Translation-Unit local referencing rules

Papers p1815 (https://wg21.link/p1815) and p2003 (https://wg21.link/p2003) add limitations on which entities an exported region may reference (for instance, the entities an exported template definition may reference). These are not fully implemented.

Standard Library Header Units

The Standard Library is not provided as importable header units. If you want to import such units, you must explicitly build them first. If you do not do this with care, you may have multiple declarations, which the module machinery must merge—compiler resource usage can be affected by how you partition header files into header units.

Modular compilation is not enabled with just the -std=c++20 option. You must explicitly enable it with the -fmodules-ts 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 or supported. If you wish to use a non-standard suffix (see Options Controlling the Kind of Output), you also need to provide a -x c++ option too.2

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-ts 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-ts, 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.

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.



Some users like to distinguish module interface files with a new suffix, such as naming the source module.cppm, which involves teaching all tools about the new suffix. A different scheme, such as naming module-m.cpp would be less invasive.