我目前正在尝试从使用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;
}
发生什么[[我认为是因为解析器试图将int直接分配给类型为Base
的值,但是由于int并不直接映射到std :: string或boost: :recursive_wrapper <>,它会变得不安(其中,不安是指11页编译器错误)。 Somehow,boost :: variant避免了这个问题。有任何线索吗?
以某种方式boost::variant
避免了错误。
是的。 Boost变体具有属性传播支持。
此外,boost::variant
对boost::recursive_wrapper
进行了特殊处理,因此可能是两次禁飞。
关于递归std::variant
的好文章在这里https://vittorioromeo.info/index/blog/variants_lambdas_part_2.html
boost::variant
怎么了?
如果您愿意,可以编写一些转换特征,甚至可以研究x3 :: variant-它可能更适合您?
#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_tag
和types
嵌套类型。[[[Note]通过巧妙地对
添加一些调试输出和测试用例:types
序列进行硬编码,我们可以假装巧妙地处理递归包装器。我们很幸运,这足以欺骗系统。
#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)
现场完整演示
#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]]
¹(在需要显式访问基类的通用编程中,细微的差别可能会咬你)