共享库中仅具有全局状态的C ++标头

时间:2018-11-23 18:02:28

标签: c++ global-variables dynamic-linking dynamic-library header-only

我正在使用C ++库,理想情况下,我将只保留标头。

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

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

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

    return *( g );
}

这非常适合使用该库的可执行文件。

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

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

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

有什么方法可以方便地完成此任务吗?

2 个答案:

答案 0 :(得分:1)

您可以强制将符号仅保存在一个文件中,例如:

override func viewDidLayoutSubviews() {

        if(self.indicator != nil)
        {
            self.indicator.center = self.view.center
        }
    }

然后在您的一个框架cpp中,定义宏,然后再添加标题。

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

这绝对不是很好,但是至少要确保一个文件中只有一个静态变量实例。当然,也可以与静态库一起正常使用。

答案 1 :(得分:1)

那又怎么样:

// 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使用。编译包含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;