C++;仅在共享库中具有全局状态的标头 我正在研究一个C++库,我只希望保持头。< /P>

C++;仅在共享库中具有全局状态的标头 我正在研究一个C++库,我只希望保持头。< /P>,c++,global-variables,dynamic-linking,dynamic-library,header-only,C++,Global Variables,Dynamic Linking,Dynamic Library,Header Only,此库的特定部分需要全局状态。 假设这个例子需要一个字符串的全局向量 我可以通过函数中的静态变量轻松实现这一点: std::vector< std::string > & GetGlobalStrings( void ) { static auto g = new std::vector< std::string >(); return *( g ); } std::vector&getGlobalString(void) { 静态自动g=new

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

我可以通过函数中的
静态
变量轻松实现这一点:

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

    return *( g );
}
std::vector&getGlobalString(void)
{
静态自动g=newstd::vector();
返回*(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
#如果定义(我需要坏的黑客)|定义(全局状态存储)
#定义USE_FULL_函数
#恩迪夫
#ifdef使用_FULL_功能
std::vector&getGlobalString()
{
静态std::vectorg;
返回g;
}
#否则
std::vector&getGlobalString();
#恩迪夫
然后在一个框架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;
}
如果
GetGlobalStrings
使用ODR,则
inline
关键字保证链接器看到的不同编译单元中的
GetGlobalStrings
的多个定义将合并为一个。请放心,C++保证了来自内联函数内的静态变量也被合并。请注意,函数定义中的
dllimport
是非法的,除非定义声明为
inline
。我不太熟悉MacOS动态库,但它应该与使用-fvisibility=default标志的clang类似

对于C++17,也可以使用内联变量而不是函数:

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

您需要动态分配该向量吗?你不能只做
静态std::vector g;返回g?没有支持静态变量的共享库?可疑的您将需要使其非内联,可能只有框架可以访问函数实现,而可执行文件只能访问原型。你是否有一个速度问题,使其非标题只?我认为这可以通过一些平台特定的技巧来完成。我对MacOS一无所知,但在Windows上,例如,您可以使用共享内存块执行此操作。@Galik我正在动态分配内存,因此不会收到需要退出时间析构函数(-Wexit时间析构函数)的警告。无论如何,问题是相同的,无论分配类型如何。即使使用自动分配,可执行文件和框架仍将有单独的定义。@MatthieuBrucher无速度问题,仅页眉对于库目的更方便。谢谢。我肯定是这样看的,我真的试过了。不幸的是,这会导致打包问题,因为我还希望能够在其他平台上创建一个可执行文件,只使用头文件。这将需要人们定义宏,我希望避免这种情况……好吧,我终于做到了,尽管我不得不稍微调整Xcode中的构建设置,以允许将可执行文件链接到仅标题库或公开仅标题库的框架。无论如何,这是一条路要走,所以标记这是公认的答案。谢谢你的回答。为macOS找到了一个有效的解决方案,不确定我会在Windows上遇到同样的问题。。。但是我会记住你的答案:)这仍然会输出一个严厉的警告,说函数是在不应该定义的时候定义的。你必须混合这两个答案才能得到好的结果。
inline EXPORT_SYMBOL std::vector<std::string> GlobalString;