C++ Modules
Modules offer a simpler, more efficient, and safer alternative to the traditional system of including files via the preprocessor.
Traditionally, C++ code has been organized in header files .h and implementation files .cpp. But this has frankly always been a hassle inherited from C.
The introduction of modules in C++11 and their formalization in C++20 aims to completely change the way C++ projects are structured and managed.
A module in C++ is a unit of code that consists of a declaration and definition of functions, classes, all in the same file.
Key Features
- Encapsulation: Modules allow you to define what is accessible from the outside (what is exported) and what is private to the module (what is kept internal).
- Compilation Optimization: Modules allow compiling once and reusing that result, reducing compilation times.
- Elimination of Circular Dependencies: Modules allow importing only the necessary components, avoiding unnecessary dependencies.
- Elimination of Redefinition Problems Headers often require the use of
#include
guards or non-standard#pragma
once to avoid multiple inclusions that cause redefinition errors. Modules eliminate the need for these mechanisms, as their design automatically prevents multiple inclusions and redefinitions.
Basic Syntax of Modules
A module in C++ is declared using the keyword module
followed by the module name.
The declaration is generally placed in a separate file with the extension .cppm
(or sometimes .ixx
, although .cppm
is more common and standard in most compilers).
module mymodule; // We declare a module called "mymodule" export void foo() { // We export the function foo std::cout << "Hello from foo!" << std::endl; } export class MyClass { // We export a class MyClass public: void sayHello() { std::cout << "Hello from MyClass!" << std::endl; } };
Functions and classes prefixed with export become accessible from outside the module.
To use a module in another file, we simply use the keyword import
, which replaces the traditional #include
. See
import mymodule; // We import the module "mymodule" int main() { foo(); // We call the exported function from mymodule MyClass obj; obj.sayHello(); // We use the class exported from mymodule }
The following file contains both the declaration and definition of the function sum, and it is exported for other files to use:
export module sum; export int sum(int a, int b) { return a + b; }
To use the module, we simply import it in the main.cpp
file.
import sum; #include <iostream> int main() { int result = sum(3, 4); std::cout << "The sum is: " << result << std::endl; return 0; }
To compile the program with modules, the command would be something like:
g++ -fmodules-ts sum.cppm main.cpp -o program
The option -fmodules-ts
is used in some compilers like GCC to enable C++20 modules, although options may vary by compiler.
Comparison with separate Compilation
The C++ programming language inherited the translation unit model from C, where every source file is individually compiled with no knowledge about other parts of the project. This model has several drawbacks, and C++20 Modules is the Standard's answer for them. Moreover, Modules allows a cleaner encapsulation of concern.