C++ header-only 在共享库中具有全局状态

C++ header-only with global state in a shared library

我正在开发一个 C++ 库,我最好保留它 header-only。

此库的特定部分需要全局状态。
假设此示例需要一个全局字符串向量。

我可以使用函数内的 static 变量轻松实现此目的:

std::vector< std::string > & GetGlobalStrings( void )
{
    static auto g = new std::vector< std::string >();

    return *( g );
}

这对使用该库的可执行文件非常有效。

现在出于某种原因,我还需要将该库打包到 macOS 框架中。

在该框架内,有将访问此全局状态的编译代码。
与该框架链接的可执行文件也是如此。

显然这是行不通的,因为可执行文件和框架将对静态变量有单独的定义,从而使这个全局状态不那么全局。

有什么方法可以方便地完成此操作吗?

您可以强制符号只在一个文件中,例如:

#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 之一中,在包含 header 之前定义宏。

现在,如果您需要导出符号(Windows 主要是 Linux/macOS 隐藏可见性),那么它会变得有点棘手,因为您需要另一个全局标志来说明您是否是否在框架中并激活 export/import 属性。

这肯定不是很好,但至少你确保在一个文件中只有一个静态变量实例。当然,也可以与静态库一起正常工作。

怎么样:

// 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;
}

然后写一个 ODR 使用你定义的 GetGlobalStrings 的 .cpp。在 Windows 上,声明函数 dllexport 是隐式 ODR 使用。编译包含 GlobalString.h 的 cpp 并将其链接到 dll 应该可以。

// GlobalSring.cpp

#define GLOBAL_STRING_SRC
#include <GlobalString.h>

inline 关键字保证如果 GetGlobalStrings 使用 ODR,链接器看到的来自不同编译单元的 GetGlobalStrings 的多个定义将合并为一个。请放心,C++ 保证内联函数中的静态变量也被合并。请注意,函数 定义 上的 dllimport 是非法的,除非声明定义 inline。我不太熟悉 MacOS 动态库,但它应该与带有 -fvisibility=default 标志的 clang 类似地工作。

对于 C++17,也可以使用内联变量代替函数:

inline EXPORT_SYMBOL std::vector<std::string> GlobalString;