3.24 Модули C++
Модули — это возможность языка C++20. Как следует из названия, они предоставляют модульную систему компиляции, призванную ускорить сборку и обеспечить лучшую изоляцию библиотек. В статье «Объединение модулей» https://wg21.link/p1103 представлен наиболее простой для чтения набор изменений стандарта, хотя в нём не отражены более поздние изменения.
Поддержка модулей в G++ реализована не полностью. Помимо ошибок, известны следующие недостающие возможности:
- Приватный фрагмент модуля
-
Приватный фрагмент модуля распознаётся, но выдаётся ошибка.
- Правила видимости определений разделов
-
Сущности могут быть определены в разделах реализации, и эти определения недоступны за пределами модуля. Это не реализовано, поэтому определения доступны для использования вне модуля.
- Текстовое объединение достижимых сущностей GM
-
Сущности могут быть определены несколько раз в разных модулях заголовков. Такие определения необходимо дедуплицировать; это реализовано для импортов, а также в случаях, когда импорт повторно определяет сущность, заданную текстуально. Однако обратный случай не реализован: текстуальное повторное определение сущности, определённой в импортированном модуле заголовка. В этом случае выдаётся ошибка повторного определения.
- Правила ссылок на сущности, локальные для единицы трансляции
-
В статьях p1815 (https://wg21.link/p1815) и p2003 (https://wg21.link/p2003) введены ограничения на сущности, на которые может ссылаться экспортируемая область (например, сущности, на которые может ссылаться определение экспортируемого шаблона). Они реализованы не полностью.
- Модули заголовков стандартной библиотеки
-
Стандартная библиотека не предоставляется в виде импортируемых модулей заголовков. Если вы хотите импортировать такие модули, сначала необходимо явно их собрать. Если сделать это неосторожно, могут возникнуть повторные объявления, которые механизм модулей должен будет объединить. Использование ресурсов компилятора может зависеть от того, как вы разбиваете файлы заголовков на модули заголовков.
Модульная компиляция не включается одним лишь параметром -std=c++20. Её необходимо явно включить параметром -fmodules-ts. Она не зависит от выбранной версии языка, хотя, разумеется, в версиях до C++20 является расширением.
Новые суффиксы исходных файлов не требуются. Поддерживаются некоторые суффиксы, предпочитаемые другими компиляторами для единиц интерфейса модуля (например, «.ixx» и «.cppm»), но файлы с такими суффиксами обрабатываются так же, как любые другие исходные файлы C++.
Компиляция единицы интерфейса модуля создаёт дополнительный выходной файл (помимо ассемблерного или объектного файла), называемый скомпилированным интерфейсом модуля (CMI). В нём кодируются экспортируемые объявления модуля. При импорте модуля считывается CMI. Граф импортов является ориентированным ациклическим графом (DAG). Импортируемые модули необходимо собирать до модуля, который их импортирует.
Файлы заголовков также могут компилироваться в модули заголовков — промежуточный механизм, предназначенный для ускорения компиляции. Для его включения используется параметр -fmodule-header, который подразумевает параметр -fmodules. Имена таких CMI определяются по полному пути к соответствующему файлу заголовка, поэтому они могут содержать полный путь с подкаталогами. Если файл заголовка найден по абсолютному пути, расположение CMI всё равно определяется относительно корневого каталога CMI.
Поскольку у файлов заголовков часто нет суффикса, обычно необходимо указать параметр -x, чтобы сообщить компилятору, что исходный файл является файлом заголовка. Можно использовать -x c++-header, -x c++-user-header или -x c++-system-header. При использовании вместе с -fmodules каждый из этих параметров подразумевает соответствующий параметр -fmodule-header. Два последних варианта используют для поиска указанного файла путь включения пользовательских или системных заголовков. Это позволяет, например, компилировать файлы заголовков стандартной библиотеки в виде модулей заголовков, не выясняя, где именно они установлены. Указание языка одним из этих вариантов также отключает вывод объектного файла, поскольку с файлами заголовков не связан объектный файл.
В качестве альтернативы, а также для единицы интерфейса модуля в установленном расположении, можно использовать параметр -fsearch-include-path, указывающий, что главный исходный файл нужно искать в пути включения, а не в текущем каталоге.
Модули заголовков можно использовать почти так же, как предварительно скомпилированные заголовки (см. раздел Использование предварительно скомпилированных заголовков), но с меньшим количеством ограничений: директива #include, преобразуемая в импорт модуля заголовка, может находиться в любой точке исходного файла, а несколько модулей заголовков можно использовать одновременно. В частности, работает подход с параметром -include: для предварительно скомпилированных заголовков libstdc++ используется заголовок bits/stdc++.h, поэтому можно
g++ -fmodules -x c++-system-header -c bits/stdc++.h g++ -fmodules -include bits/stdc++.h mycode.C
и все директивы #include стандартной библиотеки в mycode.C будут пропущены, поскольку импорт подключил всю библиотеку. Это простой способ ускорить компиляцию с помощью модулей без изменения кода.
Параметр -fmodule-only отключает создание соответствующего объектного файла при компиляции единицы интерфейса модуля. Создаётся только CMI. Этот параметр подразумевается при использовании параметра -fmodule-header.
Параметры -flang-info-include-translate и -flang-info-include-translate-not сообщают, выполняется ли преобразование директив include. Если первый параметр указан без аргумента, он сообщает обо всех преобразованиях include. Второй сообщает обо всех случаях, когда файлы include не преобразуются, за исключением тех, которые заведомо должны обрабатываться текстуально. Если указан аргумент, сообщается о запросах на преобразование include для файлов заголовков с соответствующим конечным фрагментом пути. Эту форму можно повторять, чтобы указать несколько файлов заголовков. Параметр может помочь определить, выполняется ли преобразование include: при правильной работе он ведёт себя так, будто его вообще нет.
Параметр -flang-info-module-cmi позволяет определить, откуда компилятор считывает CMI. Без этого параметра компилятор не сообщает об успешном чтении. У параметра есть необязательный аргумент, ограничивающий уведомления указанным набором именованных модулей или модулей заголовков.
Параметр -Winvalid-imported-macros приводит к разрешению всех импортированных макросов в конце компиляции. Без этого параметра импортированные макросы разрешаются только при раскрытии или (повторном) определении. Этот параметр обнаруживает конфликты определений всех импортированных макросов.
Подробную информацию о группе параметров -fmodule-mapper см. в разделе Сопоставитель модулей.
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-15.3.0/gcc/C_002b_002b-Modules.html