c++ - 拆分后 boost spirit x3 的奇怪语义行为

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

在我将语法拆分为推荐的 parser.hppparser_def.hppparser 之后,我遇到了 boost spirit x3 的奇怪行为。 cpp 文件。 我的示例语法解析了一些简单的枚举:

enum = "enum" > identifier > "{" > identifier % "," > "}

这是我的枚举语法。 当我不将枚举和标识符解析器拆分为推荐文件时,一切正常,尤其是字符串 "enum {foo, bar}" 如预期的那样抛出预期失败。 这个例子可以在这里找到:unsplitted working example

但是当我将完全相同的语法拆分到不同的文件中时,解析器会抛出

terminate called after throwing an instance of 'std::logic_error'
  what():  basic_string::_M_construct null not valid

试图解析相同的字符串"enum {foo, bar}"

可以在这里找到这个例子:splitted strange example

  1. ast.hpp

    #pragma once
    
    #include <vector>
    #include <string>
    #include <boost/fusion/include/adapt_struct.hpp>
    
    
    
    namespace ast{
    
    namespace x3 = boost::spirit::x3;
    
    struct Enum {
        std::string _name;
        std::vector<std::string> _elements;
    };
    
    
    }
    
    BOOST_FUSION_ADAPT_STRUCT(ast::Enum, _name, _elements)
    
  2. 配置.hpp

    #pragma once 
    
    #include <boost/spirit/home/x3.hpp>
    
    namespace parser{
    
        namespace x3 = boost::spirit::x3;
    
        typedef std::string::const_iterator iterator_type;
        typedef x3::phrase_parse_context<x3::ascii::space_type>::type context_type;
    
    }
    
  3. 枚举.cpp

    #include "enum_def.hpp"
    #include "config.hpp"
    
    namespace parser { namespace impl {
         BOOST_SPIRIT_INSTANTIATE(enum_type, iterator_type, context_type)
    }}
    
    namespace parser {
    
    const impl::enum_type& enum_parser()
    {
        return impl::enum_parser;
    }
    
    }
    
  4. enum_def.hpp

    #pragma once
    
    #include "identifier.hpp"
    #include "enum.hpp"
    #include "ast.hpp"
    
    namespace parser{ namespace impl{
    
        namespace x3=boost::spirit::x3;
    
        const enum_type enum_parser = "enum";
    
        namespace{
            const auto& identifier = parser::identifier();
        }
        auto const enum_parser_def =
            "enum"
            > identifier
            > "{"
            > identifier % ","
            >"}";
    
        BOOST_SPIRIT_DEFINE(enum_parser)
    }}
    
  5. 枚举.hpp

    #pragma once
    
    #include <boost/spirit/home/x3.hpp>
    #include "ast.hpp"
    
    namespace parser{ namespace impl{
        namespace x3=boost::spirit::x3;
    
        typedef x3::rule<class enum_class, ast::Enum> enum_type;
    
        BOOST_SPIRIT_DECLARE(enum_type)
    
    }}
    
    namespace parser{
        const impl::enum_type& enum_parser();
    }
    
  6. 标识符.cpp

    #include "identifier_def.hpp"
    #include "config.hpp"
    
    namespace parser { namespace impl {
         BOOST_SPIRIT_INSTANTIATE(identifier_type, iterator_type, context_type)
    }}
    
    namespace parser {
    
    const impl::identifier_type& identifier()
    {
        return impl::identifier;
    }
    
    }
    
  7. identifier_def.hpp

    #pragma once
    #include <boost/spirit/home/x3.hpp>
    #include "identifier.hpp"
    
    namespace parser{ namespace impl{
    
        namespace x3=boost::spirit::x3;
    
        const identifier_type identifier = "identifier";    
    
        auto const identifier_def = x3::lexeme[
            ((x3::alpha | '_') >> *(x3::alnum | '_'))
        ];
    
        BOOST_SPIRIT_DEFINE(identifier)
    }}
    
  8. 标识符.hpp

    #pragma once
    #include <boost/spirit/home/x3.hpp>
    
    namespace parser{ namespace impl{
        namespace x3=boost::spirit::x3;
    
        typedef x3::rule<class identifier_class, std::string> identifier_type;
    
        BOOST_SPIRIT_DECLARE(identifier_type)
    }}
    
    
    namespace parser{
        const impl::identifier_type& identifier();
    }
    
  9. main.cpp

    #include <boost/spirit/home/x3.hpp>
    #include "ast.hpp"
    #include "enum.hpp"
    
    namespace x3 = boost::spirit::x3;
    
    template<typename Parser, typename Attribute>
    bool test(const std::string& str, Parser&& p, Attribute&& attr)
    {
        using iterator_type = std::string::const_iterator;
        iterator_type in = str.begin();
        iterator_type end = str.end();
    
        bool ret = x3::phrase_parse(in, end, p, x3::ascii::space, attr);
        ret &= (in == end);
        return ret;
    
    }
    
    int main(){
        ast::Enum attr;
        test("enum foo{foo,bar}", parser::enum_parser(), attr);
        test("enum {foo,bar}", parser::enum_parser(), attr);    
    }
    

这是一个错误,我是否遗漏了什么,或者这是一个预期的行为?

编辑:here是我的 repo 示例,它抛出 std::logic_error 而不是 expectation_failure

最佳答案

我找到了错误的原因。

这个错误是因为 expect 指令在 parser::impl::identifier 初始化程序运行之前按值将其作为主题解析器。

To visualize, imagine the static initializer for parser::impl::enum_parser running before parser::impl::identifier. This is valid for a compiler to do.

因此,拷贝有一个未初始化的 name 字段,一旦期望点尝试使用 which_ 构造 x3::expectation_failure,该字段就会失败 成员,因为从 nullptr 构造 std::string 是非法的。

总而言之,我担心这里的根本原因是 Static Initialization Order Fiasco .我会看看是否可以修复它并提交 PR。

解决方法:

一个直接的解决方法是倒序列出源文件的顺序,以便在定义之后使用:

set(SOURCE_FILES 
    identifier.cpp
    enum.cpp 
    main.cpp 
)

请注意,如果这在实现定义的编译器上修复了它(在我的编译器上修复了)。该标准未指定跨编译单元的静态初始化顺序。

关于c++ - 拆分后 boost spirit x3 的奇怪语义行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41770375/

相关文章:

java - 如何在 Java 中解析 JSON 字符串中的项目

parsing - 高效灵活的二进制数据解析

c++ - Boost spirit x3 - 惰性解析器

c++ - boost spirit 难度,从 XML 示例开始

c++ - 在 LLVM 中,你如何检查一个 block 是否是一个合并 block

c++ - C++中矩阵的最小值

c++ - 我在理解继承和让多个类协同工作时遇到问题?

c++ - 在 Qt 中使用不同的模板构建目标

iPhone - GDataXMLNode 和 GDataXMLElement 之间的区别以及如何使用它们

c++ - 如何使用 Boost Spirit 解析像转义字符串这样的 CSV?