我有一个有几个boost::variants的应用程序,它们共享许多字段。我希望能够在不复制和粘贴一堆代码的情况下,将这些访问者组合成“较大”变体的访问者。对于非递归变体来说,这样做似乎很简单,但是一旦有了递归变量,访问者(当然)中的自引用就指向错误的类。要使这个具体(并从boost::中滚动):
#include "boost/variant.hpp"
#include <iostream>
struct add;
struct sub;
template <typename OpTag> struct binop;
typedef boost::variant<
int
, boost::recursive_wrapper< binop<add> >
, boost::recursive_wrapper< binop<sub> >
> expression;
template <typename OpTag>
struct binop
{
expression left;
expression right;
binop( const expression & lhs, const expression & rhs )
: left(lhs), right(rhs)
{
}
};
// Add multiplication
struct mult;
typedef boost::variant<
int
, boost::recursive_wrapper< binop<add> >
, boost::recursive_wrapper< binop<sub> >
, boost::recursive_wrapper< binop<mult> >
> mult_expression;
class calculator : public boost::static_visitor<int>
{
public:
int operator()(int value) const
{
return value;
}
int operator()(const binop<add> & binary) const
{
return boost::apply_visitor( *this, binary.left )
+ boost::apply_visitor( *this, binary.right );
}
int operator()(const binop<sub> & binary) const
{
return boost::apply_visitor( *this, binary.left )
- boost::apply_visitor( *this, binary.right );
}
};
class mult_calculator : public boost::static_visitor<int>
{
public:
int operator()(int value) const
{
return value;
}
int operator()(const binop<add> & binary) const
{
return boost::apply_visitor( *this, binary.left )
+ boost::apply_visitor( *this, binary.right );
}
int operator()(const binop<sub> & binary) const
{
return boost::apply_visitor( *this, binary.left )
- boost::apply_visitor( *this, binary.right );
}
int operator()(const binop<mult> & binary) const
{
return boost::apply_visitor( *this, binary.left )
* boost::apply_visitor( *this, binary.right );
}
};
// I'd like something like this to compile
// class better_mult_calculator : public calculator
// {
// public:
// int operator()(const binop<mult> & binary) const
// {
// return boost::apply_visitor( *this, binary.left )
// * boost::apply_visitor( *this, binary.right );
// }
// };
int main(int argc, char **argv)
{
// result = ((7-3)+8) = 12
expression result(binop<add>(binop<sub>(7,3), 8));
assert( boost::apply_visitor(calculator(),result) == 12 );
std::cout << "Success add" << std::endl;
// result2 = ((7-3)+8)*2 = 12
mult_expression result2(binop<mult>(binop<add>(binop<sub>(7,3), 8),2));
assert( boost::apply_visitor(mult_calculator(),result2) == 24 );
std::cout << "Success mult" << std::endl;
}我真的希望这样的注释去掉better_mult_expression来编译(和工作),但是它没有--因为基本计算器访问者中的this指针不引用mult_expression,而是引用表达式。
有没有人建议我克服这个问题,还是我只是在胡闹呢?
发布于 2021-08-04 11:58:08
首先,我建议该变体包括所有可能的节点类型,而不是区分mult和expression。这种区别在AST级别没有意义,只是在解析器阶段(如果您以递归/PEG的方式实现操作符优先)。
除此之外,以下是一些观察结果:
apply_visitor分派封装到您的评估函式中,则可以将代码复制减少一个很大的因素。using将继承的重载拉到范围中进行重载解析,因此这可能是最直接的答案:
住在Coliru
struct better_mult_calculator :计算器{使用计算器::运算符();自动操作符()( const & binop&二进制)const{返回boost::apply_visitor(*this,binary.left) * boost::apply_visitor(*this,binary.right);};进步!
从那份清单开始,让我们刮掉一些噪音!
<functional>标头是标准的:
名称空间AST {模板结构绑定;使用add = binop>;使用sub = binop>;使用mult = binop>;使用expr = boost::variant;使用模板结构{ expr;};} //命名空间AST
现在整个calculator可以是:
结构计算器: boost::static_visitor { int运算符()( int值) const {返回值;}模板int运算符()(AST::binop&二进制) const {返回Op}( boost::apply_visitor(*this,binary.left),boost::apply_visitor(*this,binary.right));};
在这里,您的变体可以添加任意操作,甚至不需要触摸计算器。
现场演示,43行代码double)扩展变量时,它将工作。它甚至可以工作,不管您是否将包含节点类型子集的不兼容变体类型传递给它。operator()作为一个分派函数:全演示
住在Coliru
#include <boost/variant.hpp>
#include <iostream>
namespace AST {
using boost::recursive_wrapper;
template <typename> struct binop;
using add = binop<std::plus<>>;
using sub = binop<std::minus<>>;
using mult = binop<std::multiplies<>>;
using expr = boost::variant<int,
recursive_wrapper<add>,
recursive_wrapper<sub>,
recursive_wrapper<mult>>;
template <typename> struct binop { expr left, right; };
} // namespace AST
struct Calculator {
auto operator()(auto const& v) const { return call(v); }
private:
template <typename... T> int call(boost::variant<T...> const& v) const {
return boost::apply_visitor(*this, v);
}
template <typename T>
int call(T const& lit) const { return lit; }
template <typename Op>
int call(AST::binop<Op> const& bin) const {
return Op{}(call(bin.left), call(bin.right));
}
};
int main()
{
using namespace AST;
std::cout << std::boolalpha;
auto sub_expr = add{sub{7, 3}, 8};
expr e1 = sub_expr;
expr e2 = mult{sub_expr, 2};
Calculator calc;
auto result1 = calc(e1);
std::cout << "result1: " << result1 << " Success? " << (12 == result1) << "\n";
// result2 = ((7-3)+8)*2 = 12
auto result2 = calc(e2);
std::cout << "result2: " << result2 << " Success? " << (24 == result2) << "\n";
}仍印
result1: 12 Success? true
result2: 24 Success? truehttps://stackoverflow.com/questions/68638064
复制相似问题