将Boost Spirit解析器从boost :: variant转换为std :: variant

问题描述 投票:1回答:1

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

#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;
}

Wandbox for the error

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

c++ boost boost-spirit boost-variant std-variant
1个回答
0
投票
以某种方式boost::variant避免了错误。

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

此外,boost::variantboost::recursive_wrapper进行了特殊处理,因此可能是两次禁飞。

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

boost::variant怎么了?

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

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的支持

更新

您可以通过使用必需的机制使Base派生为结构,以向Spirit指示它是变体(以及针对哪种类型),可以解决问题。这样,您就不必经历特质专门化了:

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_tagtypes嵌套类型。

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

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

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

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

奖金

修正语法:

    您可能希望在字符串生成周围使用lexeme[]
  • 您可能想知道没有分隔符,所以字符串的最小长度(+ char_,而不是* char _)>>
  • 您可能必须对分支进行重新排序,因为字符串产生会吞噬递归规则的整数。
  • 这是我对语法的一些修饰,其中的规则与AST非常相似,通常是这样的:

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时代,您可以推断出经过修改的融合成员:

BOOST_FUSION_ADAPT_STRUCT(Recurse, _i, _base)

现场完整演示

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]]


¹(在需要显式访问基类的通用编程中,细微的差别可能会咬你)
© www.soinside.com 2019 - 2024. All rights reserved.