April 1, 2018·4 min read·engineeringcppmodulesstandardsmigration

A shared modules plan separates the standard subset from migration work

Dos Reis and Smith propose a common route for modules toward C++20. The plan separates a shared language subset from header-import and macro work that still needs experimentation in the TS.

Two module designs can agree on new code while disagreeing about how existing headers enter the model. Gabriel Dos Reis and Richard Smith's April 1 plan, P0983R0, uses that overlap to propose a route toward C++20. The plan combines a shared subset for the international standard draft with further migration work in the Modules TS.

The authors describe their intentions as best effort. They propose a coordinated operation at Rapperswil, not a completed adoption or a guarantee about the final contents of C++20. The difference matters because some of the semantic choices and build-tool consequences remain explicit work items in the document.

Modules interest me because C++ abstractions often impose their cost during compilation and diagnosis. A component boundary could help, but adopting one in an existing codebase is a separate problem from writing an isolated new module. This plan recognizes both requirements and gives them different routes for further specification.

The overlap has a strict translation-unit boundary

The proposed standard subset consists of programs accepted by both the Modules TS and the alternative the plan calls the Atom proposal. For this subset, a module translation unit starts with its module declaration. Earlier preprocessor directives are permitted only if they generate no tokens.

That restriction is more precise than saying that all existing includes remain available before the declaration. A directive can change preprocessing state without producing C++ tokens. An ordinary header that emits declarations is different. The plan specifically excludes arbitrary header inclusion into the global module from the initial shared subset.

A minimal proposed interface therefore has the module declaration before its C++ declarations:

// Shape proposed for the shared standard subset.
export module geometry;
export struct point {
    double x;
    double y;
};

This illustrates the placement restriction without claiming that the committee has adopted it or prescribing a compiler command. The absence of earlier declarations is part of the proposed model. It lets the module's identity be established before the declarations that belong to it are processed.

Richard Smith's P0947R1, dated March 6, supplies the alternative design's rationale. It requires a module declaration first and imports before other declarations. Smith argues that this order helps tools determine the dependency graph without analyzing the whole source file and establishes ownership before compiler operations that depend on it.

Existing headers need a more permissive route

P0983R0 proposes amending the TS to permit a different structure. The source begins with module;, followed by preprocessor directives, then a named module declaration and its top-level declarations. Its example uses the intervening directives to include platform-specific headers before declaring the named module.

That route preserves a place for declarations that arise from the existing header model. It also retains the TS reachability rule for declarations appearing before the module declaration, where the authors say there is no reordering issue. The plan does not treat that behavior as automatically part of the first standard subset.

The TS work would additionally support header imports, including importing macros from a legacy header. The authors separate this from exporting macros from a named module, which remains its own topic. Those operations involve different promises. Importing an old header through a migration mechanism does not settle whether a newly designed module should expose preprocessor state as part of its interface.

This separation is useful because macros are part of how existing interfaces communicate. Smith's alternative design explicitly aims to support old code, including code that cannot reasonably migrate. A plan that ignored macros entirely would leave a significant part of header interoperability unspecified, even if it gave a clean model for newly authored interfaces. P0947R1's revision history already separates macro export from exported imports, showing that the two operations need distinct rules even within one proposed design and its interface model.

Partitions address organization inside a component

The shared plan also proposes standard-draft support for module partitions, based on the partition work incorporated in the Atom proposal. It acknowledges that details still need work, including adoption and consequences for build tools. A partition is therefore part of the intended common direction, but its complete operational story is not settled by this two-page plan.

P0947R1 explains the purpose. A partition can be imported within its containing module, allowing a module interface to be distributed across translation units. It can also hold internal declarations shared within the module without making them part of the external interface. Code outside the module cannot import the partition directly under that proposal.

The distinction between module and partition consequently describes two levels of dependency. The module is the component consumed by clients. Partitions organize reusable pieces within it. The interface unit can choose which partition exports become visible outside, so splitting source internally does not require exposing every split as a new public component.

I find that distinction important for maintainability. A library's source organization and its public dependency structure need not change together. The proposed partition mechanism gives authors a way to reorganize internal declarations while controlling the interface that consumers actually import.

Semantic agreement still requires more than shared syntax

P0983R0 names reachability and the path of instantiation as areas where the two designs differ. It also identifies dependent argument-dependent lookup as work to resolve with Nathan Sidwell and John Spicer. The authors intend to follow the committee's direction toward merging the alternative design into the TS, but do not present those semantic questions as finished.

That qualification prevents an easy overstatement of the agreement. Matching module declarations and imports can define a useful overlap, yet template instantiation can still depend on which declarations are considered when. A shared grammar cannot by itself settle all the lookup behavior of programs written with that grammar.

The plan's following stages keep header-import semantics, migration recommendations and further experimentation in the TS. Feedback from that work would identify additional pieces suitable for later standard integration. The immediate proposal is therefore a coordinated subset and an experimental route for the unresolved cases, with specific remaining questions about templates and header interoperability.