分裂后增强精神x3的奇怪语义行为 [英] Strange semantic behaviour of boost spirit x3 after splitting

查看:90
本文介绍了分裂后增强精神x3的奇怪语义行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I came across a strange behaviour of boost spirit x3, after I splittet my grammar up into the recommended parser.hpp, parser_def.hpp, parser.cpp files. My example gramar parses some kind of easy enums:

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

这是我的枚举语法. 当我不将枚举和标识符解析器拆分为推荐的文件时,一切正常,尤其是字符串"enum {foo, bar}" 引发预期失败. 可在此处找到此示例:未拆分的工作示例

this is my enum grammar. When I don't split the enum and identifier parser into the recommended files, everything works fine, especially the string "enum {foo, bar}" throws an expectation failure, as expected. This example can be found here: unsplitted working example

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

But when I split the exactly same grammar up into the different files, the parser throws

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

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

trying to parse the same string "enum {foo, bar}"

可以在此处找到此示例:拆分奇怪的示例

this example can be found here: splitted strange example

  1. ast.hpp

  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)

  • config.hpp

  • config.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;
    
    }
    

  • enum.cpp

  • enum.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;
    }
    
    }
    

  • enum_def.hpp

  • 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)
    }}
    

  • enum.hpp

  • enum.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();
    }
    

  • identifier.cpp

  • identifier.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;
    }
    
    }
    

  • identifier_def.hpp

  • 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)
    }}
    

  • identifier.hpp

  • identifier.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();
    }
    

  • main.cpp

  • 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);    
    }
    

  • 这是一个错误,我是否缺少某些东西,或者这是预期的行为?

    Is this a bug, am I missing something, or is this an expected behaviour?

    此处是我的回购交易,并带有抛出std::logic_error而不是expectation_failure

    here is my repo with an example which throws an std::logic_error instead of an expectation_failure

    推荐答案

    我已经找到了导致错误的原因.

    I've found the cause of the bug.

    该错误是因为Expect指令按值将其作为主题解析器,这是在parser::impl::identifier初始化程序运行之前进行的.

    The bug is with the fact that the expect directive takes it subject parser by value, which is before the parser::impl::identifier initializer runs.

    要可视化,请想象在parser::impl::identifier之前运行的parser::impl::enum_parser静态初始化程序.这对于编译器是有效的.

    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是非法的.

    The copy, therefore, has an uninitialized name field, which fails as soon as the expectation point tries to construct the x3::expectation_failure with the which_ member, because constructing a std::string from a nullptr is illegal.

    总而言之,我担心这里的根本原因是静态初始化顺序惨败.我将查看我是否可以解决该问题并提交PR.

    All in all, I fear the root cause here is Static Initialization Order Fiasco. I'll see whether I can fix it and submit a PR.

    一种直接的解决方法是按相反的顺序列出源文件的顺序,以便在定义后使用:

    An immediate workaround is to list the order of the source files in reverse, so that use comes after definition:

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

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

    Note that if this fixes it on your compiler (it does on mine) that is implementation defined. The standard does NOT specify the order of static initialization across compilation units.

    这篇关于分裂后增强精神x3的奇怪语义行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆