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;
Другие вопросы по тегам