Заголовки библиотеки и #define
Я не был уверен, что искать для этого. Так что извините, если это просто. Но позвольте мне изложить сценарий и посмотреть, какие ответы там.
Допустим, у меня есть библиотека, которая определяет такую структуру:
struct Example {
int a;
#if B_ENABLED
int b;
#endif
};
Этот заголовок устанавливается как часть установки библиотеки в целом. Мой вопрос здесь заключается в том, что если моя библиотека определяет B_ENABLED, она будет иметь структуру с этими двумя переменными. Однако, если мое приложение не определяет это также. Затем он будет интерпретировать заголовок как определяющий структуру только с одним членом.
Является ли лучший способ справиться с этим, просто чтобы создать какой-то заголовок "options", который будет включать все #defines, которые были указаны в сборке библиотеки?
Моя библиотека строится с помощью CMAKE. Таким образом, решение CMAKE для этого - дополнительный кредит =D.
1 ответ
Решение № 1 (настройка + установка)
Включают config.hpp
файл в вашем заголовочном файле:
#ifndef FOO_HPP_
#define FOO_HPP_
#include "config.hpp" // FOO_DEBUG
class Foo {
public:
int result() const;
private:
int a_;
#ifdef FOO_DEBUG
int b_;
#endif // FOO_DEBUG
};
#endif // FOO_HPP_
config.hpp
вывод команды configure_file:
configure_file(config.hpp.in "${PROJECT_BINARY_DIR}/config/config.hpp")
include_directories("${PROJECT_BINARY_DIR}/config")
install(FILES Foo.hpp "${PROJECT_BINARY_DIR}/config/config.hpp" DESTINATION include)
входной файл config.hpp.in
использовать специальные cmakedefine
директива:
#ifndef CONFIG_HPP_
#define CONFIG_HPP_
#cmakedefine FOO_DEBUG
#endif // CONFIG_HPP_
Обратите внимание, что при использовании установленной библиотеки в другом проекте:
- вам все еще нужно указать включаемые каталоги для библиотеки
- если у вашей библиотеки есть зависимости, вам нужно связать их вручную
- у вас не может быть 2 конфигурационных файлов (Debug/Release)
Решение № 2 (рекомендуется экспорт / импорт)
Команда install(EXPORT ...) может содержать всю информацию об использовании библиотеки (также называемые требованиями к использованию: включая определения, связанную библиотеку, конфигурацию и т. д.):
add_library(Foo Foo.cpp Foo.hpp)
# Target which used Foo will be compiled with this definitions
target_compile_definitions(Foo PUBLIC $<$<CONFIG:Release>:FOO_DEBUG=0>)
target_compile_definitions(Foo PUBLIC $<$<CONFIG:Debug>:FOO_DEBUG=1>)
# This directory will be used as include
target_include_directories(Foo INTERFACE "${CMAKE_INSTALL_PREFIX}/include")
# This library will be linked
target_link_libraries(Foo PUBLIC pthread)
# Regular install
install(FILES Foo.hpp DESTINATION include)
# Install with export set
install(TARGETS Foo DESTINATION lib EXPORT FooTargets)
install(EXPORT FooTargets DESTINATION lib/cmake/Foo)
Установка такого проекта даст файлы (CMAKE_DEBUG_POSTFIX
является d
):
include/Foo.hpp
lib/libFoo.a
lib/libFood.a
lib/cmake/Foo/FooTargets-debug.cmake
lib/cmake/Foo/FooTargets-release.cmake
lib/cmake/Foo/FooTargets.cmake
Включают FooTargets.cmake
файл для импорта установленной библиотеки в проект. Например, используя find_package
команда (нужна настройка, см. configure_package_config_file):
add_executable(prog main.cpp)
find_package(Foo REQUIRED) # import Foo
target_link_libraries(prog Foo)
Обратите внимание, что:
- путь к
include/Foo.hpp
автоматически добавляется в опции компилятора - зависимая библиотека
pthread
автоматически добавляется вprog
опция компоновщика - определение
FOO_DEBUG=0
добавлен в тип сборки Release - определение
FOO_DEBUG=1
добавлен в тип сборки Debug
обоснование
So excuse me if this is simple
Это не (:
Корень проблемы в ODR (C++ Standard 2011, 3.2 [basic.def.ord], с.3):
Every program shall contain exactly one definition of every non-inline function
or variable that is odr-used in that program; no diagnostic required. The
definition can appear explicitly in the program, it can be found in the
standard or a user-defined library
ИМХО хорошего общего решения пока не существует. Использование CMake с импортированной конфигурацией может немного помочь, но в некоторых случаях вы все равно получите ошибки компоновщика (например, если вы используете библиотеку, скомпилированную с gcc
, который связан с libstdcxx
по умолчанию, и попробуйте связать его с проектом clang
компилятор, который связан с libcxx
). Некоторые из этих проблем (не все, все же) могут быть решены с помощью файлов инструментов. Смотрите примеры.