Modules in VC++ 2019 16.5

Modules are one of the bigest changes in C++20 but the compilers’ support for them is a work in progress. The Visual C++ compiler has experimental support for modules that can be enabled by using the /experimental:module and /std:c++latest switches. In this post, I will walk through the core of the functionality available in Visual Studio 2019 16.5.

A first example

A typical hello world application in C++ looks like this:

How do we transform this code so that it uses modules? Just replace the #include preprocessor directive with an import directive.

The std.core module provides most of the content of the C++ Standard Library. The library is modularized as follows:

  • std.regex: the content of header <regex>
  • std.filesystem: the content of header <filesystem>
  • std.memory: the content of header <memory>
  • std.threading: the contents of headers <atomic>, <condition_variable>, <future>, <mutex>, <shared_mutex>, <thread>
  • std.core the rest of the C++ Standard Library

To be able to use the modularized version of the Standard Library you must install the component called C++ Modules for v142 build tools shown in the following image:

When importing the Standard Library you should build with the /MD and /EHsc options.

To build the code snippet above, open a Developer Command Prompt from Visual Studio, and execute the following command:

Now, if you run main.exe, you get the expected result:

Writing a module

Instead of just printing a greeting text in main(), we could get that text from a function. In the following example, this function called get_greeting_text() is exported from a module called greetings. This module is defined in an module interface unit called greetings.ixx.

The .ixx extension is required by the VC++ compiler for module interface units.

The main.cpp file needs to change slightly to import the greetings module and invoke the get_greeting_text() function.

Now, you need to build both greetings.ixx and main.cpp. The following commands must be executed:

Let’s add more to the greetings module. In the following snippet, greeter is a class with an overloaded call operator that, when invoked, returns a random greeting text.

In main.cpp we will have the following:

The commands for compiling this code remain the same. However, each time we execute the program now a different text will be printed to the console.

Composing a module from partitions

Modules can be split into partitions. Partitions help to organize the code of a module, especially if the module is large. Partitions are not exported as stand-alone units but as parts of a module interface unit.

To exemplify module partitions, let’s split the code of the greetings modules into two partitions: one that contains the free functions, called greetings-func and one that contains the classes, called greetings-types. These are also available in files with the extension .ixx. Here is how the look:

The content of greetings-func.ixx is:

The content of greetings-types.ixx is:

The syntax for exporting a module partitions is export module <module-name>:<partition-name>. The rest is no different than regular module interface units.

These two partitions are then imported and re-exported from the module interface unit, greetings.ixx as follows:

The syntax for exporting a partition is export import :<partition-name>. Of course, apart from these directives, the module interface unit may contain any other exports.

The content of main.cpp does not change. However, we need to change the commands we use to build the code, as follows:

Building this way is possible because we leveraged a naming scheme supported by the VC++ compiler for module partition units. That is <module-name>-<partition-name>.ixx. If you do not follow this scheme, then you need to use the /module:reference switch to specify the module partition interfaces.

Internal partitions

A partition does not have to be an interface unit. It could contain code that is not supposed to be exported from the module. Such a partition is called an internal partition and must be put in a file with the extension .cpp.

To see how these work, let’s modify the previous example where we used the rand() function in the greeter class. We will remove the details of generating a new integer to another function called next_rand() available in an internal partition called greetings:details. This function is not exported from the greetings module. The content of greetings-details.cpp is shown in the following snippet:

We need to modify the code in the greetings:types partition as follows (notice the import :details directive):

Nothing else needs to change except for the build commands. We have a new file to build, greetings-details.cpp and it requires a new compiler switch, /module:internalPartition to indicate that the file that is compiled is an internal partion of a module.

Now, we can change the implementation details of the next_rand() function without affecting the module interface.

To build the program we only need to run the following commands:

Importing header units

What if the get_greeting_text() was already available in a header file, which perhaps you cannot modularize, maybe because you do not own the code? Modules support the importing of a special translation unit called header unit.

Suppose the header, called greetings.h looks like this:

We can import this using the same import directive, as shown in the below snippet:

To build the program, this time, the build commands must be the following:

There are several compiler switches used here:

  • /module:exportHeader specifies that a header will be exported as a header unit. It requires the path to the header.
  • /Fo that specifies the name of a object file. Without this, the compiler only generates and .ifc file.
  • /module:reference that has an argument of the form <path-to-header>:<path-to-ifc>.

The .ifc file is a binary file generated by the compiler when exporting a module interface. It contains metadata about the module interface and is modeled based on the Internal Program Representation (IPR) for C++, developed by Gabriel Dos Reis and Bjarne Stroustrup. The IFC is the binary module interface (BMI), which is the term found in documentation.

Exporting templates

Templates can also be exported from a module. Let’s look at an example. The following module is available in a file called foo.ixx:

In this snippet, the module foo contains a class template also called foo and function template called make_foo() that creates an instance of foo. Notice that the keyword export is preceding the keyword template. This module can be imported and its exports used in main.cpp as follows:

To build this program you must use the following build commands:

If you run this, it will print 42 and modules to the console.

See also

To learn more about modules in Visual C++ you can read the following:

1 Reply to “Modules in VC++ 2019 16.5”

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.