2

我正在开发一个 C++ 库,理想情况下我会保留标头。

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

static我可以通过函数内的变量轻松实现这一点:

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

    return *( g );
}

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

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

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

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

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

4

2 回答 2

2

关于什么:

// 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 使用您的定义的 .cpp GetGlobalStrings。在 Windows 上,声明函数dllexport是隐式 ODR 使用。编译包含GlobalString.h并将其链接到 dll 的 cpp 应该可以工作。

// GlobalSring.cpp

#define GLOBAL_STRING_SRC
#include <GlobalString.h>

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

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

inline EXPORT_SYMBOL std::vector<std::string> GlobalString;
于 2018-11-23T22:50:21.113 回答
1

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

#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),那么它会变得有点棘手,因为您需要另一个全局标志来说明您是否在框架中并激活导出/导入属性。

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

于 2018-11-23T18:27:39.800 回答