Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Система построения проектов CMake. Учебник.pdf
Скачиваний:
0
Добавлен:
08.09.2026
Размер:
2 Мб
Скачать
☆
3.2. Boost
Эта программа ожидает один или несколько аргументов командной строки: «-h», «-v», «-n hчислоi» (или их длинные вер­сии). Если командная строка не соответствует этому формату, выводится сообщение о правильном использовании команды, и программа завершается. Иначе, если указан аргумент «-h», выводится справка об аргументах. Также выводятся названия всех указанных в командной строке аргументов, и, если какой­то из них задаёт целочисленный аргумент, выводится его значе­ние.
Пусть требуется возможность построения проекта в конфи­гурациях «Debug», «Release» и т. д. Пусть также необходима воз­можность выбора сборки со статическими или разделяемыми библиотеками. Что касается многопоточности, анализ програм­мы показывает, что библиотека Program Options не использует­ся в нескольких потоках, поэтому её потокобезопасность не тре­буется.
Таким образом, Файл CMakeLists.txt для проекта может быть следующим:
cmake_minimum_required(VERSION 3.2)
project(ex-boost)
if(BUILD_SHARED_LIBS)
add_definitions(-DBOOST_ALL_DYN_LINK)
else()
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED OFF)
281
3. Примеры использования пакетов
if(MSVC)
set(Boost_USE_STATIC_RUNTIME ON)
endif()
endif()
find_package(Boost 1.40 REQUIRED program_options)
include_directories(${Boost_INCLUDE_DIRS})
add_definitions(-DBOOST_ALL_NO_LIB)
add_executable(ex-boost ex-boost.cpp)
target_link_libraries(
ex-boost ${Boost_LIBRARIES})
target_compile_features(
ex-boost
PRIVATE
cxx_auto_type
cxx_range_for)
include(correct_vc_static.cmake)
Модуль correct_vc_static.cmake, подключаемый послед­ней командой, имеет такое же содержимое, что и одноимённый модуль из примера подключения библиотек OpenCV на с. 258.
Здесь перед вызовом команды find_package() (п. 2.11.1) устанавливаются значения переменных из табл. 3.3. Пере­менной Boost_USE_STATIC_LIBS присваивается «истина», ес-
282
3.2. Boost
ли специальная переменная BUILD_SHARED_LIBS установлена в истину. При использовании компилятора Microsoft Visual
C++в этом случае также устанавливается в истину перемен-
ная Boost_USE_STATIC_RUNTIME, чтобы объектный модуль при­ложения и библиотечный модуль Boost использовали один и тот же тип стандартных библиотек времени исполнения (иначе будут ошибки при компоновке). Дополнительно модуль correct_vc_static.cmake в этом случае устанавливает клю­чи компилятора, обеспечивающие зависимость компилируемо­го объектного модуля от статических библиотек. Значение пе­ременной Boost_USE_MULTITHREADED устанавливается в «ложь» только в статическом варианте сборки, поскольку библиоте­ки Boost не собираются в разделяемом однопоточном вариан­те. Значение переменной Boost_USE_DEBUG_RUNTIME подходит по умолчанию при любом варианте сборки.
По умолчанию в заголовочных файлах Boost включены за­висящие от компилятора директивы подключения библиотек при компоновке («#pragma comment(lib, hимя_библиотекиi)»). Эти директивы доступны не для всех компиляторов. Чтобы они не вступали в конфликт с правилами, определяемыми CMake, они отключаются установкой символа препроцессора BOOST_ALL_NO_LIB при помощи команды add_definitions(). Однако в случае использования разделяемых библиотек Boost это имеет побочный эффект: из объявлений функций и клас­сов исчезает объявление __declspec (dllimport) (см. пример на с. 110). Чтобы избежать этого, при использовании разделяе-
283
3. Примеры использования пакетов
мых библиотек также устанавливается символ препроцессора
BOOST_ALL_DYN_LINK. ∗

3.2.4. Частичная подмена стандартной библиотеки

В процессе развития программного проекта может возник­нуть задача по его портированию на некоторую устаревшую ар­хитектуру, для которой не существует современного компиля­тора языка C++. Это могут быть старые сборки систем на осно­ве ядра Linux (например, на вычислительных кластерах), вер­сии Windows, поддержка которых прекращена со стороны ком­пиляторов и т. д. Одной из задач портирования является заме­на используемых возможностей стандартной библиотеки, ко­торых нет в составе старых компиляторов. Чтобы избежать их реализации вручную, можно использовать тот факт, что боль­шинство их сначала появилось в наборе библиотек Boost, преж­де чем быть утверждёнными в новом стандарте (новые контей­неры, интеллектуальные указатели, средства многопоточности и т. д.). Часто интерфейс этих средств мало различается меж­ду стандартной библиотекой и Boost: отличия могут быть толь­ко в путях к заголовочным файлам и в названиях пространств имён.
Если стандартная библиотека компилятора включает встроенную поддержку требуемых возможностей, лучше ис­пользовать её. Если же необходимых средств в ней нет, тогда на систему, где выполняется сборка, можно установить Boost. Он занимает не так много места, и большинство его библио­тек имеет хорошую совместимость с большим набором компи-
284
3.2. Boost
ляторов, в том числе устаревших. Таким образом, задача за­ключается в определении того, какие стандартные заголовоч­ные файлы из числа требуемых присутствуют в компиляторе, и при необходимости использовать вместо них заголовочные файлы и библиотечные модули Boost. Эта задача аналогична од­ной из тех, которые решаются сценарием configure, генериру­емым инструментами Autotools (п. 1.3.2). Чтобы проверить, име­ется ли в наличии та или иная возможность в компиляторе, сценарий генерирует тексты коротких программ и пытается их скомпилировать.
В CMake подобные задачи решаются при помощи стандарт­ных модулей, поставляемых вместе с инструментом. Целая се­рия модулей, имена которых начинаются с «Check», предназна­чена для проверки наличия тех или иных возможностей экспе­риментальным путём. Один из таких модулей под названием CheckIncludeFileCXX может быть использован для проверки возможности подключения заголовочного файла с заданным пу­тём. В модуле определена команда:
check_include_file_cxx(
hимя_файлаi hимя_переменнойifhфлаги_компилятораig)
Она запускает на компиляцию генерируемый файл C
++
с директивой «#include <hимя_файлаi>». Компиляция выпол­няется при помощи временного файла CMakeLists.txt, в ко­тором цель определяется командой add_executable(). Компи­ляция выполняется тем набором инструментов, который со­ответствует выбранному генератору. Результат (истина/ложь)
285
3. Примеры использования пакетов
записывается в переменную с заданным именем, которая со­здаётся в кэше (для ускорения последующих запусков CMake, так как при этом каждый раз не нужно выполнять провер­ку компилируемости кода). Вывод компилятора на тестовом запуске записывается в файл CMakeOutput.log, находящийся в подкаталоге с именем, которое хранится в специальной пе­ременной CMAKE_FILES_DIRECTORY (как правило, содержит стро­ку «/CMakeFiles»). Этот подкаталог находится внутри каталога построения проекта и предназначен для файлов, генерируемых
CMake.
Модуль CheckCXXSourceCompiles решает более общую за­дачу: он проверяет компилируемость заданного исходного кода текущим набором инструментов. В нём определена команда:
check_cxx_source_compiles(
hкодi hимя_переменнойi
f
FAIL_REGEX hрегулярное_выражениеig)
Необязательное регулярное выражение может задавать со­ответствие вывода компилятора, которое будет означать неуда­чу.
Процессом компиляции, запускаемым этими командами, можно управлять при помощи переменных (табл. 3.5).
286
3.2. Boost
Таблица 3.5
Переменные, влияющие на компиляцию командами
check_...
Переменная Значение
CMAKE_REQUIRED_FLAGS Дополнительные флаги ком-
пилятора
*
CMAKE_REQUIRED_DEFINITIONS Дополнительные опре-
деления символов пре­процессора (в формате
-Dhимяif=hзначениеig)
CMAKE_REQUIRED_INCLUDES Дополнительные пути поис-
ка заголовочных файлов
CMAKE_REQUIRED_LIBRARIES Библиотеки для компонов-
**
ки
*
Также могут передаваться в качестве третьего аргумента ко­манды check_include_file_cxx().
**
Используется только check_cxx_source_compiles().
ПРИМЕР Пусть требуется скомпилировать программу, которая ис­пользует разделяемые интеллектуальные указатели (файл
ex-shared.cpp):
#include "compatibility.h"
#include <iostream>
287
3. Примеры использования пакетов
struct Test
{
int m_n;
Test() : m_n(99) { }
};
int main()
{
my::shared_ptr <Test> ptr(new Test);
std::cout << ptr->m_n << std::endl;
}
В более новых компиляторах шаблон shared_ptr <> опре­делён в пространстве имён std в заголовочном файле <memory> (если включена совместимость с C++11 или выше). В тех вер­сиях компиляторов, которые были выпущены до утверждения стандарта C++11, предварительные версии новых возможностей библиотек определены в файлах <tr1/...> в пространстве std::tr1 (по названию «Technical Report 1», неформально обо­значающему расширения стандартной библиотеки, принятые в черновом варианте после выхода стандарта C++03). В ещё более старых версиях этот тип указателей отсутствует в стандартных заголовках.
Заголовочный файл compatibility.h определяет про­странство имён my:
#ifdef HAS_STD_SHARED
288
#include <memory>
namespace my = std;
#elif defined (HAS_TR1_SHARED)
#include <tr1/memory>
namespace my = std::tr1;
#else
#include <boost/shared_ptr.hpp>
namespace my = boost;
#endif
На описание проекта возлагается задача поиска нужных
3.2. Boost
заголовочных файлов и определения символов препроцессора
HAS_STD_SHARED и HAS_TR1_SHARED в нужных ситуациях (файл CMakeLists.txt):
cmake_minimum_required(VERSION 2.4)
project(ex-shared)
set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS TRUE)
include(CheckCXXSourceCompiles)
if(CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_REQUIRED_FLAGS -std=c++11)
endif()
check_cxx_source_compiles(
"#include <memory>
int main()
289
3. Примеры использования пакетов
{
std::shared_ptr <int> p;
}"
STD_SHARED_EXISTS)
if(STD_SHARED_EXISTS)
message(STATUS "Found std::shared_ptr <>")
add_definitions(-DHAS_STD_SHARED)
if(CMAKE_COMPILER_IS_GNUCXX)
add_definitions(-std=c++11)
endif()
else()
set(CMAKE_REQUIRED_FLAGS "")
include(CheckIncludeFileCXX)
check_include_file_cxx(tr1/memory TR1_SHARED_EXISTS)
if(TR1_SHARED_EXISTS)
message(STATUS "Found std::tr1::shared_ptr <>")
add_definitions(-DHAS_TR1_SHARED)
else()
find_package(Boost 1.30)
if(Boost_FOUND)
message(STATUS "Found boost::shared_ptr <>")
include_directories(${Boost_INCLUDE_DIRS})
290
else()
message(
FATAL_ERROR
"Neither std::shared_ptr, std::tr1::shared_ptr,"
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]