A Module Implementation Partition (Chartreux)

A Partitioned Module (Javanese) showed a module interface partitionJavanese:Balinese has its own exports, and the primary module re-exports them with export import :Balinese;. That’s only one of the two partition flavors described in Partitions and Submodules. This chapter demonstrates the other: a module implementation partition, which has no interface of its own at all and exists purely as internal plumbing for the module that owns it.

Implementation Partition: Chartreux-Detail.cppm

1 module Chartreux:Detail;   // implementation partition -- no `export` before
2                             // `module`, so this unit has no interface of its
3                             // own. It can only ever be imported by other
4                             // units of Chartreux itself.
5 
6 auto purr_prefix() -> char const * {
7    return "*purr* ";
8 }

Line 1 is the entire difference from an interface partition: no export before module. An implementation partition cannot itself contain an export declaration — it’s ill-formed to try — and it can never be imported from outside module Chartreux, exported or not, no matter what the primary module does.

Module Interface Unit: Chartreux.cppm

1 export module Chartreux;
2 
3 export auto meow() -> void; // defined in chartreux.cpp

Note what’s not here: an import :Detail; line. Clang emits a real diagnostic if an implementation partition is imported directly into the primary interface unit —

1 warning: importing an implementation partition unit in a module interface is
2 not recommended. Names from Chartreux:Detail may not be reachable
3 [-Wimport-implementation-partition-unit-in-interface-unit]

— because reachability of names from an implementation partition isn’t guaranteed when pulled straight into the interface unit. The textbook-correct place for that import is the module’s own implementation unit instead.

Module Implementation Unit: chartreux.cpp

 1 module;
 2 
 3 #include <print>
 4 
 5 module Chartreux;   // module implementation unit -- not a partition itself,
 6                      // just the .cpp half of Chartreux's interface/impl split
 7 import :Detail;      // implementation partitions belong here, not in the
 8                       // primary interface unit -- nothing from :Detail is
 9                       // exported either way, so importers of Chartreux never
10                       // see purr_prefix().
11 
12 auto meow() -> void {
13    std::print("{}", purr_prefix());
14    std::println("Le Chartreux dit «Miaou».");
15 }

module Chartreux; (line 5) makes this the module’s implementation unit — the same role aegean.cpp played for Aegean — and import :Detail; (line 6) pulls in the implementation partition cleanly, with no warning, because this unit isn’t the module’s public interface. meow()’s body (lines 12-15) uses purr_prefix() freely; nothing outside Chartreux’s own units ever will.

User Code: main.cpp

 1 import Chartreux;
 2 
 3 // purr_prefix() is unreachable here -- it belongs to Chartreux's
 4 // implementation partition, which was never exported.
 5 
 6 auto main() -> int {
 7    meow();
 8 
 9    return 0;
10 }

Building With CMake

 1 cmake_minimum_required(VERSION 3.30.5)
 2 
 3 project(Chartreux LANGUAGES CXX)
 4 set(module_name chartreux)
 5 
 6 add_library(${module_name})
 7 target_compile_features(${module_name} PUBLIC cxx_std_23)
 8 target_sources(${module_name}
 9                PUBLIC
10                FILE_SET cxx_modules TYPE CXX_MODULES
11                FILES Chartreux.cppm Chartreux-Detail.cppm
12                PRIVATE chartreux.cpp
13 )
14 
15 add_executable(${PROJECT_NAME} main.cpp)
16 target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_23)
17 
18 target_link_libraries(${PROJECT_NAME} ${module_name})

The only structural difference from Aegean’s CMake file is that FILE_SET cxx_modules (line 10) now lists two .cppm files — the primary interface and the implementation partition both belong there, since both produce a BMI. chartreux.cpp (line 12), the plain implementation unit, stays in PRIVATE as before.

Interface Partitions vs. Implementation Partitions

Interface Partition (Balinese) Implementation Partition (Chartreux:Detail)
Declaration export module M:Name; module M:Name; (no export)
May contain export Yes No — ill-formed
Importable from Any unit of module M; re-exportable to the outside via export import :Name; Only other units of module M; never reaches outside importers
Typical use A named, reusable piece of the module’s public surface Internal helper code shared across a module’s own units, with no public surface at all

Both are ways of splitting a module across multiple files without exposing more than intended. Which one to reach for comes down to a single question: does anything outside the module ever need this, even indirectly? If yes, it’s an interface partition. If the answer is a flat no, it’s an implementation partition — the module-native equivalent of a private, unexported helper header used only by a library’s own .cpp files.