Modules offer a simpler, more efficient, and safer alternative compared to the traditional file inclusion system using the preprocessor.
Traditionally, code in C++ has been organized into header files .h and implementation files .cpp. But frankly, this has 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.
The concept of modules exists in many languages and has become a programming standard. It represents a huge improvement for C++.
What is a Module?
In simple terms, a module in C++ is a unit of code composed of declarations and definitions of functions, classes, all in the same file.
Key Features
- Encapsulation: Modules allow defining what is accessible from 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.
If you are a regular programmer, by now you should be crying with excitement to see all these advantages (I’m still crying 😭)
Basic Syntax of Modules
Declaring a Module
A module in C++ is declared using the module keyword 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;
}
};
In this example:
module mymodule;declares the modulemymodule.- Functions and classes prefixed with
exportare made accessible from outside the module.
Importing a Module
To use a module in another file, we simply use the import keyword, which replaces the traditional #include.
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
}
In this example,
- The
main.cppfile imports themymodulemodule and uses the entities that have been exported (the functionfooand the classMyClass).
Example With and Without Modules
Let’s see the advantages of modules by making a simple example with the traditional header system and with the new module system.
Benefits of Modules in C++
To a large extent, C++ modules are designed to replace the use of header files in the language.
Modules introduce a more modern, efficient, and secure way to organize and reuse code, solving several problems associated with the traditional header system.
In case I haven’t convinced you yet, let’s look at some of their advantages 👇
Faster Compilation
- In the traditional system, each
.cppfile must include all necessary headers, which generates a large amount of redundant processing. - Modules allow compiling code units once and reusing them, which significantly decreases compilation times.
Better Encapsulation
- Traditional header files expose both the interface (declarations of classes, functions, etc.) and some implementation details to other files that include them.
- Modules clearly define which parts of the code are visible and accessible from other modules or files, which facilitates better control over visibility and prevents accidental dependencies.
Elimination of Redefinition Problems
- Headers often require the use of
#include guardsor#pragma onceto avoid multiple inclusions that cause redefinition errors. - Modules eliminate the need for these mechanisms, as their design automatically prevents multiple inclusion and redefinition. 🎉🎉🎉
Avoidance of Unwanted Macros
- Macros in headers can generate conflicts when included in different files and can unintentionally affect parts of the code.
- With modules, macros do not propagate outside the module in which they are defined.
Improvements in Readability and Maintenance
- Modules provide a more organized system that makes it easier to understand project dependencies, as each module explicitly defines its interfaces and does not rely on the use of preprocessor directives like
#include.
