Вам достаётся большая C++-кодовая база, целиком построенная на #include-заголовках, и нужно перевести её на модули C++20. Команда не может остановить разработку фич, поэтому миграция должна идти маленькими шагами, сохраняя сборку проекта на каждом коммите, а часть сторонних заголовков вообще нельзя переписать. Опишите, как вы выстраиваете порядок миграции, как переведённый и ещё-не-переведённый код сосуществуют во время перехода и какое требование к системе сборки должно быть выполнено в первую очередь.
Мигрируйте инкрементально, снизу вверх: начните с leaf-библиотек без исходящих зависимостей, оборачивайте legacy-заголовки в глобальный фрагмент модуля и двигайтесь вверх по графу. Для непереведённых зависимостей берите header unit; нужна модуль-осведомлённая сборка вроде CMake 3.28+.
- ✗Пытаться сделать единовременный перевод вместо инкрементального, начиная с leaf-узлов
- ✗Забывать, что
#includeиimportмогут сосуществовать во время перехода - ✗Игнорировать, что систему сборки надо обновить до начала любой конверсии
- →Почему порядок «от leaf-узлов» необходим, а не просто удобен?
- →Как держать заголовок и его модульную версию синхронными, избегая ODR-проблем?
План миграции
Миграцию ведут постепенно, снизу вверх по графу зависимостей — никогда «большим взрывом».
Шаг 1 — leaf-зависимости. Начните с библиотек, у которых нет исходящих зависимостей в вашем коде. Их перевод не ломает никого ниже.
// Было: string_utils.h
#pragma once
#include <string>
std::string trim(const std::string& s);
// Стало: string_utils.cppm
export module string_utils;
import <string>; // header unit для ещё-не-модуля
export std::string trim(const std::string& s);
Шаг 2 — legacy-заголовки через глобальный фрагмент. Зависимость, которую пока нельзя трогать, подключают в глобальном фрагменте — её сущности видны внутри модуля, но не утекают потребителям.
module; // глобальный фрагмент
#include "legacy_config.h" // ещё не модуль
export module app_config;
export void load_config(); // legacy_config.h НЕ виден импортёрам
Шаг 3 — двигайтесь вверх. Когда все зависимости узла стали модулями, конвертируйте сам узел. #include и import спокойно сосуществуют в файле во время перехода.
Шаг 4 — система сборки. До любой конверсии обновите сборку: CMake 3.28+ с FILE_SET CXX_MODULES, иначе порядок «BMI раньше потребителя» не обеспечить.