C++ Modules in CMake with Visual Studio

Will Buik

We’ve seen a lot of excitement around C++ modules since we announced support in the Microsoft C++ compiler and Visual Studio. Ever since, the community has been asking us when CMake support was coming. I am happy to announce that Visual Studio 2022 17.2 Preview 2 has experimental support for C++ modules in CMake projects. One caveat for this early support, is that you must use the Visual Studio (MSBuild) generator. If you want to try it out, you can download the latest preview of Visual Studio today. We are looking forward to hearing your feedback about how modules work in your CMake projects.

C++ modules are a new feature in C++20. They can help you compartmentalize your code, speed up build times, and they work seamlessly, side-by-side with your existing code. To learn more, I would recommend checking A Tour of C++ Modules in Visual Studio. That post explores the details of how and when to use modules and some of the ways Visual Studio can help you do it.

For an even deeper dive into modules, you can also explore the following series:

Getting Started with Modules and CMake

While CMake support for modules in Visual Studio is still experimental, it is straightforward to start using it. You don’t need to do anything special in your CMake project files. Just make sure you have enabled the C++20 language standard. Starting in this preview, our CMake project template will do that for you, but if you are working with an existing project you will need to add this to your CMakeLists.txt:

set_property(TARGET $TargetName PROPERTY CXX_STANDARD 20)

Note: Enabling C++20 requires CMake 3.12 or higher. Visual Studio ships with the latest version of CMake, but this may be a consideration if you use this project across operating systems or need to use an older version of CMake.

You will also need to make sure you are using the Visual Studio generator with CMake. Support for other generators, such as Ninja, is planned but it isn’t available yet. Ninja is the default generator when working with CMake projects in Visual Studio so you will need to modify your project’s CMakePresets.json or CMakeSettings.json file to change this.

CMake Presets:

If your project is using CMake Presets, in CMakePresets.json, find the “windows-base” configure preset. It will have a “generator” property that is most likely set to “Ninja”. Update it to this:

{
  "version": 3,
  "configurePresets": [ {
    "name": "windows-base",
    "hidden": true,
    "generator": "Visual Studio 17 2022",
    //…
  }]
}

CMake Settings:

If your project is using CMake Settings, you can open the CMakeSettings.json editor and change the Generator setting (under Advanced) to “Visual Studio 17 2022:”

CMake Settings > Advanced > CMake generator

You will need to do this for each of your project’s configurations.

And that’s it, once the Visual Studio generator is selected and the C++20 language standard is enabled for your target you can use modules in your project.

Creating Modules in a CMake Project

To add a module to your project, just right click on any folder and select “Add New Item:”

CMake Context Menu > Add > Add New Item...

And select the “C++ Module Interface Unit (.ixx)” template in in the “Visual C++” category:

New File > Visual C++ > C++ Module Interface Unit (.ixx)

If you are using the Targets View, it is even easier. Just click “Add -> New Module…” in the context menu on any target:

CMake Targets View Context Menu > Add > New Module...

Exporting and Using Modules

Once you have created a Module interface (.ixx) file, you can export functions, classes, and structs. The example below defines a simple module called Printers and exports a single struct:

// Printers.ixx
// 
// The .ixx extension lets the build system know this file contains
// a module interface.

// Begin global module fragment.
module;

// Headers included in the global module fragment (between "module;" and
// "export module Printers;") can be used by the module implementation but
// are not exported. These included headers are invisible to translation
// units that import the Printers module.
#include <iostream>

// Creates the Printers module. This can be imported into other translation
// units with "import Printers;" Any items marked with the export keyword
// will be available in translation units that import the Printers module.
export module Printers;

// This only applies to this module's translation unit. It does not leak
// into translation units that import the Printers module.
using namespace std;

// These constants are not exported, they are invisible from translation
// units that import the Printer module.
const string default_spacer = " ";
const string default_separator = ",\n";

// SimplePrinter is exported and accessible to any code that imports the
// Printers module.
export struct SimplePrinter
{
    string element_spacer;
    string separator;

    void print_element(int e)
    {
        std::cout << e << element_spacer;
    }

    void print_separator()
    {
        std::cout << separator;
    }
};

// Exports the function get_default_printer.
// This is accessible from translation units that import the Printers module.
export SimplePrinter get_default_printer()
{
    return SimplePrinter {
        .element_spacer = default_spacer,
        .seperator = default_seperator
    };
}

Once you define the module, other source files and module interfaces can consume it with the import keyword. It is possible to import any modules declared in the current target or any of its imports. For example, “PrintVector.cpp” below uses the Printers module we defined above:

// PrintVector.cpp

// Conventional #includes and module imports can be freely mixed.
#include <vector>

// Import the Printers module defined in Printers.ixx.
import Printers;

using namespace std;

void print_vector(const vector &list)
{
    // SimplePrinter and get_default_printer are imported from the
    // Printers module.
    SimplePrinter printer = get_default_printer();

    for (auto i : list) {
        printer.print_element(i);
    }
    printer.print_separator();
}

You can do a lot with C++ modules. To learn more, I would recommend reading A Tour of C++ Modules in Visual Studio which goes into much more detail about how to use modules. For instance, it is possible to split module interfaces from their implementations. That post has been updated with some of the latest details about using modules with CMake.

Feedback

If you are interested in trying out C++ modules in your own CMake projects, please download the latest Visual Studio Preview. Try it out and let us know if you have any questions or feedback. If you find any issues or have a suggestion, the best way to reach out to us is to Report a Problem.

Posted in C++

8 comments

Discussion is closed. Login to edit/delete existing comments.

  • James Wil 0

    I was kinda disappointed by C++20 modules

    We still have to be careful of cyclic dependencies and we have to forward declare everything…. in 2022, cmon, if you do something new do it right way..

  • Jeremy Ong 0

    Thanks for the materials. I spent a few hours attempting to explore the usage of modules currently, and the big sticking point for me is the lack of documentation around how modules interact with existing static and dynamic linkage mechanisms. In particular, trying to port a non-trivial renderer to use C++20 modules, I ran into a number of issues w.r.t. symbol visibility across library boundaries. Importing a module from a linked library doesn’t seem to work, so is the expectation that module interface files should be compiled in every dependent library also? It’s also unclear whether macros used to handle dllexport and dllimport directives are still needed or not in the dynamic linkage scenario. Any sample projects or documentation to clarify how a larger project might be structured via C++20 modules would be appreciated!

    • Paulo Pinto 0

      The Visual Studio team keeps doing little demos with CLI applications and only depending on C++ standard library, because they have yet to make the C++ Windows frameworks play well with modules or even mixing them in projects with pre-compiled headers.

      Despite all these issues, I have been using C++ modules for all my hobby coding while trying to deal with issues.

      So if you want to see Win32, or C++/WinRT being used alongside modules, have a look at these projects,

      https://github.com/pjmlp/ppm2png

      https://github.com/pjmlp/RaytracingWeekend-CPP

      https://github.com/pjmlp/AStarDemo

  • fredrik bergquist 0

    Isn’t there a spelling error Seperator?

  • 胡 海啸 0

    Please share a github project for this demo.

  • Marian Witek 0

    Ok, modules work, but how to configure CMake to use standard c++ library using modules? It should do ‘import std.core” instead “#include “.
    Compiling in command line there is the flag: /experimental:module that make it work.
    Some kind of help?

  • Robert Kotch 0

    I’m not a programmer. But my C++ programmer just downloaded the 64-bit compiler for C++ Studio 2022 and he says basic headers are not compiling. What gives. We have a program that compiles fine in 32-bit but is DOA on 64 bit. Documentation is wanting to put it mildly.

    • Michael PriceMicrosoft employee 0

      Hi Robert,

      Have you tried posting a reproducible snippet of code along with the errors to a site like StackOverflow? There are often experts that can quickly diagnose an issue on such sites. If you are not getting traction there, feel free to email us at visualcpp@microsoft.com with a reproducible snippet and the errors you are seeing, and we’ll see if we can resolve it or redirect you to resources to help you resolve it.

Feedback usabilla icon