boxes 2624231 1280

More Details to Modules

 My last post gave you an introduction to modules in C++20. This post shows how to use existing modules.

boxes 2624231 1280

Before I begin this post, let me shortly sum up where we ended in my first post to modules.

A Short Recap

I created a module math1, which consisted of a module interface unit and a module implementation unit, and a client which used it. Here are the three source files.

Module Interface Unit

// math1.cppm

export module math1;

export int add(int fir, int sec);

Module Implementation Unit

// math1.cpp

module math1;

int add(int fir, int sec){
    return fir + sec;
}

Client

// main1.cpp

import math1;

int main(){
   
   add(2000, 20);
   
}

 

I compiled the program with a current clang and cl.exe compiler. From now on, I will stick with the cl.exe compiler because the compile line is slightly shorter. As promised in my last post, let me show you the program’s output.

Using a Standard Module

Essentially, neither the module interface nor the implementation unit changed in module math2.

Module Interface Unit

// math2.cppm

export module math2;

export int add(int fir, int sec);

Module Implementation Unit

// math2.cpp

module math2;

int add(int fir, int sec){
    return fir + sec;
}

Client

// main2.cpp

//#include <iostream>

import std.core;

import math2;

int main(){
	
    std::cout << std::endl;
   
    std::cout << "add(2000, 20): " << add(2000, 20) << std::endl;
   
}

 

 

Rainer D 6 P2 500x500Modernes C++ Mentoring

  • "Fundamentals for C++ Professionals" (open)
  • "Design Patterns and Architectural Patterns with C++" (open)
  • "C++20: Get the Details" (open)
  • "Concurrency with Modern C++" (open)
  • "Generic Programming (Templates) with C++": October 2024
  • "Embedded Programming with Modern C++": October 2024
  • "Clean Code: Best Practices for Modern C++": March 2025
  • Do you want to stay informed: Subscribe.

     

    Thanks to the module std.core, I can show the result of the addition.

    math2

    Using the header <iostream> would also be possible. Of course, I hear your question about which modules are available. Here is what I have from the “Using C++ Modules in Visual Studio 2017” post from the Microsoft C++ team blog.

    C++ Modules in Visual Studio 2017

    • std.regex provides the content of the header <regex>
    • std.filesystem provides the content of the header <experimental/filesystem>
    • std.memory provides the content of the header <memory>
    • std.threading provides the contents of headers <atomic>, <condition_variable>, <future>, <mutex>, <shared_mutex>, <thread>
    • std.core provides everything else in the C++ Standard Library

    Modules provide a higher abstraction than headers. This makes it quite comfortable to use them. Additionally, you can specify which name of a module should be exported or not.

    Export versus Non-Export

    The next module math3 is a little more complicated than the previous one. Here is the interface.

    Module Interface Unit

     

    // math3.cppm
    
    import std.core;
    
    export module math3;
    
    int add(int fir, int sec);
    
    export int mult(int fir, int sec);
    
    export void doTheMath();
    

     

    The module interface unit contains the exporting module declaration: export module math3;. The module declaration starts the so-called module purview. Only names after the module purview declared with export are exported. If not, the name is not visible outside the module and has module linkage. This holds in particular for the function add but not for the mult and doTheMath.

    Module Implementation Unit

    // math3.cpp
    
    module math3;
    
    int add(int fir, int sec){
        return fir + sec;
    }
    
    int mult(int fir, int sec){
        return fir * sec;
    }
    
    void doTheMath(){
    	std::cout << "add(2000, 20): " << add(2000, 20) << std::endl;
    }
    

     

    There is nothing to add to the module implementation unit. The main program is more interesting.

    Client

    // main3.cpp
    
    // #include <iostream>    // (1)
    // #include <numeric>     // (1)
    // #include <string>      // (1)
    // #include <vector>      // (1)
    import std.core;          // (2)
    
    import math3;
    
    int main(){
    	
        std::cout << std::endl;
       
        // std::cout << "add(2000, 20): " << add(2000, 20) << std::endl;   // (3)
    	
        std::vector<int> myVec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    	
        std::string doc = "std::accumulate(myVec.begin(), myVec.end(), mult): "; 
        auto prod = std::accumulate(myVec.begin(), myVec.end(), 1, mult);
    	
        std::cout << doc << prod << std::endl; 
    	
        doTheMath();
    	
    }
    

     

    You see, modules are pretty comfortable in my case. Instead of using the four headers in the line (1),  I’m okay with a simple import std.core in line (2). That was it. Here is the output of the program.

     math3

    Now, to the question: What happens if I add the function add in line (3). To recap, add is not exported and has module linkage. 

    math3Add

    The compiler complains that the function add is used in the main program, but the name add is not visible.

    Further Details

    First, you can export in various ways.

    Export

    Exporting names with export specifiers, such as in math3.cppm is tedious.

    Export Specifier

    // math3.cppm
    
    import std.core;
    
    export module math3;
    
    int add(int fir, int sec);
    
    export int mult(int fir, int sec);
    
    export void doTheMath()
     
    Instead of an export specifier, you can use an exported group.

    Exported Group

    // math3.cppm
    
    import std.core;
    
    export module math3;
    
    int add(int fir, int sec);

    export {

    int
    mult(int fir, int sec); void doTheMath();

    }
     
    The third variation is to use an exported namespace.

    Exported Namespace

    // math3.cppm
    
    import std.core;
    
    export module math3;

    namespace math3 {

    int add(int fir, int sec);

    }

    export namespace math3 {

    int
    mult(int fir, int sec); void doTheMath();

    }
     
    All three variations are semantically equivalent.
     

    It may also be quite comfortable to re-export a module

    Re-Export a Module

    Sometimes, you want to export something which you imported from another module. If you don’t export the imported module, the imported module has, consequently, module linkage, and its names are not visible outside the module. Here is a concrete example.

    Visible versus Invisible

    Imagine, I want to import and use the module math.core and math.core2 in a new module math. Here is the module interface unit of math.core and math.core2.

    • Re-exported modules

     

    // module interface unit of math.core
    
    export math.core
    
    export int mult(int fir, int sec); 
    

     

    // module interface unit of math.core2
    
    export math.core2
    
    export int add(int fir, int sec); 
    

     

    Next, here is the new module math.

    • The new module math

     

    // module interface unit of math
    
    export module math;
    
    import math.core;           // not exported with mult
    export import math.core2;   // exported with add
    
    
    // module implementation unit of math
    
    mult(1100, 2);             // fine
    add(2000, 20);             // fine
    

     

    As you can see, it’s fine to use the exported and non-exported names in the module math. But the module math.core is not exported. Only a client using the module math will see the difference.

    • Client
    // Client
    
    import math
    
    mult(1100, 2);             // ERROR
    add(2000, 20);             // fine
    

     

    The function mult has module linkage and is not visible outside the module. Only the function add is visible.

    Repackage Modules

    There is a comfortable way to repackage modules. Just put them in an exported group.

    export module math;
    
    export{
    
        import math.core;
        import math.core2;
        import math.basics;
    	
    }
    

     

    This makes all names visible for a client which imports the module math.

    What’s next?

    With my next post, I begin the last main topic of the C++ core guidelines: rules to the standard library. Believe it or not, many professional C++ developers don’t use the standard template library (STL). This holds, in particular, true for the algorithms of the STL.

     

     

     

    Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Stephen Kelley, Kyle Dean, Tusar Palauri, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, Philipp Lenk, Charles-Jianye Chen, Keith Jeffery, Matt Godbolt, and Honey Sukesan.

    Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.

    My special thanks to Embarcadero
    My special thanks to PVS-Studio
    My special thanks to Tipi.build 
    My special thanks to Take Up Code
    My special thanks to SHAVEDYAKS

    Modernes C++ GmbH

    Modernes C++ Mentoring (English)

    Do you want to stay informed about my mentoring programs? Subscribe Here

    Rainer Grimm
    Yalovastraße 20
    72108 Rottenburg

    Mobil: +49 176 5506 5086
    Mail: schulung@ModernesCpp.de
    Mentoring: www.ModernesCpp.org

    Modernes C++ Mentoring,

     

     

    0 replies

    Leave a Reply

    Want to join the discussion?
    Feel free to contribute!

    Leave a Reply

    Your email address will not be published. Required fields are marked *