Modules
C++20 introduced modules, which is one of the biggest language feature changes since the introduction of templates in C++11. Modules aim to replace the traditional header file mechanism to improve compilation time and provide better encapsulation.
Traditional C++ includes can be quite cumbersome, unlike Python, where modules can be directly imported using statements like import numpy as np. Let’s look at the problems caused by include directives.
Problems with Traditional Include
Long Compilation Time
Repeated Parsing: Each time the compiler processes a source file, it re-parses and processes all included header files. These headers may contain a lot of repeated code, leading to increased compilation times.
Complex Dependencies: The nested inclusion relationships of header files can be complex, potentially causing multiple inclusions of the same headers, further increasing compilation time.
Macro Pollution
Naming Conflicts: Macro definitions used in header files can pollute the global namespace, leading to unexpected naming conflicts and hard-to-diagnose errors.
Uncontrolled Dependencies
Dependency Propagation: One header file includes another, causing dependency chains to propagate, making the dependency relationships of compilation units very complex and uncontrollable.
Poor Encapsulation
Leakage of Internal Implementation: Headers often expose implementation details, breaking encapsulation. Any changes to the headers force recompilation of all dependent files.
How Modules Solve These Problems
Reducing Compilation Time
One-time Parsing: Modules need to be parsed and compiled only once, then can be imported multiple times without repeated parsing. This significantly reduces compilation time.
Modular Boundaries: Modules introduce clearer boundaries, reducing unnecessary dependency propagation.
Eliminating Macro Pollution
Namespace Isolation: Modules do not directly use macros, thus avoiding naming conflicts and global namespace pollution issues.
Simplifying Dependency Management
Clear Dependencies: Module import dependencies are clearer, reducing dependency propagation and making dependency management simpler and more controllable.
Improving Encapsulation
Hiding Implementation Details: Modules can better encapsulate implementation details, exporting only necessary interfaces. This means changes to internal implementations do not force recompilation of all dependent files.
Demo of Using Modules in VS2022
Using modules in VS2022 is straightforward. First, select C++20 in the language settings, or the compilation will fail. (Visual Studio 2019 version 16.8 and above, or Visual Studio 2022, these versions support C++20 modules.)
Add a new item:
Select C++ Module Interface Unit (.ixx):
filename:math.ixx We define functions, classes, and structs.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
export module math;
export int add(int a, int b) {
return a + b;
}
export class Math {
public:
int add(int a, int b);
int subtract(int a, int b);
};
export struct Point
{
int x;
int y;
};
filename:math_impl.cpp At the beginning, specify the module to be implemented. Class implementation part.
1
2
3
4
5
6
7
8
9
module math;
int Math::add(int a, int b) {
return a + b;
}
int Math::subtract(int a, int b) {
return a - b;
}
At the beginning of the main function, import the module math.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import math;
#include <iostream>
int main()
{
std::cout << "Hello World!\n";
int result = add(5, 3);
std::cout << "Result: " << result << std::endl;
Math math;
std::cout << "math.add(5,3): " << math.add(5,3) << std::endl;
std::cout << "math.subtract(5,3): " << math.subtract(5,3) << std::endl;
Point p{ .x = 0, .y = 10 };
std::cout << p.x << " " <<p.y<< std::endl;
return 0;
}
The files are as shown below.
Execution result
It’s simple, isn’t it? Happy coding!





