C++ только для заголовков с глобальным состоянием в общей библиотеке
Я работаю над библиотекой C++, которую я бы в идеале оставил только в заголовках.
Определенная часть этой библиотеки требует глобального состояния.
Скажем, для этого примера нужен глобальный вектор строк.
Я могу легко достичь этого с static
переменная внутри функции:
std::vector< std::string > & GetGlobalStrings( void )
{
static auto g = new std::vector< std::string >();
return *( g );
}
Это прекрасно работает для исполняемых файлов, использующих библиотеку.
Теперь по какой-то причине мне также нужно упаковать эту библиотеку в инфраструктуру macOS.
Внутри этой структуры есть скомпилированный код, который будет обращаться к этому глобальному состоянию.
Так же исполняемый файл связан с этой структурой.
Очевидно, что это не работает, поскольку исполняемый файл и инфраструктура будут иметь отдельные определения для статической переменной, что делает глобальное состояние не таким уж глобальным.
Есть ли способ сделать это удобным способом?
2 ответа
Вы можете заставить символы находиться только в одном файле, например:
#if defined(I_NEED_A_BAD_HACK) || defined(GLOBAL_STATE_STORE)
# define USE_FULL_FUNCTION
#endif
#ifdef USE_FULL_FUNCTION
std::vector< std::string >& GetGlobalStrings()
{
static std::vector< std::string > g;
return g;
}
#else
std::vector< std::string >& GetGlobalStrings();
#endif
Затем в одном из ваших фреймворков cpp определите макрос, прежде чем включать заголовок.
Теперь, если вам нужно экспортировать символ (в основном для Windows, но также и для Linux/macOS со скрытой видимостью), он становится немного сложнее, так как вам нужен еще один глобальный флаг, указывающий, есть ли у вас фреймворк, и активирующий экспорт / атрибут импорта.
Это определенно не здорово, но, по крайней мере, вы гарантируете, что у вас есть только один экземпляр статической переменной в одном файле. Также работает корректно со статической библиотекой, конечно.
Как насчет:
// GlobalString.h
#include <string>
#include <vector>
#ifdef _MSC_VER
#ifdef GLOBAL_STRING_SRC
#define GLOBAL_STRING_DECLSPEC __declspec(dllexport)
#else
#define GLOBAL_STRING_DECLSPEC __declspec(dllimport)
#endif //GLOBAL_STRING_DECLSPEC
#endif // GLOBAL_STRING_SRC
inline EXPORT_SYMBOL std::vector<std::string>& GetGlobalStrings() noexcept
{
static std::vector<std::string> retval;
return retval;
}
Затем напишите.cpp, что ODR использует ваше определение GetGlobalStrings
, В Windows объявление функции dllexport
является неявным использованием ODR. Компилирование cpp, которое включает GlobalString.h
и связывание его в DLL должно работать.
// GlobalSring.cpp
#define GLOBAL_STRING_SRC
#include <GlobalString.h>
inline
Ключевое слово гарантирует, что несколько определений GetGlobalStrings
из разных блоков компиляции, видимых компоновщиком, будут объединены только в один, если GetGlobalStrings
используется ODR. Будьте уверены, что C++ гарантирует, что статические переменные из встроенной функции также будут объединены. Обратите внимание, что dllimport
определение функции недопустимо, если определение не объявлено inline
, Я не очень знаком с динамическими библиотеками MacOS, но он должен работать аналогично clang с флагом -fvisibility=default.
С C++17 можно также использовать встроенную переменную вместо fuction:
inline EXPORT_SYMBOL std::vector<std::string> GlobalString;