具有调试输出(BOOST_SPIRIT_X3_DEBUG)的X3解析器段错误 [英] X3 parser segfaults with debug output (BOOST_SPIRIT_X3_DEBUG)

查看:52
本文介绍了具有调试输出(BOOST_SPIRIT_X3_DEBUG)的X3解析器段错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此问题涉及两个问题(如接受的答案所示),这两个问题都存在于该版本中带有Boost 1.64的Boost Spirit X3的更新,但在撰写本文时(2017-04-30),现在两者都已在developer分支中固定(或至少在编译时检测到).

This question touches on two issues (as shown by the accepted answer), both of which are present in the version of Boost Spirit X3 that ships with Boost 1.64, but both of which are now fixed (or at least detected at compile time) in develop branch at the time of writing (2017-04-30).

我已经更新了 mcve项目,以反映我为使用开发分支所做的更改的最新增强版本,希望它可以帮助面临其他类似问题的其他人.

I have updated the mcve project to reflect the changes that I made to use the develop branch instead of the latest boost release, in the hopes that it might help out others who face a similar issues.


原始问题

示例代码(特别是rexpr_full和calc)以及 CppCon 2015 BoostCon .


The original question

I am trying to learn how to break up Spirit X3 parsers into separate reusable grammars, as encouraged by the example code (rexpr_full and calc in particular) and the presentations from CppCon 2015 and BoostCon.

我有一个符号表(本质上是将不同的类型映射到我支持的类型的枚举类),我想在几个解析器中重用它.我可以找到的符号表的唯一示例是罗马数字示例,该示例位于单个源文件中.

I have a symbol table (essentially mapping different types to a enum class of the types I am supporting), which I would like to reuse in several parsers. The only example of symbol tables I could find is the roman numerals example, which is in a single source file.

当我尝试将符号表以更具结构化的示例的样式移动到其自己的cpp/h文件中时,如果我尝试解析符号表中未包含的任何字符串,则解析器将出现段错误.如果符号表与使用它的解析器在同一编译单元中定义,则将引发期望异常(这是我希望它执行的操作).

When I try to move the symbol table into its own cpp/h file in the style of the more structured examples my parser will segfault if I try to parse any string which is not in the symbol table. If the symbol table is defined in the same compilation unit as the parsers that use it throws an expectation exception instead (which is what I would expect it to do).

在定义了BOOST_SPIRIT_X3_DEBUG的情况下,我得到以下输出:

With BOOST_SPIRIT_X3_DEBUG defined I get the following output:

<FruitType>
  <try>GrannySmith: Mammals</try>
  <Identifier>
    <try>GrannySmith: Mammals</try>
    <success>: Mammals</success>
    <attributes>[[
Process finished with exit code 11

我做了一个小项目,显示了我要实现的目标,可以在这里找到: https://github.com/sigbjornlo/spirit_fruit_mcve

I have made a small project which shows what I am trying to achieve and is available here: https://github.com/sigbjornlo/spirit_fruit_mcve

我的问题:

  • 在这种情况下,为什么将符号解析器移动到单独的编译单元会导致分段错误?
  • 使符号表可在多个解析器中重用的推荐方法是什么?(在MCVE中,我显然只在另一个解析器中使用 解析器,但是在我的完整项目中,我想在其他几个解析器中使用它.)
  • Why does moving the symbol parser to a separate compilation unit cause a segmentation fault in this case?
  • What is the recommended way of making a symbol table reusable in multiple parsers? (In the MCVE I obviously only use the fruit parser in one other parser, but in my full project I want to use it in several other parsers.)

下面是MCVE项目的代码:

Below is the code for the MCVE project:

main.cpp

#include <iostream>

#define BOOST_SPIRIT_X3_DEBUG
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

#include "common.h"
#include "fruit.h"

namespace ast {
    struct FruitType {
        std::string identifier;
        FRUIT fruit;
    };
}

BOOST_FUSION_ADAPT_STRUCT(ast::FruitType, identifier, fruit);

namespace parser {
    // Identifier
    using identifier_type = x3::rule<class identifier, std::string>;
    const auto identifier = identifier_type {"Identifier"};
    const auto identifier_def = x3::raw[x3::lexeme[(x3::alpha | '_') >> *(x3::alnum | '_')]];
    BOOST_SPIRIT_DEFINE(identifier);

    // FruitType
    struct fruit_type_class;
    const auto fruit_type = x3::rule<fruit_type_class, ast::FruitType> {"FruitType"};

    // Using the sequence operator creates a grammar which fails gracefully given invalid input.
    // const auto fruit_type_def = identifier >> ':' >> make_fruit_grammar();

    // Using the expectation operator causes EXC_BAD_ACCESS exception with invalid input.
    // Instead, I would have expected an expectation failure exception.
    // Indeed, an expectation failure exception is thrown when the fruit grammar is defined here in this compilation unit instead of in fruit.cpp.
    const auto fruit_type_def = identifier > ':' > make_fruit_grammar();

    BOOST_SPIRIT_DEFINE(fruit_type);
}

int main() {
    std::string input = "GrannySmith: Mammals";
    parser::iterator_type it = input.begin(), end = input.end();

    const auto& grammar = parser::fruit_type;
    auto result = ast::FruitType {};

    bool successful_parse = boost::spirit::x3::phrase_parse(it, end, grammar, boost::spirit::x3::ascii::space, result);
    if (successful_parse && it == end) {
        std::cout << "Parsing succeeded!\n";
        std::cout << result.identifier << " is a kind of " << to_string(result.fruit) << "!\n";
    } else {
        std::cout << "Parsing failed!\n";
    }

    return 0;
}

std::string to_string(FRUIT fruit) {
    switch (fruit) {
        case FRUIT::APPLES:
            return "apple";
        case FRUIT::ORANGES:
            return "orange";
    }
}

common.h

#ifndef SPIRIT_FRUIT_COMMON_H
#define SPIRIT_FRUIT_COMMON_H

namespace x3 = boost::spirit::x3;

enum class FRUIT {
    APPLES,
    ORANGES
};

std::string to_string(FRUIT fruit);

namespace parser {
    using iterator_type = std::string::const_iterator;
    using context_type = x3::phrase_parse_context<x3::ascii::space_type>::type;
}

#endif //SPIRIT_FRUIT_COMMON_H

fruit.h

#ifndef SPIRIT_FRUIT_FRUIT_H
#define SPIRIT_FRUIT_FRUIT_H

#include <boost/spirit/home/x3.hpp>

#include "common.h"

namespace parser {
    struct fruit_class;
    using fruit_grammar = x3::rule<fruit_class, FRUIT>;

    BOOST_SPIRIT_DECLARE(fruit_grammar)

    fruit_grammar make_fruit_grammar();
}


#endif //SPIRIT_FRUIT_FRUIT_H

fruit.cpp

#include "fruit.h"

namespace parser {
    struct fruit_symbol_table : x3::symbols<FRUIT> {
        fruit_symbol_table() {
            add
                    ("Apples", FRUIT::APPLES)
                    ("Oranges", FRUIT::ORANGES);
        }
    };

    struct fruit_class;
    const auto fruit = x3::rule<fruit_class, FRUIT> {"Fruit"};
    const auto fruit_def = fruit_symbol_table {};
    BOOST_SPIRIT_DEFINE(fruit);

    BOOST_SPIRIT_INSTANTIATE(fruit_grammar, iterator_type, context_type);

    fruit_grammar make_fruit_grammar() {
        return fruit;
    }
}

推荐答案

在复制器上做得很好.这让我想起了我的PR https://github.com/boostorg/spirit/pull/229 (请参阅此处的分析奇怪的语义行为分裂后的增强精神x3 ).

Very good work on the reproducer. This reminded me of my PR https://github.com/boostorg/spirit/pull/229 (see analysis here Strange semantic behaviour of boost spirit x3 after splitting).

问题可能是静态初始化命令Fiasco在规则的调试名称被复制之前对其进行了复制.

The problem would be Static Initialization Order Fiasco taking copies of the debug-names of rules before they became initialized.

实际上,实际上禁用调试信息确实可以消除崩溃,并正确引发期望失败.

In fact, indeed disabling the debug information does remove the crash, and correctly throws the expectation failure.

develop分支¹也会发生同样的情况,因此可能还有另一件事类似,或者我错过了一个机会.现在,您可以禁用调试输出.如果找到该地点,我会发布更新.

The same happens with the develop branch¹, so either there is another similar thing, or I missed a spot. For now, know you can disable the debug output. I'll post an update if I find the spot.

我没有错过任何地方. call_rule_definition 中有一个单独的问题,其中它使用实际属性类型而不是转换后的类型来参数化 context_debug<> 帮助器类:

I didn't miss a spot. There's a separate issue in call_rule_definition where it parameterizes the context_debug<> helper class with the actual attribute type instead of the transformed one:

#if defined(BOOST_SPIRIT_X3_DEBUG)
                typedef typename make_attribute::type dbg_attribute_type;
                context_debug<Iterator, dbg_attribute_type>
                dbg(rule_name, first, last, dbg_attribute_type(attr_), ok_parse);
#endif

该注释似乎表明此行为是所需的:它尝试在转换之前打印属性.但是,除非综合属性类型与实际属性类型匹配,否则它完全不起作用.在这种情况下,它使 context_debug 悬挂地引用了临时转换的属性,从而导致

The comment seems to suggest that this behaviour is as desired: it tries to print the attribute before transformation. However, it totally doesn't work unless the synthesized attribute type matches the actual attribute type. In this case, it makes context_debug take a dangling reference to a temporary converted attribute, leading to Undefined Behaviour.

在工作案例中,这实际上也是未定义的行为.我只能假设在内联定义的情况下事情发生得很好,使事情看起来像预期的那样工作.

It's in fact also undefined behaviour in the working cases. I can only assume things happen to pan out nicely in the inline-definition case, making it seem like things work as intended.

据我所知,这将是一个干净的解决方案,可以防止任何不必要的转换以及它们附带的临时操作:

To the best of my knowledge this would be a clean fix, preventing any unwarranted conversions and the temporaries that come with them:

#if defined(BOOST_SPIRIT_X3_DEBUG)
                context_debug<Iterator, transform_attr>
                dbg(rule_name, first, last, attr_, ok_parse);
#endif

我为此创建了请求请求: https://github.com/boostorg/精神/拉力/232

I've created pull request for this: https://github.com/boostorg/spirit/pull/232

¹开发分支似乎没有合并到 查看全文

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