Поделиться через


Пошаговое руководство. Импорт библиотек STL в качестве блоков заголовков

В этом пошаговом руководстве показано, как импортировать библиотеки стандартной библиотеки шаблонов C++ (STL) в качестве единиц заголовков в Visual Studio. Более быстрый и надежный способ импорта стандартной библиотеки см. в руководстве по импорту стандартной библиотеки C++ с помощью модулей.

Импорт заголовка STL в виде единицы заголовка проще, чем использование предварительно скомпилированных файлов заголовков. Единицы заголовков проще настроить и использовать, значительно меньше на диске, обеспечивают аналогичные преимущества производительности и являются более гибкими, чем общий PCH.

Дополнительные сведения о единицах заголовков и преимуществах, которые они предоставляют, см. в разделе "Что такое единица заголовка?". Чтобы контрастировать блоки заголовков с другими способами импорта стандартной библиотеки, см. раздел "Сравнение единиц заголовков", модулей и предварительно скомпилированных заголовков.

Необходимые компоненты

Чтобы использовать блоки заголовков, используйте Visual Studio 2022 или более поздней версии или Visual Studio 2019 версии 16.11 или более поздней. Параметр /std:c++20 (или более поздней версии) требуется для использования единиц заголовка.

Два подхода к импорту заголовков STL в виде единиц заголовков

Перед импортом заголовка STL его необходимо скомпилировать в блок заголовка. Блок заголовка — это двоичное представление файла заголовка. Он имеет .ifc расширение.

Рекомендуется создать статическую библиотеку, содержащую встроенные блоки заголовков для заголовков STL, которые вы хотите использовать. Затем наведите ссылку на библиотеку и import его блоки заголовков. Такой подход может привести к более быстрым сборкам и более эффективному использованию. Сведения об этом подходе см. в статье "Подход 1. Создание статической библиотеки единиц заголовка библиотеки STL".

Другой подход заключается в том, чтобы Visual Studio сканировал заголовки STL, которые вы #include в проекте, компилировали их в единицы заголовков, а import не #include эти заголовки. Этот подход полезен, если у вас есть большая база кода, так как вам не нужно изменять исходный код. Этот подход менее гибкий, чем подход статической библиотеки, так как он не позволяет повторно использовать встроенные блоки заголовков в других проектах. Но вы по-прежнему получаете преимущество производительности импорта отдельных библиотек STL в качестве единиц заголовка. Сведения об этом подходе см. в разделе "Подход 2. Сканирование включает в себя импорт заголовков STL".

Подход 1. Создание статической библиотеки единиц заголовка библиотеки STL

Рекомендуемый способ использовать библиотеки STL в качестве единиц заголовка — создать один или несколько проектов статической библиотеки. Эти проекты должны состоять из единиц заголовков библиотеки STL, которые вы хотите использовать. Затем наведите ссылку на проекты библиотеки, чтобы использовать эти единицы заголовков STL. Это похоже на использование общих предварительно скомпилированных заголовков, но проще.

Блоки заголовков (и модули), встроенные в проект статической библиотеки, автоматически доступны для ссылки на проекты, так как система проектов автоматически добавляет соответствующий /headerUnit параметр командной строки компилятору, чтобы ссылки на проекты могли импортировать блоки заголовков.

Этот подход гарантирует, что блок заголовка для определенного заголовка создается только один раз. Он позволяет импортировать некоторые или все блоки заголовков, которые невозможно использовать с PCH. Единицы заголовков можно включить в любой порядок.

В следующем примере создается проект статической библиотеки, состоящий из единиц заголовка <iostream> и <vector> блоков заголовка. После сборки решения вы сошлетесь на этот проект общего блока заголовка из другого проекта C++. import <iostream>; Везде или import <vector>; где бы ни было найдено, встроенная единица заголовка для этой библиотеки используется вместо преобразования заголовка с препроцессором. Это повышает производительность сборки, например PCH-файлы, если один и тот же заголовок включен в несколько файлов. Заголовок не должен многократно обрабатываться файлами, содержащими его. Вместо этого уже обработанный скомпилированный блок заголовка импортируется.

Чтобы создать статическую библиотеку, содержащую библиотеки <iostream> STL, выполните <vector>следующие действия.

  1. Создайте пустой проект C++. Назовите его SharedPrj.
    Выберите пустой проект для C++ из типов проектов, доступных в окне создания проекта : Screenshot that shows creating a new empty C++ project.

  2. Добавьте новый файл C++ в проект. Измените содержимое файла на:

    import <iostream>;
    import <vector>;
    

Настройка свойств проекта

Задайте свойства проекта для совместного использования блоков заголовка из этого проекта:

  1. В главном меню Visual Studio выберите "Свойства Project>SharedPrj", чтобы открыть диалоговое окно страниц свойств проекта:Screenshot that shows settings for Configuration Type and C++ Language Standard.
  2. Выберите все конфигурации в раскрывающемся списке "Конфигурация" и выберите "Все платформы" в раскрывающемся списке "Платформа". Эти параметры гарантируют, применяются ли изменения для отладки или выпуска.
  3. В левой области диалогового окна "Страницы свойств проекта" выберите "Общие свойства>конфигурации".
  4. Выберите для свойства Тип конфигурации значение Статическая библиотека (.lib).
  5. Измените стандарт языка C++ на ISO C++20 Standard (/std:c++20) (или более поздней версии).
  6. В левой области диалогового окна "Страницы свойств проекта" выберите "Свойства>конфигурации" C/C++>General.
  7. В раскрывающемся списке "Источники сканирования для зависимостей модуля" выберите "Да". (Этот параметр приводит к тому, что компилятор сканирует код для зависимостей, которые могут быть встроены в блоки заголовков): Screenshot that shows the scan module dependencies property setting.
  8. Нажмите кнопку "ОК ", чтобы закрыть диалоговое окно "Страницы свойств проекта". Скомпилируйте решение, выбрав Сборка>Собрать решение в главном меню.

Ссылка на библиотеку единиц заголовка

Чтобы импортировать <iostream> и <vector> как блоки заголовков из статической библиотеки, создайте проект, ссылающийся на статическую библиотеку следующим образом:

  1. Не закрывая текущее решение, в меню Visual Studio выберите Файл>Добавить>Новый проект.

  2. В мастере создания проекта выберите шаблон консольного приложения C++ и нажмите кнопку "Далее".

  3. Присвойте новому проекту пошаговое руководство. Измените раскрывающийся список решения , чтобы добавить в решение. Выберите "Создать ", чтобы создать проект и добавить его в решение.

  4. Измените содержимое исходного файла Walkthrough.cpp следующим образом:

    import <iostream>;
    import <vector>;
    
    int main()
    {
        std::vector<int> numbers = {0, 1, 2};
        std::cout << numbers[1];
    }
    

Для единиц заголовка требуется /std:c++20 параметр (или более поздняя версия). Задайте стандарт языка, выполнив следующие действия.

  1. В Обозреватель решений щелкните правой кнопкой мыши проект Пошагового руководства и выберите "Свойства", чтобы открыть диалоговое окно страниц свойств проекта:Screenshot that shows setting the language standard to the preview version.
  2. В левой области диалогового окна "Страницы свойств проекта" в пошаговом руководстве выберите "Общие свойства>конфигурации".
  3. В раскрывающемся списке "Стандартный язык C++" выберите СТАНДАРТ ISO C++20 (/std:c++20) (или более поздней версии).
  4. Нажмите кнопку "ОК ", чтобы закрыть диалоговое окно "Страницы свойств проекта".

В проекте Пошагового руководства добавьте ссылку на проект SharedPrj, выполнив следующие действия:

  1. В проекте Walkthrough выберите узел Ссылки, затем нажмите Добавить ссылку. Выберите SharedPrj в списке проектов: Screenshot that shows the Add Reference dialog. It's used to add a reference to the Walkthrough project.Добавление этой ссылки приводит к тому, что система сборки будет использовать блоки заголовков, созданные SharedPrj всякий раз, когда import проект Пошагового руководства соответствует одному из встроенных единиц заголовков в SharedPrj.
  2. Нажмите кнопку "ОК", чтобы закрыть диалоговое окно "Добавить ссылку".
  3. Щелкните правой кнопкой мыши проект Walkthrough и выберите пункт Назначить запускаемым проектом.
  4. Постройте решение. (Используйте Создание решения сборки>в главном меню.) Запустите его, чтобы увидеть, что он создает ожидаемые выходные данные:1

Преимуществом этого подхода является то, что вы можете ссылаться на проект статической библиотеки из любого проекта, чтобы использовать в нем блоки заголовков. В этом примере статическая библиотека содержит <vector> единицы и <iostream> единицы заголовка.

Вы можете создать единый проект статической библиотеки, содержащий все часто используемые заголовки STL, импортируемые из различных ваших проектов. Либо можно создавать небольшие проекты общих библиотек, где будут находиться различные группы библиотек STL, импортируемые как блоки заголовков. Затем можно сослаться на эти проекты общего блока заголовка по мере необходимости.

В результате должна увеличиться пропускная способность сборки, поскольку импорт блока заголовка значительно сокращает работу, которую должен выполнить компилятор.

При использовании этого подхода с собственными проектами создайте проект статической библиотеки с параметрами компилятора, совместимыми с проектом, который ссылается на него. Например, проекты STL следует создавать с параметром компилятора /EHsc, чтобы включить обработку исключений, поэтому должны быть проекты, ссылающиеся на проект статической библиотеки.

Используйте /translateInclude.

Параметр /translateInclude компилятора (доступный в диалоговом окне "Страницы свойств проекта" в разделе C/C++>General>Translate Includes to Import) упрощает использование библиотеки единиц заголовка в старых проектах, которые #include библиотеки STL. Это делает ненужным изменить #include директивы import в проекте, но по-прежнему дает преимущество импорта единиц заголовков вместо их включения.

Например, если у вас есть в проекте и вы ссылаетесь #include <vector> на статическую библиотеку, содержащую блок заголовка для <vector>, вам не нужно вручную изменять #include <vector>import <vector>; исходный код. Вместо этого компилятор автоматически обрабатывает #include <vector> как import <vector>;. Дополнительные сведения об этом подходе см. в разделе "Подход 2. Сканирование включает в себя импорт заголовков STL". Не все файлы заголовков STL можно скомпилировать в единицу заголовка. Поставляемый header-units.json с Visual Studio список файлов заголовков STL можно скомпилировать в единицы заголовков. Заголовок, который использует макросы для указания его поведения, часто не может быть скомпилирован в единицу заголовка.

Оператор #include , который не относится к единице заголовка, рассматривается как обычный #include.

Использование одних блоков заголовков в разных проектах

Блоки заголовков, созданные проектом статической библиотеки, автоматически доступны для всех проектов, которые напрямую или косвенно ссылаются на него. Существуют параметры проекта, позволяющие выбрать, какие блоки заголовков должны быть автоматически доступны для всех ссылающихся проектов. Они доступны среди параметров проекта в разделе Каталоги VC++.

  1. В Обозреватель решений щелкните проект правой кнопкой мыши и выберите пункт "Свойства", чтобы открыть диалоговое окно "Страницы свойств проекта".
  2. В левой области диалогового окна выберите каталоги VC++ свойств>конфигурации:Screenshot that shows public project content properties, like Public Include Directories and All Header Files are Public.

Следующие свойства управляют видимостью единиц заголовков в системе сборки:

  • Общедоступные каталоги включения указывают каталоги проектов для единиц заголовков, которые должны автоматически добавляться в путь включения в проекты ссылок.
  • Общедоступные каталоги модулей C++ указывают, какие каталоги проектов содержат блоки заголовков, которые должны быть доступны для ссылки на проекты. Это свойство позволяет сделать некоторые блоки заголовков общедоступными. Это видно для других проектов, поэтому поместите блоки заголовков, которые вы хотите поделиться здесь. Если этот параметр используется для удобства, укажите общедоступные каталоги включения, чтобы автоматически добавлять общедоступные заголовки в путь "Включить" в проекты ссылок.
  • Все модули являются общедоступными: при использовании блоков заголовков, созданных в рамках проекта DLL, символы должны экспортироваться из библиотеки DLL. Чтобы автоматически экспортировать символы модуля, задайте для этого свойства значение "Да".

Использование предварительно созданного файла модуля

Как правило, самый простой способ повторного использования единиц заголовков среди решений — ссылаться на проект единицы общего заголовка из каждого решения.

Если необходимо использовать встроенный блок заголовка, для которого у вас нет проекта, можно указать, где находится созданный .ifc файл, чтобы импортировать его в решение. Для доступа к этому параметру:

  1. В главном меню выберите "Свойства проекта>", чтобы открыть диалоговое окно "Страницы свойств проекта".
  2. В левой области диалогового окна выберите свойства>конфигурации C/C++>General.
  3. В дополнительных зависимостях модуля добавьте модули для ссылки, разделенные точкой с запятой. Ниже приведен пример формата, используемого для дополнительных зависимостей модулей: ModuleName1=Path\To\ModuleName1.ifc; ModuleName2=Path\To\ModuleName2.ifcScreenshot showing project Property Pages properties under Configuration Properties, C/C++, General, with Additional Module Dependencies selected.

Выбор из нескольких копий блока заголовка

Если вы ссылаетесь на проекты, создающие несколько единиц заголовков, либо с одинаковым именем, либо для одного файла заголовка, необходимо указать, какой из них следует использовать. Возможно, у вас есть разные версии блока заголовков, созданные с разными параметрами компилятора, например, и необходимо указать версию, соответствующую параметрам проекта.

Используйте свойство дополнительных зависимостей блока заголовка проекта, чтобы устранить столкновения, указав единицу заголовка, которую следует использовать. В противном случае невозможно предсказать, какой из них выбран.

Чтобы задать свойство Дополнительных зависимостей единиц заголовка , выполните следующие действия.

  1. В главном меню выберите "Свойства проекта>", чтобы открыть диалоговое окно "Страницы свойств проекта".
  2. В левой области диалогового окна выберите свойства>конфигурации C/C++>General.
  3. Укажите, какие модули или файлы единиц заголовка следует использовать в дополнительных зависимостях блока заголовков для разрешения конфликтов. Используйте этот формат для дополнительных зависимостей единиц заголовка: Path\To\Header1.h= Path\To\HeaderUnit1.ifc;Path\To\Header2.h= Path\To\ HeaderUnit2.ifcScreenshot that shows the Additional Header Unit Dependencies setting in the project Property Pages dialog.

Важно!

Убедитесь, что проекты с общими блоками заголовков созданы с совместимыми параметрами компиляции. Если реализовать блок заголовка с параметрами компиляции, отличными от указанных при его создании, компилятор выдаст предупреждения.

Примечание.

Чтобы использовать блоки заголовков, скомпилированные как часть проекта DLL, задайте параметру Все модули являются общедоступными значение Да.

Подход 2. Сканирование включает в себя импорт заголовков STL

Еще одним способом импорта библиотек STL является проверка заголовков STL в проекте Visual Studio и их компиляция в единицы заголовков #include . Затем компилятор импортирует, а не включает эти заголовки.

Этот параметр удобно, если проект включает в себя множество файлов заголовков STL во многих файлах или если пропускная способность сборки не является критической. Этот параметр не гарантирует, что блок заголовка для определенного файла заголовка создается только один раз. Однако это полезно, если у вас есть большая база кода: вам не нужно изменять исходный код, чтобы воспользоваться преимуществами единиц заголовков для многих используемых библиотек STL.

Этот подход менее гибкий, чем подход статической библиотеки, так как он не дает возможности повторно использовать встроенные блоки заголовков в других проектах. Этот подход может не подходить для более крупных проектов: это не гарантирует оптимальное время сборки, так как все источники должны быть проверены для #include инструкций.

Не все файлы заголовков можно автоматически преобразовать в единицы заголовков. Например, заголовки, зависящие от условной компиляции с помощью макросов, не должны быть преобразованы в единицы заголовков. Существует список разрешений в виде header-units.jsonфайла для заголовков STL, которые компилятор использует при /translateInclude указании. Он определяет, какие заголовки STL можно скомпилировать в единицы заголовков. Файл header-units.json находится в каталоге установки для Visual Studio. Например, %ProgramFiles%\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.30.30705\include\header-units.json. Если файл заголовка STL не находится в списке, он рассматривается как обычный #include вместо импорта его в виде единицы заголовка. Еще одним преимуществом header-units.json файла является предотвращение дублирования символов в встроенных блоках заголовков. То есть, если компиляция единицы заголовка несколько раз приводит к другому заголовку библиотеки, символы не будут повторяться.

Чтобы попробовать этот подход, создайте проект, включающий две библиотеки STL. Затем измените свойства проекта таким образом, чтобы он импортирует библиотеки в виде единиц заголовков вместо их включения, как описано в следующем разделе.

Создание проекта консольного приложения С++

Выполните следующие действия, чтобы создать проект, включающий две библиотеки STL: <iostream> и <vector>.

  1. В Visual Studio создайте проект консольного приложения C++.

  2. Замените содержимое исходного файла следующим образом:

    #include <iostream>;
    #include <vector>;
    
    int main()
    {
        std::vector<int> numbers = {0, 1, 2};
        std::cout << numbers[1];
    }
    

Задание параметров проекта и запуск проекта

Следующие шаги задают параметр, который приводит к проверке включенных заголовков компилятором для преобразования в единицы заголовков. Они также задают параметр, который приводит к тому, что компилятор будет рассматриваться #include так, как если бы вы писали import для файлов заголовков, которые можно рассматривать как единицы заголовков.

  1. В главном меню выберите "Свойства проекта>", чтобы открыть диалоговое окно "Страницы свойств проекта".
  2. Выберите все конфигурации в раскрывающемся списке "Конфигурация" и выберите "Все платформы" в раскрывающемся списке "Платформа". Эти параметры гарантируют, что изменения применяются для отладки или выпуска и других конфигураций.
  3. В левой области диалогового окна выберите свойства>конфигурации C/C++>General.
  4. Установите для параметра Проверка источников на наличие зависимостей модуля значение Да. Этот параметр гарантирует, что все совместимые файлы заголовков компилируются в единицы заголовков.
  5. Задайте для преобразования импортазначение "Да". Этот параметр компилирует файлы заголовков STL, перечисленные в файле в header-unit.json виде единиц заголовков, а затем импортирует их вместо использования препроцессора в #include них. Screenshot that shows the scan module dependencies property setting in the project Property Pages.
  6. Нажмите кнопку "ОК ", чтобы сохранить изменения и закрыть диалоговое окно "Страницы свойств проекта".

Параметр /std:c++20 или более поздней версии требуется для использования единиц заголовка. Чтобы изменить стандарт языка C++, используемый компилятором, выполните следующие действия.

  1. В главном меню выберите "Свойства проекта>", чтобы открыть диалоговое окно "Страницы свойств проекта".
  2. Выберите все конфигурации в раскрывающемся списке "Конфигурация" и выберите "Все платформы" в раскрывающемся списке "Платформа". Эти параметры гарантируют, что изменения применяются для отладки или выпуска и других конфигураций.
  3. В левой области диалогового окна "Страницы свойств проекта" выберите "Общие свойства>конфигурации".
  4. В раскрывающемся списке "Стандартный язык C++" выберите СТАНДАРТ ISO C++20 (/std:c++20) (или более поздней версии).
  5. Нажмите кнопку "ОК ", чтобы сохранить изменения и закрыть диалоговое окно "Страницы свойств проекта".
  6. В главном меню создайте решение, выбрав "Построить решение сборки>".

Запустите решение, чтобы убедиться, что он создает ожидаемые выходные данные: 1

Основное внимание на том, следует ли использовать этот подход, заключается в балансе между удобством и стоимостью сканирования всех файлов, чтобы определить, какие файлы заголовков следует создавать в виде единиц заголовков.

См. также

Сравнение единиц заголовков, модулей и предварительно скомпилированных заголовков
Руководство. Импорт стандартной библиотеки C++ с помощью модулей
Создание и импорт блоков заголовков в проектах Visual C++
/translateInclude