如何处理Boost Spirit X3导致Visual Studio 2019 "static initialization order fiasco"?

How to deal with Boost Spirit X3 causing a "static initialization order fiasco" in Visual Studio 2019?

本文关键字:2019 Studio static initialization fiasco order Visual 导致 何处理 处理 X3      更新时间:2023-10-16

我正在 boost::spirit::x3 之上的 C++ 中研究一个重要的解析器。我正在将解析代码拆分为逻辑单元,其中一些是彼此的依赖关系。例如,一个单元是一个表达式分析器,它也公开一个标识符分析器。目标语言的许多高级语法结构都包含表达式和标识符,因此此单元通常是依赖项。我已按照文档的建议将代码拆分为三重文件。如果有单位foobarquux,我有这样的文件:

parser
foo.h
foo_def.h
foo.cpp
bar.h
bar_def.h
bar.cpp
quux.h
quux_def.h
quux.cpp

其中.hBOOST_SPIRIT_DECLARE展开;_def.hBOOST_SPIRIT_DEFINE展开,.cppBOOST_SPIRIT_INSTANTIATE展开。我遇到了一个持续存在的问题,由于 Spirit 代码中的静态初始化惨败(.../x3/nonterminal/rule.hpp 的第 160 行(,我的代码在启动时出错,但仅在运行调试版本时。

基于像这个和这个这样的堆栈溢出问题和答案,再加上代码在发布配置中构建时执行时没有错误的事实,我相信我的代码没有任何问题。如果无法在调试中修复我的代码,我可以看到两种解决方法:

  1. 仅在调试版本中使用杂注声明来更改静态初始化的顺序。(更改.vcxproj文件中项的顺序不会影响初始化顺序(在Visual Studio中,如果我在表达式解析器的.cpp文件中放置一个#pragma init_seg(lib),问题就会消失。

  2. 仅在调试版本中在必要时包含多余的_def.h文件,例如,如果quux依赖于上面的bar,那么在quux.cpp中包含bar_def.h将解决问题,但违背了将解析器拆分为多个文件的目的。

我想知道这个问题的状态是什么?这似乎是一个已知问题,但已经存在了很长时间,所以我不应该期望它在精神级别得到修复吗?有没有更好的方法来构建我的代码,以免发生此问题?我想知道是否有可能,例如,将所有解析器及其子解析器创建为首次使用时创建的函数范围内的静态变量,但文献中没有这样的例子,所以我不确定是否有可能?

通常的方法是让函数通过引用返回局部静力学。

函数局部静力学

  • 具有静态"生命周期"(存储持续时间(,但仅在首次使用时初始化(躲避惨败(
  • 从 C++11 开始,您甚至可以依靠初始化来实现线程安全

因此,您可能在标题中:

my_rule_type const& my_rule();

在定义规则的 cpp 中:

my_rule_type const& my_rule() {
static const my_rule_type s_my_rule = ns::my_rule;
return s_my_rule;
}

更新

实际上,文档示例不会为返回引用而烦恼,而是每次返回一个副本:

parser::employee_type employee()
{
return parser::employee;
}

这表明这样做不会有明显的开销,我建议您避免引用语义的复杂性。