Open In App

Modules in C++ 20

Last Updated : 06 Sep, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

In C++20, the concept of modules was introduced to improve the efficiency of the compilation process and provide better organization and encapsulation of code. Modules allow developers to divide their code into separate components, each with its own interface and implementation, and import them as needed. This article will explain the concept of modules in C++20, provide examples, and demonstrate the approach to working with modules.

Need for Modules

  • Modules reduce the shortcomings associated with header files, thus compilation time is reduced.
  • Module file is compiled once and the results are stored in a binary file which is processed by the compiler much faster than a header file.
  • Macros and non-exported entities declared in a module are invisible outside the module.
  • Modules can be imported in any order without taking care of macro redefinitions.

Module Units

A module unit is a source file meant to contain a module declaration. Different types of module units in C++20 modules are:

  • Module interface unit: This unit exports a module name where the module declaration contains the export keyword.
  • Module implementation unit: This unit is a separate file that is used to implement the modules.
  • Module partition: It is a module where module-partition component is present.

Syntax

Module Declaration

To declare a module, you need to create a module interface unit, which is a separate file with the ‘.ixx’, ‘.cppm’, or ‘.mxx’ extensions. Inside this file, you declare the module using the module keyword followed by the module name.

// module_name.ixx - Module interface unit
module module_name; // declares a module named "module_name"
export module module_name; // declares and exports a module named "module_name"
module A.B; // declares a submodule named "B" within module "A"
export module A.B // declares and exports a submodule named "B" within module "A"

Exporting Declarations

Within a module, we can define declarations (e.g., variables, functions, classes) that we want to export to other modules using the ‘export’ keyword. Exported declarations become part of the module’s interface and can be imported by other modules.

export module module_name; // module declaration
export data_type variable_name; // export declaration
export return_type function_name(); // export declaration

Example

cppm




// math.cppm - Module implementation file
  
// module declaration for math module
  
export module math;
  
// function to add two integers
export int add(int a, int b) { return a + b; }
  
// function to multiply two integers
export int multiply(int a, int b) { return a * b; }


Explanation

In the above example, we have a module named math implemented in the file ‘math.cppm’. The export module math; statement declares the module named “math”. The export keyword indicates that the module is part of the interface and can be imported by other modules.

Importing Modules and Declarations

To use declarations from other modules, we need to import them. The import keyword is used to import a module or a specific declaration from a module.

module module_name;
// imports the entire module "othermodule" into the current module "mymodule"
import othermodule;
// imports the declaration of the function "function_name" from the module "othermodule" into the current module "mymodule"
import othermodule.function_name;

Example

C++




// main.cpp
  
// importing the math module
  
import math;
  
#include <iostream>
int main()
{
    // calling the add function from the math module
    int result = add(3, 5);
  
    // calling the multiply function from the math module
    result = multiply(2, 4);
    return 0;
}


Explanation

In the main.cpp file, we import the math module declared in the previous example using the import keyword. This allows us to access the functions add and multiply defined in the math module. We can use these functions as if they were defined in the current file.

Building and Compiling Modules

To compile and build modules, you’ll need a C++20-compliant compiler. To compile and run the code, you’ll need a C++20-compliant compiler that supports modules. The specific steps depend on the compiler you’re using. Here’s an example using ‘g++’ :

C




g++ -std=c++20 -c math.ixx   // Compile the module interface unit
g++ -std=c++20 -c math_extra.cpp   // Compile a partition (implementation unit)
g++ -std=c++20 main.cpp math.ixx math_extra.o -o main   // Link the modules and build the executable


Output

Addition Result: 8
Multiplication Result:8

These are the basic syntax elements of modules in C++20. With modules, you can better organize and encapsulate your code, improve compilation times, and enhance code reusability and maintainability.

When a header file is included using the #include directive, the preprocessing macros defined within the translation unit can affect the processing of the included header file. However, when using modules, the processing of preprocessing macros defined within the translation unit does not directly affect the processing of included headers within the module. Here, the Global module fragment can be useful.

Global module fragment

A global module fragment can be placed at the beginning of module units. The global module fragment allows us to include header files when they can not be imported directly, particularly when the header file relies on preprocessing macros for configuration.

module;
// Preprocessing directives (optional)
module-declaration;

Example

cppm




// module_a_fragment.cppm (Global module fragment)
module;
  
// Preprocessing directives (optional)
#define MY_MACRO
  
// End of global module fragment
  
module A;
  
import<iostream>;
  
// Example function
export void demo_func() { 
  cout << "Hello from module A!\n";
}


C++




// main.cpp (not a module unit)
import A;
  
int main()
{
    demo_func(); // Call function from module A
    return 0;
}


Output

Hello from module A!

Private module fragment

Primary module interface unit can be suffixed by a private module fragment. Private module fragment represents a module as a single translation unit that restricts the accessibility of its contents to importers.

// Public declarations accessible to importers
module ModuleName;
// Private declarations not accessible to importers
module ModuleName : private;
// End of the private module fragment

Module partitions

A module can be divided in module partition units which starts with a colon :. These partitions are only accessible by the main module.

export module A:B; // Declares a module interface unit for module 'A', partition ':B'.

Conclusion

This article demonstrates how to create and use modules in C++20. The module interface and implementation are separated, allowing for better organization and encapsulation of code. The imported module’s functions can be used in other files, enhancing code reusability and maintainability.

Related Article:



Like Article
Suggest improvement
Share your thoughts in the comments