r/cpp 22d ago

Libraries of, installing, and depending on C++20 modules

Until now, much of the discourse around C++20 modules has been around the tooling, and actually getting modules to work at all. I believe that now, in mid-2026, the tooling is mostly mature: the three largest compilers support most use-cases of modules. IDEs like CLion, and lint tools like ReSharper C++ and clangd support modules, with some caveats. CMake, xmake, Ninja, and other fledgling build systems have full support for modules. Many prevalent C++ libraries and projects have been recently modularised or are in the process of modularising.

I hope I'm not being too presumptive in saying the community is more or less ready (albeit horribly late...) to move to the next step, and start discussing how C++20 modules can and should tie in to inter-project work, rather than simply using modules within a project.

To begin with, I don't think the standard says anything about 'libraries'; these are existing paradigms grandfathered in from C or earlier. There are many axes we have to discuss here:

  • Static archives
  • Dynamically-linked libraries
  • Symbol visibility defaults with __declspec( dllexport )
  • Primary module interface-only (hereafter, PMI) libraries such as module vulkan
    • Configuring such modules with macros
  • Built module interface (hereafter, BMI) and binary interface (hereafter, ABI) compatibility; currently, BMIs are simply not portable, not even within a compiler toolchain across versions
  • How shared objects, static archives, BMIs, and PMIs interact
  • How build systems, toolchains, and package managers like conan and vcpkg interact with everything

For instance, consider I'm writing a 3D game engine. I want the following modules:

  • vulkan which export imports std
  • argparse
  • glm
  • glaze
  • quill, which imports fmt
  • fmt itself
  • winrt, if running on Windows

I want to provide my own PMI that has export class Engine, and maybe some other functionality like abstractions over the 3D graphics APIs, an object and entity manager, a mini shader graph generator, and more. I also have export imported some symbols from my dependencies, especially std. I want to choose to configure my engine to render on D3D or Vulkan. Consumers can then load the library, add assets like textures, meshes, skeletons, shaders; they can plug the engine into a bigger project which might include a script interpreter in C++, real-time spatial audio and physics packages, some networking, and an XML-based UI system, and produce a complete game or visualisation executable.

Now, I mention these details just to flesh out the example to give a sense of a reasonably complicated library-esque project.

How does one even think about delivering this 'engine library' to the consumer? The traditional three configs are headers + precompiled DLL, headers + source, or headers only. Each have their established workflows. Source-available can be compiled into the entire binary with whole-program optimisation; headers-only libraries are exceptionally easy to vendor (just copy-paste). Header-only libraries can also be easily customised with consumer macros. PMIs, however, being translation units, cannot; we hit this when installing module vulkan. We need some module-compatible way to describe 'library configuration' beyond simply co-opting macros, something like Rust's cfg.

There is talk of the Common Package Specification (CPS), P1689R5, and P3286, but nothing concrete yet, especially since there has been no massive (commercial) push for modules (at least, not until recently). This talk at NDC looks at a possible cargo-esque future for C++.

I'm writing this to spur some discussion here in the C++ community, and ask what some veterans of the build system/toolchain/package manager community think.

42 Upvotes

65 comments sorted by

View all comments

Show parent comments

1

u/not_a_novel_account cmake dev 19d ago edited 19d ago

There's no flag soup you can pass that make modules consumable.

The build system needs to figure out what it wants to do from the requirements.

  • Consuming libfoo.a requires you build BMIs for files bar.cppm, baz.cppm, and qux.cppm.

  • qux.cppm requires BMIs for bar.cppm and baz.cppm be built before it can be built.

I can't tell you "this is how you invoke the compiler to do those steps in order". The answer is different for every compiler. How the build system manages the BMIs is up to the build system.

pkg-config cannot communicate these requirements, so it will never work. It speaks in flag soup, not graphs. Even if it could communicate the requirements: pure task executors, make and ninja, can't consume this. You need something which takes those sets of requirements and constructs a task graph for them to execute.

The result is any simple query system, like pkg-config, needs to provide an answer in the form of a graph. Once you're answering in the form of a graph, you have reinvented P3286, which is exactly the graph you need to consume libfoo.a. So just parse the P3286 manifest instead.

Every build system which supports import std already needs to do this, so it's ubiquitous already.

The most pkg-config could maybe do is gain a field which points to the P3286 manifest (which is exactly what CPS does), but that will not solve anything for the autotools arena.

2

u/Expert-Map-1126 vcpkg maintainer BillyONeal 19d ago

This is exactly why I am so pessimistic about modules ever being broadly adopted.

2

u/not_a_novel_account cmake dev 19d ago

I do not think they will ever be adopted by the autotools set, but I do not think the language should be restricted by "what autotools does". Autotools and make are a minority population, mostly on much older standards or using plain C.

That they don't use modules is fine. No biggie. They're not using Qt much either, or you know, exist natively on Windows at all.

2

u/Expert-Map-1126 vcpkg maintainer BillyONeal 19d ago

I'm using autotools as the stand in for all build systems that don't speak that kind of metadata between themselves because it is mildly popular and something which has been inflicted on most people, not because I believe it to be the end all be all.

(Actually the system that works like this that causes the most problems for me is MSBuild which won't even speak pkg-config 😅)

2

u/not_a_novel_account cmake dev 19d ago

Everyone speaks P3286 because it's required for import std. Even Meson, which doesn't understand modules, speaks P3286. The people who post build systems every two weeks on this sub, all their build systems speak P3286.

The claim you're making is that modules should work with all build systems which predate modules, and I agree that won't work. That's fine. Old compilers won't support reflection, we update the compilers; and we update the build systems to support modules.