A Module Implementation Partition (Chartreux)
A Partitioned Module (Javanese) showed a module interface partition — Javanese: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.