将属性转换为 boost::variant
casting attribute to boost::variant
在学习如何使用 boost spirit、phoenix 和 fusion 库时,我遇到了这个不能在 msvc(2015,版本 14)和 boost 1.61.0 上编译的最小示例
#include <boost/spirit/include/karma.hpp>
#include <boost/variant/variant.hpp>
namespace ka = boost::spirit::karma;
struct U /* a kind of union (legacy code)*/
{
bool kind;
double foo; /* if kind = true */
size_t bar; /* if kind = false */
};
typedef boost::variant<double, size_t> UVariant;
namespace boost { namespace spirit { namespace traits {
template<>
struct transform_attribute<U,UVariant,ka::domain>
{
typedef UVariant type;
static type pre(U & u) {
switch (u.kind)
{
case true:
return type(u.foo);
case false:
return type(u.bar);
}
}
};
}}}
typedef std::back_insert_iterator<std::string> iterator;
class grm: public ka::grammar<iterator, U()>
{
public:
grm():grm::base_type(start)
{
start = ka::attr_cast<U,UVariant >(foo | bar);
foo = ka::double_;
bar = ka::uint_;
*/
}
private:
ka::rule<iterator,U()> start;
ka::rule<iterator,double()> foo;
ka::rule<iterator,size_t()> bar;
};
int main(int argc, char * argv[])
{
grm g;
U u;
u.kind = true;
u.foo = 1.0;
std::string generated;
std::back_insert_iterator<std::string> sink(generated);
ka::generate(sink,g,u);
return 0;
}
然后我收到以下错误消息:
error C2665:
'boost::detail::variant::make_initializer_node::apply::initializer_node::initialize' :
none of the 5 overloads could convert all arguments types
报告了一个类似的问题 here 虽然我不明白答案是如何解决这个问题的,以及这是否真的是同一个问题,因为似乎所有类型都已正确提供(不需要类型转换) .
问题似乎是 Spirit 没有选择您的自定义 transform_attribute
自定义点。它使用默认的,它试图从 const U
(!!) 构造一个 boost::variant<double,size_t>
,但显然失败了。
Karma 始终在内部使用 const 值,因此您需要将 transform_attribute
的专业化更改为:
namespace boost { namespace spirit { namespace traits {
template<>
struct transform_attribute<const U,UVariant,ka::domain>
^^^^^^^
{
typedef UVariant type;
static type pre(const U & u) {
^^^^^^^
//same as before
}
};
}}}
然后它会被 Karma 拾取,一切都会正常。
完整样本(On rextester):
#include <boost/spirit/include/karma.hpp>
#include <boost/variant/variant.hpp>
namespace ka = boost::spirit::karma;
struct U /* a kind of union (legacy code)*/
{
bool kind;
double foo; /* if kind = true */
size_t bar; /* if kind = false */
};
typedef boost::variant<double, size_t> UVariant;
namespace boost { namespace spirit { namespace traits {
template<>
struct transform_attribute<const U,UVariant,ka::domain>
{
typedef UVariant type;
static type pre(const U & u) {
if(u.kind)
{
return type(u.foo);
}
else
{
return type(u.bar);
}
}
};
}}}
typedef std::back_insert_iterator<std::string> iterator;
class grm: public ka::grammar<iterator, U()>
{
public:
grm():grm::base_type(start)
{
start = ka::attr_cast< UVariant >(foo | bar);
foo = ka::double_;
bar = ka::uint_;
}
private:
ka::rule<iterator,U()> start;
ka::rule<iterator,double()> foo;
ka::rule<iterator,size_t()> bar;
};
int main(int argc, char * argv[])
{
grm g;
U u;
u.kind = false;
u.foo = 1.0;
u.bar = 34;
std::string generated;
std::back_insert_iterator<std::string> sink(generated);
ka::generate(sink,g,u);
std::cout << generated << std::endl;
return 0;
}
在学习如何使用 boost spirit、phoenix 和 fusion 库时,我遇到了这个不能在 msvc(2015,版本 14)和 boost 1.61.0 上编译的最小示例
#include <boost/spirit/include/karma.hpp>
#include <boost/variant/variant.hpp>
namespace ka = boost::spirit::karma;
struct U /* a kind of union (legacy code)*/
{
bool kind;
double foo; /* if kind = true */
size_t bar; /* if kind = false */
};
typedef boost::variant<double, size_t> UVariant;
namespace boost { namespace spirit { namespace traits {
template<>
struct transform_attribute<U,UVariant,ka::domain>
{
typedef UVariant type;
static type pre(U & u) {
switch (u.kind)
{
case true:
return type(u.foo);
case false:
return type(u.bar);
}
}
};
}}}
typedef std::back_insert_iterator<std::string> iterator;
class grm: public ka::grammar<iterator, U()>
{
public:
grm():grm::base_type(start)
{
start = ka::attr_cast<U,UVariant >(foo | bar);
foo = ka::double_;
bar = ka::uint_;
*/
}
private:
ka::rule<iterator,U()> start;
ka::rule<iterator,double()> foo;
ka::rule<iterator,size_t()> bar;
};
int main(int argc, char * argv[])
{
grm g;
U u;
u.kind = true;
u.foo = 1.0;
std::string generated;
std::back_insert_iterator<std::string> sink(generated);
ka::generate(sink,g,u);
return 0;
}
然后我收到以下错误消息:
error C2665: 'boost::detail::variant::make_initializer_node::apply::initializer_node::initialize' : none of the 5 overloads could convert all arguments types
报告了一个类似的问题 here 虽然我不明白答案是如何解决这个问题的,以及这是否真的是同一个问题,因为似乎所有类型都已正确提供(不需要类型转换) .
问题似乎是 Spirit 没有选择您的自定义 transform_attribute
自定义点。它使用默认的,它试图从 const U
(!!) 构造一个 boost::variant<double,size_t>
,但显然失败了。
Karma 始终在内部使用 const 值,因此您需要将 transform_attribute
的专业化更改为:
namespace boost { namespace spirit { namespace traits {
template<>
struct transform_attribute<const U,UVariant,ka::domain>
^^^^^^^
{
typedef UVariant type;
static type pre(const U & u) {
^^^^^^^
//same as before
}
};
}}}
然后它会被 Karma 拾取,一切都会正常。
完整样本(On rextester):
#include <boost/spirit/include/karma.hpp>
#include <boost/variant/variant.hpp>
namespace ka = boost::spirit::karma;
struct U /* a kind of union (legacy code)*/
{
bool kind;
double foo; /* if kind = true */
size_t bar; /* if kind = false */
};
typedef boost::variant<double, size_t> UVariant;
namespace boost { namespace spirit { namespace traits {
template<>
struct transform_attribute<const U,UVariant,ka::domain>
{
typedef UVariant type;
static type pre(const U & u) {
if(u.kind)
{
return type(u.foo);
}
else
{
return type(u.bar);
}
}
};
}}}
typedef std::back_insert_iterator<std::string> iterator;
class grm: public ka::grammar<iterator, U()>
{
public:
grm():grm::base_type(start)
{
start = ka::attr_cast< UVariant >(foo | bar);
foo = ka::double_;
bar = ka::uint_;
}
private:
ka::rule<iterator,U()> start;
ka::rule<iterator,double()> foo;
ka::rule<iterator,size_t()> bar;
};
int main(int argc, char * argv[])
{
grm g;
U u;
u.kind = false;
u.foo = 1.0;
u.bar = 34;
std::string generated;
std::back_insert_iterator<std::string> sink(generated);
ka::generate(sink,g,u);
std::cout << generated << std::endl;
return 0;
}