C++20 Modules: Escape Include Hell and Speed Up Builds with import

C++20 Modules: Escape Include Hell and Speed Up Builds with import

이 글의 핵심

Why modules beat repeated header parsing: module units, export blocks, partitions, build commands, performance tables, and production migration patterns with PIMPL and CI caching.

Introduction: “Including headers makes builds too slow”

One large header pulls in dozens more; every .cpp parses that text again. Modules parse once and reuse BMIs (e.g. .pcm)—major compile-time wins at scale.

Requirements: C++20, GCC 11+, Clang 13+, MSVC 2019 16.10+; CMake 3.28+ has solid FILE_SET ... MODULES support.


What is a module?

A module is a translation unit with an export module (or implementation module name;) that exposes only exported declarations to import consumers—unlike textual #include paste.

Global module fragment

module;
#include <vector>   // only in this preamble
export module mylib;

export int add(int a, int b) { return a + b; }

Header vs module (concept)

#includeimport
UnitText pasteParsed BMI cache
Duplicate workPer TUOnce per module
MacrosLeak everywhereContained

Partitions

Split large modules: export module mylib:part1;, aggregate with export import mylib:part1; in the primary interface.


Build commands (sketch)

  • GCC: -std=c++20 -fmodules-ts, compile interface units first in dependency order.
  • Clang: often -std=c++20 -c file.cppm.
  • MSVC: /std:c++20 and /interface for .ixx.

CMake 3.28+

target_sources(app PRIVATE FILE_SET all MODULES
    math.cppm
    geometry.cppm
    geometry_impl.cpp
)

Common errors

  1. Module not found / link order—build interface .o before importers.
  2. export inside module; preamble—illegal.
  3. Using non-exported symbols from another module.
  4. Circular imports—split interfaces or introduce a third module.
  5. #include before import can pollute macros—prefer import first.
  6. Wrong extension—use .cppm / .ixx for interface units.
  7. Wrong partition compile order.
  8. export in implementation unit—only primary/partition interfaces export.

Performance (illustrative)

Large projects often see ~50–75% compile-time reduction versus repeated header parsing—depends on project shape and compiler.


Production patterns

  • Migrate new code first; keep #include where needed.
  • PIMPL + modules for ABI stability.
  • Wrap import std gaps with thin module shims until the toolchain supports import std.
  • Cache .pcm paths in CI.

  • Module migration #24-2
  • Modules overview
  • VS slow build

Next: Module migration #24-2

Previous: Async coroutines #23-3