首页 > 解决方案 > 将 Boost Spirit 解析器从 boost::variant 转换为 std::variant 2

问题描述

我正在尝试使用 x3 解析器std::variant代替boost::variant,但没有成功。

我关注了关于这个主题的帖子:从 boost::variant 到 std::variant 的转换提升精神解析器,但在我的情况下,让它工作所需的样板似乎不起作用。(简化的)错误消息指出:

std::variant<...> has no member named apply_visitor

我承认,但这个样板对我来说是如此的不明飞行物,我将无法修复它。

这是重现我的问题的最小测试代码,可在 coliru 上找到

#include <iostream>
#include <variant>
#define BOOST_SPIRIT_X3_DEBUG
#include "boost/spirit/home/x3.hpp"
#include "boost/spirit/include/support_istream_iterator.hpp"

// boiler plate provided in the original post
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...> > {};
}



namespace x3 = boost::spirit::x3;

typedef std::variant<unsigned int, std::string> type_t;

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

static std::ostream & operator<<(std::ostream& o, type_t const & v)
{
    if (std::holds_alternative<unsigned int>(v))
        o << std::get<unsigned int>(v) << "\n";
    else
        o << std::get<std::string>(v) << "\n";
    return o;
}

int main(void)
{
    static_assert(x3::traits::is_variant<type_t>::value);
    auto const parser
    = x3::rule<struct parser_tag, type_t >{ "parser" }
    = ('{' >> (x3::uint_ | x3::string("Hello")) >> '}');

    type_t var;
    std::string input("{12}");
    bool v = x3::phrase_parse(input.begin(), input.end(), parser ,x3::space,var);
    std::cout << "parsing : " << (v ? "OK" : "KO") << "\n";
    std::cout << "var = " << var << "\n";
    return 0;
}

标签: c++boostc++17boost-spirit-x3

解决方案


代码很好。改编只是没有定义为调试处理程序打印变体的方法。所以,评论

//#define BOOST_SPIRIT_X3_DEBUG

使其编译:http ://coliru.stacked-crooked.com/a/304122ff888cef2b

parsing : OK
var = 12

添加您自己的打印

跟踪代码显示 nonterminal/rule.hpp 包含 simple_trace.hpp,它使用 print_attribute 特征来打印内容。

namespace boost::spirit::x3::traits {
    template <typename Out, typename T, typename Enable = void>
    struct print_attribute_debug;
}

好的,让我们看看我们是否可以为我们的目的专门化这个特征:

namespace boost::spirit::x3::traits {
    template <typename Out, typename... Ts>
    struct print_attribute_debug<Out, std::variant<Ts...>, void> {
        template <typename U>
        static void call(Out& out, U const& value) {
            std::visit([&out](auto const& v) {
                x3::traits::print_attribute(out, v);
            }, value);
        }
    };
}

成功

住在科利鲁

#include <iostream>

#include <variant>
#define BOOST_SPIRIT_X3_DEBUG
#include "boost/spirit/home/x3.hpp"
#include "boost/fusion/include/for_each.hpp"
#include "boost/spirit/include/support_istream_iterator.hpp"

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...> > {};
}

namespace boost::spirit::x3::traits {
    template <typename Out, typename... Ts>
    struct print_attribute_debug<Out, std::variant<Ts...>, void> {
        template <typename U>
        static void call(Out& out, U const& value) {
            std::visit([&out](auto const& v) {
                x3::traits::print_attribute(out, v);
            }, value);
        }
    };
}

namespace x3 = boost::spirit::x3;

typedef std::variant<unsigned int, std::string> type_t;

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

static std::ostream & operator<<(std::ostream& o, type_t const & v)
{
    if (std::holds_alternative<unsigned int>(v))
        o << std::get<unsigned int>(v) << "\n";
    else
        o << std::get<std::string>(v) << "\n";
    return o;
}

int main(void)
{
    static_assert(x3::traits::is_variant<type_t>::value);
    auto const parser
    = x3::rule<struct parser_tag, type_t >{ "parser" }
    = ('{' >> (x3::uint_ | x3::string("Hello")) >> '}');

    type_t var;
    std::string input("{12}");
    bool v = x3::phrase_parse(input.begin(), input.end(), parser ,x3::space,var);
    std::cout << "parsing : " << (v ? "OK" : "KO") << "\n";
    std::cout << "var = " << var << "\n";
    return 0;
}

印刷

<parser>
  <try>{12}</try>
  <success></success>
  <attributes>12</attributes>
</parser>
parsing : OK
var = 12

推荐阅读