将Boost Spirit解析器从boost :: variant过渡到std :: variant [英] Transitioning Boost Spirit parser from boost::variant to std::variant

查看:152
本文介绍了将Boost Spirit解析器从boost :: variant过渡到std :: variant的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在尝试从使用boost :: variant转移一些代码,而不是使用std :: variant,但是遇到了一个我无法解决的问题.下面是一个最小的测试用例:

I'm currently trying to move some code away from using boost::variant in favour of std::variant, but have run into a problem that I can't figure out. Below is a minimal test case:

#include <string>
#include <variant>

#include <boost/spirit/home/x3.hpp>
#include <boost/variant/recursive_wrapper.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

struct Recurse;
//using Base = boost::variant< // This works
using Base = std::variant<
    std::string,
    boost::recursive_wrapper<Recurse>>;

struct Recurse
{
    int _i;
    Base _base = std::string{};
};

BOOST_FUSION_ADAPT_STRUCT(
    Recurse,
    (int, _i),
    (Base, _base)
)

namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;

const x3::rule<class Base_, Base> base = "base";
const auto operand = *x3::char_("a-zA-Z0-9_") | base;
const auto base_def = (x3::int_ >> operand) | operand;

BOOST_SPIRIT_DEFINE(base)

int main()
{
    std::string text;
    Base result;
    x3::phrase_parse(std::begin(text), std::end(text), base, ascii::space, result);
    return 0;
}

错误的框

我认为 发生的原因是解析器试图将int直接分配给 Base 类型的值,但是由于int不会直接映射到std :: string或boost :: recursive_wrapper<>,它会变得不安(其中,不安是指11页编译器错误).不知何故,boost :: variant避免了此问题.有什么线索吗?

What I think is occurring is that the parser is trying to assign an int directly to a value of type Base, but since an int doesn't directly map to a std::string or a boost::recursive_wrapper<>, it gets upset (whereby upset I mean 11 pages of compiler errors). Somehow, boost::variant avoids this issue. Any clues please?

推荐答案

以某种方式 boost :: variant 避免了该错误.

是的.Boost变体具有属性传播支持.

Yeah. Boost variant has attribute propagation support.

此外, boost :: variant boost :: recursive_wrapper 进行了特殊处理,因此可能是一次双重禁飞.

Besides, boost::variant has special handling of boost::recursive_wrapper so it might be a double no-fly.

有关递归 std :: variant s的好文章在这里https://vittorioromeo.info/index/blog/variants_lambdas_part_2.html

boost :: variant 有什么问题?

如果您愿意,可以编写一些转换特征,甚至可以研究x3 :: variant-也许更适合您?

If you want you can write some transformation traits, or even look into x3::variant - it might suit you better?

在Coliru上直播

Live On Coliru

#include <string>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/variant/recursive_wrapper.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;

struct Recurse;
using Base = x3::variant<
    std::string,
    x3::forward_ast<Recurse> >;

struct Recurse
{
    int _i;
    Base _base;
};

BOOST_FUSION_ADAPT_STRUCT(
    Recurse,
    (int, _i),
    (Base, _base)
)

const x3::rule<class Base_, Base> base = "base";
const auto operand = *x3::char_("a-zA-Z0-9_") | base;
const auto base_def = (x3::int_ >> operand) | operand;

BOOST_SPIRIT_DEFINE(base)

int main()
{
    std::string text;
    Base result;
    x3::phrase_parse(std::begin(text), std::end(text), base, ascii::space, result);
    return 0;
}

旁注:没有 x3 :: forward_ast<> 不能帮助 std :: variant ,从而确认 std :: variant 只是缺少对x3的支持

Side note: No x3::forward_ast<> does not help with std::variant, confirming that std::variant just lacks support in x3

更新

您可以通过以下方法解决问题:将您的 Base 派生为具有所需机制的派生结构,以向Spirit指示它是一个变体(以及针对哪种类型).这样一来,您就不必经历特质专长了:

UPDATE

You can work-around things by making your Base a derived struct with the required machinery to indicate to Spirit that it is a variant (and over which types). That way you don't have to go through trait specialization hell:

struct Recurse;

struct Base : std::variant<std::string, boost::recursive_wrapper<Recurse> > {
    using BaseV = std::variant<std::string, boost::recursive_wrapper<Recurse> >;
    using BaseV::BaseV;
    using BaseV::operator=;

    struct adapted_variant_tag {};
    using types = boost::mpl::list<std::string, Recurse>;
};

struct Recurse {
    int _i;
    Base _base;
};

如您所见,它基本上是相同的¹,但是添加了 adapted_variant_tag types 嵌套类型.

As you can see, it's basically the same¹, but adds adapted_variant_tag and types nested types.

注意 ,通过巧妙地对 types 序列进行硬编码,我们可以假装巧妙地处理递归包装器.我们很幸运,这足以欺骗系统.

Note that by cleverly hardcoding the types sequence, we can pretend to handle the recursive wrapper smartly. We're lucky that this is enough to fool the system.

添加一些调试输出和测试用例:

Adding some debug output and test-cases:

在Coliru上直播

Live On Coliru

#include <string>
#include <variant>
#include <iostream>
#include <iomanip>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/variant/recursive_wrapper.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;

namespace { // for debug
    template<class T>
    std::ostream& operator<<(std::ostream& os, boost::recursive_wrapper<T> const& rw) {
       return os << rw.get();
    }
    template<class... Ts>
    std::ostream& operator<<(std::ostream& os, std::variant<Ts...> const& sv) {
       std::visit([&os](const auto& v) { os << v; }, sv);
       return os;
    }
}

struct Recurse;

struct Base : std::variant<std::string, boost::recursive_wrapper<Recurse> > {
    using BaseV = std::variant<std::string, boost::recursive_wrapper<Recurse> >;
    using BaseV::BaseV;
    using BaseV::operator=;

    struct adapted_variant_tag {};
    using types = boost::mpl::list<std::string, Recurse>;
};

struct Recurse {
    int _i;
    Base _base;
    friend std::ostream& operator<<(std::ostream& os, Recurse const& r) {
        return os << "[" << r._i << ", " << r._base << "]";
    }
};

BOOST_FUSION_ADAPT_STRUCT(
    Recurse,
    (int, _i),
    (Base, _base)
)

static_assert(x3::traits::is_variant<Base>::value);
const x3::rule<class Base_, Base> base = "base";
const auto operand = *x3::char_("a-zA-Z0-9_") | base;
const auto base_def = (x3::int_ >> operand) | operand;

BOOST_SPIRIT_DEFINE(base)

int main()
{
    for (std::string const text : { "yeah8", "32 more" }) {
        Base result;
        auto f = begin(text), l = end(text);
        if (x3::phrase_parse(f, l, base, ascii::space, result)) {
            std::cout << "Result: " << result << "\n";
        } else {
            std::cout << "Failed\n";
        }

        if (f!=l) {
            std::cout << "Remaining input: " << std::quoted(std::string(f,l)) << "\n";
        }

    }
}

哪些印刷品

Result: yeah8
Result: [32, more]


更新2:锦上添花

以下是使 std :: variant 正常工作所需的特征:

namespace boost::spirit::x3::traits {
    template<typename... t>
    struct is_variant<std::variant<t...> >
        : mpl::true_ {};

    template <typename attribute, typename... t>
    struct variant_has_substitute_impl<std::variant<t...>, attribute>
    {
        typedef std::variant<t...> variant_type;
        typedef typename mpl::transform<
              mpl::list<t...>
            , unwrap_recursive<mpl::_1>
            >::type types;
        typedef typename mpl::end<types>::type end;

        typedef typename mpl::find<types, attribute>::type iter_1;

        typedef typename
            mpl::eval_if<
                is_same<iter_1, end>,
                mpl::find_if<types, traits::is_substitute<mpl::_1, attribute>>,
                mpl::identity<iter_1>
            >::type
        iter;

        typedef mpl::not_<is_same<iter, end>> type;
    };


    template <typename attribute, typename... t>
    struct variant_find_substitute<std::variant<t...>, attribute>
    {
        typedef std::variant<t...> variant_type;
        typedef typename mpl::transform<
              mpl::list<t...>
            , unwrap_recursive<mpl::_1>
            >::type types;

        typedef typename mpl::end<types>::type end;

        typedef typename mpl::find<types, attribute>::type iter_1;

        typedef typename
            mpl::eval_if<
                is_same<iter_1, end>,
                mpl::find_if<types, traits::is_substitute<mpl::_1, attribute> >,
                mpl::identity<iter_1>
            >::type
        iter;

        typedef typename
            mpl::eval_if<
                is_same<iter, end>,
                mpl::identity<attribute>,
                mpl::deref<iter>
            >::type
        type;
    };

    template <typename... t>
    struct variant_find_substitute<std::variant<t...>, std::variant<t...> >
        : mpl::identity<std::variant<t...> > {};
}

噪音很大,但是您可以将其放在标题中的某个位置.

That's a lot of noise but you can put it away in a header somewhere.

  • 您可能打算在字符串生成周围使用 lexeme []
  • 您可能想知道字符串没有任何限制(+ char_,而不是* char _)
  • 您可能不得不对分支进行重新排序,因为字符串产生会吞噬用于递归规则的整数.

这是我对语法的精妙尝试,其中的规则与AST非常相似,如通常所说:

Here's my touched-up take on the grammar, where the rules closely mirror the AST, as usually makes sense:

namespace Parser {
    static_assert(x3::traits::is_variant<Base>::value);
    const x3::rule<class Base_, Base> base = "base";
    const auto string = x3::lexeme[+x3::char_("a-zA-Z0-9_")];
    const auto recurse = x3::int_ >> base;
    const auto base_def = recurse | string;
    BOOST_SPIRIT_DEFINE(base)
}

简化融合

最后但并非最不重要的一点,在C ++ 11时代,您可以推断出经过修改的融合成员:

Simplify Fusion

Last but not least, in C++11 era you can deduce the adapted fusion members:

BOOST_FUSION_ADAPT_STRUCT(Recurse, _i, _base)

现场完整演示

在Coliru上直播

Live Full Demo

Live On Coliru

#include <string>
#include <variant>
#include <iostream>
#include <iomanip>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/variant/recursive_wrapper.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;

namespace { // for debug
    template<class T>
    std::ostream& operator<<(std::ostream& os, boost::recursive_wrapper<T> const& rw) {
       return os << rw.get();
    }
    template<class... Ts>
    std::ostream& operator<<(std::ostream& os, std::variant<Ts...> const& sv) {
       std::visit([&os](const auto& v) { os << v; }, sv);
       return os;
    }
}

struct Recurse;
using Base = std::variant<
    std::string,
    boost::recursive_wrapper<Recurse> >;

namespace boost::spirit::x3::traits {
    template<typename... T>
    struct is_variant<std::variant<T...> >
        : mpl::true_ {};

    template <typename Attribute, typename... T>
    struct variant_has_substitute_impl<std::variant<T...>, Attribute>
    {
        typedef std::variant<T...> variant_type;
        typedef typename mpl::transform<
              mpl::list<T...>
            , unwrap_recursive<mpl::_1>
            >::type types;
        typedef typename mpl::end<types>::type end;

        typedef typename mpl::find<types, Attribute>::type iter_1;

        typedef typename
            mpl::eval_if<
                is_same<iter_1, end>,
                mpl::find_if<types, traits::is_substitute<mpl::_1, Attribute>>,
                mpl::identity<iter_1>
            >::type
        iter;

        typedef mpl::not_<is_same<iter, end>> type;
    };


    template <typename Attribute, typename... T>
    struct variant_find_substitute<std::variant<T...>, Attribute>
    {
        typedef std::variant<T...> variant_type;
        typedef typename mpl::transform<
              mpl::list<T...>
            , unwrap_recursive<mpl::_1>
            >::type types;

        typedef typename mpl::end<types>::type end;

        typedef typename mpl::find<types, Attribute>::type iter_1;

        typedef typename
            mpl::eval_if<
                is_same<iter_1, end>,
                mpl::find_if<types, traits::is_substitute<mpl::_1, Attribute> >,
                mpl::identity<iter_1>
            >::type
        iter;

        typedef typename
            mpl::eval_if<
                is_same<iter, end>,
                mpl::identity<Attribute>,
                mpl::deref<iter>
            >::type
        type;
    };

    template <typename... T>
    struct variant_find_substitute<std::variant<T...>, std::variant<T...> >
        : mpl::identity<std::variant<T...> > {};
}

static_assert(x3::traits::is_variant<Base>{}, "");

struct Recurse
{
    int _i;
    Base _base;
    friend std::ostream& operator<<(std::ostream& os, Recurse const& r) {
        return os << "[" << r._i << ", " << r._base << "]";
    }
};

BOOST_FUSION_ADAPT_STRUCT(Recurse, _i, _base)

namespace Parser {
    static_assert(x3::traits::is_variant<Base>::value);
    const x3::rule<class Base_, Base> base = "base";
    const auto string = x3::lexeme[+x3::char_("a-zA-Z0-9_")];
    const auto recurse = x3::int_ >> base;
    const auto base_def = recurse | string;
    BOOST_SPIRIT_DEFINE(base)
}

int main()
{
    for (std::string const text : { "yeah8", "32 more", "18 766 most" }) {
        Base result;
        auto f = begin(text), l = end(text);
        if (x3::phrase_parse(f, l, Parser::base, ascii::space, result)) {
            std::cout << "Result: " << result << "\n";
        } else {
            std::cout << "Failed\n";
        }

        if (f!=l) {
            std::cout << "Remaining input: " << std::quoted(std::string(f,l)) << "\n";
        }
    }
}

哪些印刷品:

Result: yeah8
Result: [32, more]
Result: [18, [766, most]]


¹(细微的差异可能会在需要显式访问基类的通用编程中咬住您)


¹ (the subtle difference MAY bite you in generic programming where you need to access the base-class explicitly)

这篇关于将Boost Spirit解析器从boost :: variant过渡到std :: variant的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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