简体   繁体   English

精神X3可以与BOOST_FUSION_ADAPT_ADT一起使用吗?

[英]Can spirit X3 work with BOOST_FUSION_ADAPT_ADT?

Change my codes from QI to X3, and get some compile error with BOOST_FUSION_ADAPT_ADT . 将我的代码从QI更改为X3,并使用BOOST_FUSION_ADAPT_ADT获得一些编译错误。 I tried boost 1.64 and 1.67, neither of them work. 我试过提升1.64和1.67,它们都不起作用。 I modified the spirit X3 example rexpr_min , adding getter and setter to struct rexpr , changing the BOOST_FUSION_ADAPT_STRUCT to BOOST_FUSION_ADAPT_ADT , and compile it fail, too. 我修改了精灵X3示例rexpr_min ,将getter和setter添加到struct rexpr ,将BOOST_FUSION_ADAPT_STRUCT更改为BOOST_FUSION_ADAPT_ADT ,并将其编译失败。

Enviroment: 环境:

  • ubuntu 16.04 ubuntu 16.04

  • G++ 5.4, with -std=c++17 flag G ++ 5.4,带-std=c++17标志

  • boost 1.67 提升1.67

Error message: 错误信息:

boost/spirit/home/x3/core/detail/parse_into_container.hpp:142:35: error: invalid initialization of non-const reference of type ‘boost::fusion::extension::adt_attribute_proxy<client::ast::rexpr, 0, false>&’ from an rvalue of type ‘boost::fusion::extension::deref_impl<boost::fusion::struct_iterator_tag>::apply<boost::fusion::basic_iterator<boost::fusion::struct_iterator_tag, boost::fusion::random_access_traversal_tag, client::ast::rexpr, 0> >::type {aka boost::fusion::extension::adt_attribute_proxy<client::ast::rexpr, 0, false>}’
             return call_synthesize(parser, first, last, context, rcontext,

I guess the fusion::front(attr) return a const reference, and the call_synthesize want a non-const reference (at boost_1_64_0/boost/spirit/home/x3/core/detail/parse_into_container.hpp , line 146). 我猜fusion::front(attr)返回一个const引用,而call_synthesize想要一个非const引用(在boost_1_64_0/boost/spirit/home/x3/core/detail/parse_into_container.hpp ,第146行)。 But I don't know what to do. 但我不知道该怎么办。

I googled and find some regression of QI and they are patched in newest version. 我用谷歌搜索并找到QI的一些回归,并在最新版本中修补它们。 But there is no information with X3. 但X3没有任何信息。

The original code spirit X3 example rexpr_min , And my modification: 原代码精灵X3示例rexpr_min ,而我的修改:

/*=============================================================================
    Copyright (c) 2001-2015 Joel de Guzman

    Distributed under the Boost Software License, Version 1.0. (See accompanying
    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
//  A simple parser for X3 intended as a minimal starting point.
//  'rexpr' is a parser for a language resembling a minimal subset
//  of json, but limited to a dictionary (composed of key=value pairs)
//  where the value can itself be a string or a recursive dictionary.
//
//  Example:
//
//  {
//      "color" = "blue"
//      "size" = "29 cm."
//      "position" = {
//          "x" = "123"
//          "y" = "456"
//      }
//  }
//
///////////////////////////////////////////////////////////////////////////////

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/fusion/include/adapt_adt.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/fusion/include/io.hpp>

#include <iostream>
#include <fstream>
#include <string>
#include <map>

///////////////////////////////////////////////////////////////////////////////
//  Our AST
///////////////////////////////////////////////////////////////////////////////
namespace client { namespace ast
{
    namespace fusion = boost::fusion;
    namespace x3 = boost::spirit::x3;

    struct rexpr;

    struct rexpr_value : x3::variant<
            std::string
          , x3::forward_ast<rexpr>
        >
    {
        using base_type::base_type;
        using base_type::operator=;
    };

    typedef std::map<std::string, rexpr_value> rexpr_map;
    typedef std::pair<std::string, rexpr_value> rexpr_key_value;

    struct rexpr
    {
        rexpr_map i_entries;

        const rexpr_map& entries() const { return i_entries; }
        void entries(const rexpr_map& ent) { i_entries = ent; }
    };
}}

// We need to tell fusion about our rexpr struct
// to make it a first-class fusion citizen
BOOST_FUSION_ADAPT_ADT(client::ast::rexpr,
    (obj.entries(), obj.entries(val))
)

///////////////////////////////////////////////////////////////////////////////
//  AST processing
///////////////////////////////////////////////////////////////////////////////
namespace client { namespace ast
{
    ///////////////////////////////////////////////////////////////////////////
    //  Print out the rexpr tree
    ///////////////////////////////////////////////////////////////////////////
    int const tabsize = 4;

    struct rexpr_printer
    {
        typedef void result_type;

        rexpr_printer(int indent = 0)
          : indent(indent) {}

        void operator()(rexpr const& ast) const
        {
            std::cout << '{' << std::endl;
            for (auto const& entry : ast.entries())
            {
                tab(indent+tabsize);
                std::cout << '"' << entry.first << "\" = ";
                boost::apply_visitor(rexpr_printer(indent+tabsize), entry.second);
            }
            tab(indent);
            std::cout << '}' << std::endl;
        }

        void operator()(std::string const& text) const
        {
            std::cout << '"' << text << '"' << std::endl;
        }

        void tab(int spaces) const
        {
            for (int i = 0; i < spaces; ++i)
                std::cout << ' ';
        }

        int indent;
    };
}}

///////////////////////////////////////////////////////////////////////////////
//  Our rexpr grammar
///////////////////////////////////////////////////////////////////////////////
namespace client { namespace parser
{
    namespace x3 = boost::spirit::x3;
    namespace ascii = boost::spirit::x3::ascii;

    using x3::lit;
    using x3::lexeme;

    using ascii::char_;
    using ascii::string;

    x3::rule<class rexpr_value, ast::rexpr_value>
        rexpr_value = "rexpr_value";

    x3::rule<class rexpr, ast::rexpr>
        rexpr = "rexpr";

    x3::rule<class rexpr_key_value, ast::rexpr_key_value>
        rexpr_key_value = "rexpr_key_value";

    auto const quoted_string =
        lexeme['"' >> *(char_ - '"') >> '"'];

    auto const rexpr_value_def =
        quoted_string | rexpr;

    auto const rexpr_key_value_def =
        quoted_string >> '=' >> rexpr_value;

    auto const rexpr_def =
        '{' >> *rexpr_key_value >> '}';

    BOOST_SPIRIT_DEFINE(rexpr_value, rexpr, rexpr_key_value);
}}

///////////////////////////////////////////////////////////////////////////////
//  Main program
///////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv)
{
    char const* filename;
    if (argc > 1)
    {
        filename = argv[1];
    }
    else
    {
        std::cerr << "Error: No input file provided." << std::endl;
        return 1;
    }

    std::ifstream in(filename, std::ios_base::in);

    if (!in)
    {
        std::cerr << "Error: Could not open input file: "
            << filename << std::endl;
        return 1;
    }

    std::string storage; // We will read the contents here.
    in.unsetf(std::ios::skipws); // No white space skipping!
    std::copy(
        std::istream_iterator<char>(in),
        std::istream_iterator<char>(),
        std::back_inserter(storage));

    using client::parser::rexpr; // Our grammar
    client::ast::rexpr ast; // Our tree

    using boost::spirit::x3::ascii::space;
    std::string::const_iterator iter = storage.begin();
    std::string::const_iterator end = storage.end();
    bool r = phrase_parse(iter, end, rexpr, space, ast);

    if (r && iter == end)
    {
        std::cout << "-------------------------\n";
        std::cout << "Parsing succeeded\n";
        std::cout << "-------------------------\n";
        client::ast::rexpr_printer printer;
        printer(ast);
        return 0;
    }
    else
    {
        std::string::const_iterator some = iter+30;
        std::string context(iter, (some>end)?end:some);
        std::cout << "-------------------------\n";
        std::cout << "Parsing failed\n";
        std::cout << "stopped at: \": " << context << "...\"\n";
        std::cout << "-------------------------\n";
        return 1;
    }
}

As I've been warning people before ¹ you're pushing limits right on the intersection of things that frequently break Spirit's gears: 正如我 ¹ 之前一直在警告人们的那样,你会在经常打破精神齿轮的事物的交叉点上施加限制:

  1. single-element fusion sequences 单元件融合序列
  2. ADT adaptation in general ADT适应性一般
  3. Persistent bugs with ADT fixed in develop ( after 1.67.0 release) 与ADT持久性的错误修复开发( 1.67.0版本)

1. The Single-Element Conundrum 1.单元素难题

I won't spend much time on this because it's a rather old, boring, well-documented² and not essential to your question. 我不会花太多时间在这上面,因为它是一个相当陈旧,枯燥,记录良好的²,对你的问题不是必不可少的。

Let's side-step it by adding a dummy field: 让我们通过添加一个虚拟字段来支持它:

struct rexpr
{
    rexpr_map i_entries;

    const rexpr_map& entries() const { return i_entries; }
    rexpr_map& entries() { return i_entries; }

    void entries(const rexpr_map& ent) { i_entries = ent; }

    int i_dummy;
    int dummy() const { return i_dummy; }
    void dummy(int i) { i_dummy = i; }
};

// ... later:
BOOST_FUSION_ADAPT_ADT(client::ast::rexpr,
    (obj.entries(), obj.entries(val))
    (obj.dummy(), obj.dummy(val))
)

// ... even later:
auto const rexpr_def =
    '{' >> *rexpr_key_value >> '}' >> x3::attr(42);

2. The ADT Proxy 2. ADT代理

The attribute-category machinery of Spirit detects the entries property as a container attribute ( is_container<...>{} evaluates to true ). Spirit的属性类别机制将entries属性检测为容器属性( is_container<...>{}计算为true )。

However the requisite container traits are not in place. 然而,必要的容器特征不到位。

What's more, because of the restrictive interface that ADT proxies grant, the property values can only replaced whole-sale, meaning that we can only implement a very suboptimal version of it: 更重要的是,由于ADT代理授予的限制性接口,属性值只能替换整个销售,这意味着我们只能实现非常不理想的版本:

namespace boost { namespace spirit { namespace x3 { namespace traits {

    template <typename T, auto... Other>
    struct container_value<boost::fusion::extension::adt_attribute_proxy<T, Other...> > 
        : container_value<typename boost::fusion::extension::adt_attribute_proxy<T, Other...>::type>
    { };

    template <typename T, auto... Other>
    struct push_back_container<boost::fusion::extension::adt_attribute_proxy<T, Other...> > 
    {
        using underlying_type = typename boost::fusion::extension::adt_attribute_proxy<T, Other...>::type;

        template <typename X, typename Y>
            static bool call(X& proxy, Y&& v) {
                auto u = proxy.get();
                bool b = push_back_container<underlying_type>::call(u, std::forward<Y>(v));
                proxy = u;
                return b;
            }
    };

} } } }

3. Surprise: Old bugs fixed after 1.67.0 3.惊喜:1.67.0之后修复了旧错误

You require the commits: 您需要提交:

commit ae78e1ec2431517a8b0580099aeba8f9122d8abb
Author: Nikita Kniazev <nok.raven@gmail.com>
Date:   Thu Mar 15 17:33:36 2018 +0300

    X3: sequence: Fixed reference to temporary bug

commit e7f31017ec7c0b5584d12ec1b718d8c415b26fa1
Author: Nikita Kniazev <nok.raven@gmail.com>
Date:   Wed Mar 14 18:54:35 2018 +0300

    Qi: Fixed ADT support by permutation and sequence_or operator

    This is follow-up to 0f2b3c49ce55a41a7d22cc5533e0f4ba59e491ae

These are more recent than 1.67.0 and currently in the develop branch. 这些比1.67.0更新,目前在develop分支中。 They (in part) fix an old issue: https://github.com/boostorg/spirit/pull/153#issuecomment-152879056 . 他们(部分)解决了一个老问题: https//github.com/boostorg/spirit/pull/153#issuecomment-152879056 The current behaviour may also be impacted by commit 当前行为也可能受到提交的影响

commit a0df3c098ff4e42c0958796c4f47d4d72a20c164
Merge: f73b121 fac9dfa
Author: Nikita Kniazev <nok.raven@gmail.com>
Date:   Thu Mar 1 13:44:27 2018 +0300

    Merge pull request #370 from Kojoley/x3-pass-container-attribute-through-sequence

    X3: Pass container attribute through sequence

It's hard to gauge whether the impact is positive or negative in this ... turbulent landscape. 在这个......动荡的景观中,很难判断这种影响是积极的还是消极的。

Demo 演示

Suffice it to say that iff you 我只想说, 如果

  1. compile against ae78e1ec243151 or later ( develop ) 编译反对ae78e1ec243151或更高版本( develop
  2. Apply both the workarounds described above 应用上述两种解决方法

then I see the expected output: 然后我看到了预期的输出:

-------------------------
Parsing succeeded
-------------------------
{
    "color" = "blue"
    "position" = {
        "x" = "123"
        "y" = "456"
    }
    "size" = "29 cm."
}

(based on libs/spirit/example/x3/rexpr/rexpr_examples/a.rexpr input). (基于libs/spirit/example/x3/rexpr/rexpr_examples/a.rexpr输入)。

Summarizing 总结

I hope you don't think this is "fine". 我希望你不要认为这很“好”。 Please consider filing an issue at the mailing list/github. 请考虑在邮件列表/ github上提交问题。 Also take these into account: 还考虑到这些:


¹ not to mention my dislike for it in most cases: Using spirit to parse into classes? ¹在大多数情况下,更不用说我不喜欢它了: 用精神来解析课程?

² Spirit Qi attribute propagation issue with single-member struct , X3, what is attr_gen? ²SpiritQi 属性传播问题与单成员结构X3,什么是attr_gen? , boost::spirit::x3 attribute compatibility rules, intuition or code? boost :: spirit :: x3属性兼容性规则,直觉还是代码?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM