从独立的 TU 零碎地构建静态数据结构
Building a static data structure from independent TUs piecemeal
我有几个independently-written翻译单元;为了便于讨论,每个变量都有一个(静态或外部的)全局 std::string 变量。这些字符串可能有不同的名称,与 TU 的名称无关。而且我不保证编译的 TU 在与具有 main()
函数(例如,main.cpp
)的 TU 链接之前会相互联系。它们都包含相同的 header、magic1.h
,它定义了一个名为 fun_strings
的全局变量,稍后我们将选择其类型。
现在,我希望能够做到以下几点:
#include <string>
#include "magic1.h"
int main() {
// magic2
for(const std::string& s : fun_strings) {
foo(s);
}
}
重点是即使 main.cpp
_doesn't_include_any_of_the_other_TUs_,他们有一些静态代码导致 fun_strings 对所有 TU 的个人有 copies/references/pointers字符串。
当然,问题是如何做到这一点。我将 "magic1" 和 "magic2" 表示为我可能放置通用代码的位置。对于带有字符串的示例 TU,在其中某处使用 foo.cpp
和 std::string just_a_string("I'm a foo fun string");
。
备注:
- 解决方案必须是 thread-safe,虽然我猜这应该不是问题。
- 这是我实际尝试做的事情的简化版本,对于动机有点模糊,我们深表歉意。
- 忽略碰撞的可能性,在我遇到的实际问题中不可能有碰撞。
- 我宁愿不做任何基于动态加载 objects 和遍历它们的符号表的事情(如果我使用静态方法失败,我可能会被迫这样做)
制作一个 class 可以在声明时自动将字符串添加到 fun_strings
怎么样:
class fun_string
{
public:
fun_string(const char * string)
: m_string(string)
{
get_fun_strings().push_back(m_string);
}
private:
string m_string;
};
然后在翻译单元中,您将使用 fun_string
.
定义全局字符串
在上面的代码中,我使用了 get_fun_strings()
而不是直接命名为 fun_strings
的全局变量。这是为了避免在另一个静态变量的初始化中使用静态变量时可能发生的任何 static initialisation fiasco 问题。
vector<string> & get_fun_strings()
{
static vector<string> v;
return v;
}
还要注意,从 C++11 开始,局部函数静态保证以线程安全的方式初始化。这可能不是什么大问题,因为如果您至少有一个全局 fun_string
,函数局部静态将在 main()
之前初始化,因此可能在任何其他线程被初始化之前已创建。
至于忽略碰撞要求——这可以在 fun_string
构造函数中完成。也就是插入前应该检查是否有碰撞,并适当处理。
这里是live demo这个方法。
一些注意事项:
- 正如 Yakk 在评论中提到的,不要指望
get_fun_strings()
返回的向量包含 main()
. 之前的所有字符串
- 不要在静态对象的析构函数中使用
get_fun_strings()
。请参阅有关静态初始化顺序的页面了解其原因以及如何更改它以支持此类用法。
我有几个independently-written翻译单元;为了便于讨论,每个变量都有一个(静态或外部的)全局 std::string 变量。这些字符串可能有不同的名称,与 TU 的名称无关。而且我不保证编译的 TU 在与具有 main()
函数(例如,main.cpp
)的 TU 链接之前会相互联系。它们都包含相同的 header、magic1.h
,它定义了一个名为 fun_strings
的全局变量,稍后我们将选择其类型。
现在,我希望能够做到以下几点:
#include <string>
#include "magic1.h"
int main() {
// magic2
for(const std::string& s : fun_strings) {
foo(s);
}
}
重点是即使 main.cpp
_doesn't_include_any_of_the_other_TUs_,他们有一些静态代码导致 fun_strings 对所有 TU 的个人有 copies/references/pointers字符串。
当然,问题是如何做到这一点。我将 "magic1" 和 "magic2" 表示为我可能放置通用代码的位置。对于带有字符串的示例 TU,在其中某处使用 foo.cpp
和 std::string just_a_string("I'm a foo fun string");
。
备注:
- 解决方案必须是 thread-safe,虽然我猜这应该不是问题。
- 这是我实际尝试做的事情的简化版本,对于动机有点模糊,我们深表歉意。
- 忽略碰撞的可能性,在我遇到的实际问题中不可能有碰撞。
- 我宁愿不做任何基于动态加载 objects 和遍历它们的符号表的事情(如果我使用静态方法失败,我可能会被迫这样做)
制作一个 class 可以在声明时自动将字符串添加到 fun_strings
怎么样:
class fun_string
{
public:
fun_string(const char * string)
: m_string(string)
{
get_fun_strings().push_back(m_string);
}
private:
string m_string;
};
然后在翻译单元中,您将使用 fun_string
.
在上面的代码中,我使用了 get_fun_strings()
而不是直接命名为 fun_strings
的全局变量。这是为了避免在另一个静态变量的初始化中使用静态变量时可能发生的任何 static initialisation fiasco 问题。
vector<string> & get_fun_strings()
{
static vector<string> v;
return v;
}
还要注意,从 C++11 开始,局部函数静态保证以线程安全的方式初始化。这可能不是什么大问题,因为如果您至少有一个全局 fun_string
,函数局部静态将在 main()
之前初始化,因此可能在任何其他线程被初始化之前已创建。
至于忽略碰撞要求——这可以在 fun_string
构造函数中完成。也就是插入前应该检查是否有碰撞,并适当处理。
这里是live demo这个方法。
一些注意事项:
- 正如 Yakk 在评论中提到的,不要指望
get_fun_strings()
返回的向量包含main()
. 之前的所有字符串
- 不要在静态对象的析构函数中使用
get_fun_strings()
。请参阅有关静态初始化顺序的页面了解其原因以及如何更改它以支持此类用法。