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;
我正在开发一个 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;