【问题标题】:How to deal with Boost Spirit X3 causing a "static initialization order fiasco" in Visual Studio 2019?如何处理 Boost Spirit X3 在 Visual Studio 2019 中导致“静态初始化订单惨败”?
【发布时间】:2026-01-15 21:15:02
【问题描述】:

我正在 boost::spirit::x3 之上使用 C++ 开发一个重要的解析器。我将我的解析代码拆分为逻辑单元,其中一些是相互依赖的。例如,一个单元是一个表达式解析器,它也公开了一个标识符解析器。目标语言的许多更高级别的句法结构包括表达式和标识符,因此该单元通常是依赖项。我已将代码分成三份文件as the documentation recommends。如果有单位 foobarquux,我有如下文件:

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

.h 扩展 BOOST_SPIRIT_DECLARE 的位置; _def.h 扩展 BOOST_SPIRIT_DEFINE.cpp 扩展 BOOST_SPIRIT_INSTANTIATE。我遇到了一个持久性问题,由于 Spirit 代码中的静态初始化失败(.../x3/nonterminal/rule.hpp 的第 160 行),我的代码在启动时出错,但仅在运行调试版本时。

基于this onethis one 之类的* 问题和答案,加上在发布配置中构建时代码执行没有错误的事实,我相信我的代码没有任何问题。如果无法在调试中修复我的代码,我可以看到两种解决方法:

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

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

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

【问题讨论】:

    标签: c++ boost boost-spirit boost-spirit-x3


    【解决方案1】:

    通常的方法是让函数通过引用返回本地静态数据。

    函数局部静态

    • 具有静态“生命周期”(存储持续时间),但只会在首次使用时初始化(避免惨败)
    • 从 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;
    }
    

    更新

    其实documentation example 并不麻烦返回引用,而是每次都返回一个副本:

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

    这表明这样做不会有很大的开销,我建议您避免那里的引用语义复杂化。

    【讨论】:

    • 谢谢,但如果您的示例中 ns::my_rule 的实现取决于静态初始化顺序,则仅此一项并不能解决问题。我可以将my_rule 的整个实现带入函数范围,但我认为我必须放弃使用 BOOST_SPIRIT_INSTANTIATION 宏并将定义放在 _def.hpp 文件中。
    • 初始化顺序一个TU被定义。所以你可以在你的 as-usual-rule-definition 之后定义函数并从那里返回一个副本。请注意,我假设您使用的是最新的 Spirit(参见例如 github.com/boostorg/spirit/pull/229
    • 我相信最新的官方增强版本中包含的精神 x3。我看到 SPIRIT_X3_VERSION = 0x3004