Table of Contents
C++ Modules
Return to C++
Most CPP projects use multiple CPP translation units, and so they need to share CPP declarations and CPP definitions across those units. The usage of CPP header files is prominent for this purpose, an example being the CPP standard library whose declarations are provided by CPP preprocessor include-ing the corresponding header.
Modules are a CPP language feature to share declarations and CPP definitions across translation units.
They are an alternative to some use cases of CPP header files.
Modules are orthogonal to CPP namespaces.
// helloworld.cpp
export module helloworld; // module declaration import <iostream>; // import declaration
export void hello() { // export declaration
std::cout << "Hello world!\n";} }}
// main.cpp
import helloworld; // import declaration
int main() {
hello();} }}
Syntax
@1@ Module declaration. Declares that the current translation unit is a module unit. @2,3@ Export declaration. Export all namespace-scope declarations in
or @4,5,6@ Import declaration. Import a module unit/module partition/header unit. @7@ Starts a global module fragment. @8@ Starts a private module fragment.
Module declarations
Translation units may have a module declaration, in which case they are considered module unit.
The module declaration, if provided, must be the first declaration of the translation unit (excepted the global module fragment, which is covered later on). Each module unit is associated to a module name (and optionally a partition), provided in the module declaration.
The module name consists of one or more identifiers separated by dots (for example:
,
,
…). Dots have no intrinsic meaning, however they are used informally to represent hierarchy.
A named module is the collection of module units with the same module name.
Module units whose declaration has the keyword
are module interface units. Other module units are called module implementation units.
For every named module, there must be exactly one module interface unit with no module partition specified. This module unit is called primary module interface unit. Its exported content will be available when importing the corresponding named module.
Exporting declarations and definitions
Module interface units can export declarations and definitions, which can be imported by other translation units. They can either be prefixed by the CPP export keyword, or be inside an #include should not be used in a module unit (outside the global module fragment), because all included declarations and definitions would be considered part of the module. Instead, headers can also be imported with an import declaration:
Importing a header file will make accessible all its definitions and declarations. Preprocessor macros are also accessible (because import declarations are recognized by the preprocessor). However, contrary to
, preprocessing macros defined in the translation unit will not affect the processing of the header file. This may be inconvenient in some cases (some header files uses preprocessing macros as a form of configuration), in which case the usage of global module fragment is needed.
/////// A.cpp (primary module interface unit of 'A') export module A;
import <iostream>; export import <string_view>;
export void print(std::string_view message) {
std::cout << message << std::endl;}
/////// main.cpp (not a module unit) import A;
int main() {
std::string_view message = "Hello, world!"; print(message);} }}
Global module fragment
Module units can be prefixed by a CPP global module fragment, which can be used to include header files when importing them is not possible (notably when the header file uses preprocessing macros as configuration).
If a module-unit has a global module fragment, then its first declaration must be CPP module;. Then, only preprocessing directives can appear in the global module fragment. Then, a standard module declaration marks the end of the global module fragment and the start of the module content.
/////// A.cpp (primary module interface unit of 'A') module;
// Defining _POSIX_C_SOURCE adds functions to standard headers, // according to the POSIX standard.
- define _POSIX_C_SOURCE 200809L
- include <stdlib.h>
export module A;
import <ctime>;
// Only for demonstration (bad source of randomness). Use C++ <random> instead. export double weak_random() {
std::timespec ts; std::timespec_get(&ts, TIME_UTC); // from}// Provided in according to the POSIX standard. srand48(ts.tv_nsec); // drand48() returns a random number between 0 and 1. return drand48();
/////// main.cpp (not a module unit) import <iostream>; import A;
int main() {
std::cout << "Random value between 0 and 1: " << weak_random() << '\n';} }}
Private module fragment
Primary module interface unit can be suffixed by a CPP private module fragment, which allows a module to be represented as a single translation unit without making all of the contents of the module reachable to importers.
Private module fragment ends the portion of the module interface unit that can affect the behavior of other translation units. If a module unit contains a private module fragment, it will be the only module unit of its module.
export module foo; export int f();
module :private; // ends the portion of the module interface unit that
// can affect the behavior of other translation units // starts a private module fragment
int f() { // definition not reachable from importers of foo
return 42;} }}
Module partitions
A module can have CPP module partition units. They are module units whose module declarations include a module partition, which starts with a colon : and is placed after the CPP module name.
export module A:B; // Declares a module interface unit for module 'A', partition ':B'. }}
A module partition represents exactly one module unit (two module units cannot designate the same module partition). They are visible only from inside the named module (translation units outside the named module cannot import a module partition directly).
A module partition can be imported by module units of the same named module.
/////// A-B.cpp export module A:B; …
/////// A-C.cpp module A:C; …
/////// A.cpp export module A;
import :C; export import :B;
… }}
All definitions and declarations in a module partition are visible by the importing module unit, whether exported or not.
Module partitions can be module interface units (when their module declarations have CPP export. They must be export-imported by the primary module interface unit, and their exported statements will be visible when the module is imported.
/////// A.cpp export module A; // primary module interface unit
export import :B; // Hello() is visible when importing 'A'. import :C; // WorldImpl() is now visible only for 'A.cpp'. // export import :C; // ERROR: Cannot export a module implementation unit.
// World() is visible by any translation unit importing 'A'. export char const* World() {
return WorldImpl();} }}