gpt4 book ai didi

c++ - boost.proto + 就地修改表达式树

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:35:58 27 4
gpt4 key购买 nike

背景问题:boost.proto + detect invalid terminal before building the expression tree .

你好,我想实现的是

  1. 创建表达式树的拷贝,其中所有 vector 都替换为他们的开始迭代器(在我的例子中是一个原始指针)
  2. 就地增加迭代器
  3. 解引用树中的迭代器,但这部分应该相对容易。

所以,对于 1。我最终得到了这段代码

///////////////////////////////////////////////////////////////////////////////
// A transform that converts all vectors nodes in a tree to iterator nodes
struct vector_begin : proto::transform <vector_begin>
{
template<typename Expr, typename Unused1, typename Unused2>
struct impl : boost::proto::transform_impl<Expr, Unused1, Unused2>
{
// must strip away the reference qualifier (&)
typedef typename proto::result_of::value<
typename boost::remove_reference<Expr>::type
>::type vector_type;

typedef typename proto::result_of::as_expr
<typename vector_type::const_iterator>::type result_type;

result_type operator ()(
typename impl::expr_param var
, typename impl::state_param
, typename impl::data_param) const
{
typename vector_type::const_iterator iter(proto::value(var).begin());
return proto::as_expr(iter); // store iterator by value
}
};
};

struct vector_grammar_begin
: proto::or_ <
proto::when <vector_terminal, vector_begin>
// scalars want to be stored by value (proto stores them by const &), if not the code does not compile...
, proto::when <scalar_terminal, boost::proto::_make_terminal(boost::proto::_byval(boost::proto::_value))>
// descend the tree converting vectors to begin() iterators
, proto::when <proto::nary_expr<_, proto::vararg<vector_grammar_begin> > >
>
{};

上面的代码成功地创建了一棵树,其中所有的 vector 都被指针替换了。到目前为止,一切都很好。现在,尝试增加迭代器。我意识到推进迭代器会更好,所以只需一次转换,我就可以获得大部分随机访问迭代器的行为(取消引用是另一个缺失的部分)。对于2.,所需的转换应该是

///////////////////////////////////////////////////////////////////////////////
// A transform that advances all iterators in a tree
struct iter_advance : proto::transform <iter_advance>
{
template<typename Expr, typename Index, typename Dummy>
struct impl : boost::proto::transform_impl<Expr, Index, Dummy>
{
typedef void result_type;
result_type operator ()(
typename impl::expr_param var
, typename impl::state_param index // i'm using state to pass a data :(
, typename impl::data_param) const
{
proto::value(var)+=index; // No good... compile error here :(
}
};
};

// Ok, this is brittle, what if I decide the change vector<D,T>'s iterator type ?
struct iter_terminal
: proto::and_<
proto::terminal<_>
, proto::if_<boost::is_pointer<proto::_value>()>
>
{};


struct vector_grammar_advance
: proto::or_ <
proto::when <iter_terminal, iter_advance>
, proto::terminal<_>
, proto::when <proto::nary_expr<_, proto::vararg<vector_grammar_advance> > >
>
{};

现在,在主函数中

template <class Expr>
void check_advance (Expr const &e)
{
proto::display_expr (e);

typedef typename boost::result_of<vector_grammar_begin(Expr)>::type iterator_type;
iterator_type iter = vector_grammar_begin()(e);
proto::display_expr (iter);

vector_grammar_advance ()(iter,1);
proto::display_expr (iter);
}

int main (int, char**)
{
vec<3, double> a(1), b(2), c(3);
check_advance(2*a+b/c);
return 0;
}

我收到以下错误信息(过滤掉垃圾信息):

array.cpp:361:13: 错误:分配只读位置

'boost::proto::value<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal,
boost::proto::argsns_::term<const double*>, 0l> >((* & var))'

令我困扰的是“((* & var))”部分……无法理解如何解决此问题。提前致谢,致以最诚挚的问候

附言不相关的事情:在玩了一些变换之后,我使用的一般模式是:

  1. 决定如何处理这棵树
  2. 编写一个执行操作的原始转换
  3. 编写一个语法来识别应该在哪里应用转换,使用之前定义的转换

你觉得这样合理吗?我的意思是,仅对单个对象执行基本操作需要大量代码一种节点。使用上下文,可以一次定义多个操作,区分节点类型。也可以用转换来做到这一点吗?要使用的一般模式是什么?

最佳答案

您的直觉是正确的;你应该能够就地改变树。我需要调查 Proto 的 pass_through 转换似乎有些 const 怪异,因此解决方案有点不明显。首先,我定义了一些我将在 Proto 算法中使用的可调用对象。与原始转换相比,我更喜欢可调用对象,因为它们更易于理解、更可重用,并且生成更易于阅读的 Proto 算法。

struct begin
: proto::callable
{
template<typename Sig>
struct result;

template<typename This, typename Rng>
struct result<This(Rng)>
: boost::range_iterator<Rng>
{};

template<typename This, typename Rng>
struct result<This(Rng &)>
: boost::range_iterator<Rng>
{};

template<typename Rng>
typename boost::range_iterator<Rng>::type
operator()(Rng &rng) const
{
return boost::begin(rng);
}

template<typename Rng>
typename boost::range_iterator<Rng const>::type
operator()(Rng const &rng) const
{
return boost::begin(rng);
}
};

struct advance
: proto::callable
{
typedef void result_type;

template<typename Iter>
void operator()(Iter &it, unsigned d) const
{
it += d;
}
};

现在,我用一个简单的迭代器适配器解决了你的脆弱性问题:

template<typename Iter>
struct vector_iterator
: boost::iterator_adaptor<vector_iterator<Iter>, Iter>
{
vector_iterator()
: boost::iterator_adaptor<vector_iterator<Iter>, Iter>()
{}

explicit vector_iterator(Iter iter)
: boost::iterator_adaptor<vector_iterator<Iter>, Iter>(iter)
{}

friend std::ostream &operator<<(std::ostream &sout, vector_iterator it)
{
return sout << "vector_iterator(value: " << *it << " )";
}
};

下面是将包含 vector 的树转换为包含 vector 迭代器的树的算法。

// Turn all vector terminals into vector iterator terminals
struct vector_begin_algo
: proto::or_<
proto::when<
proto::terminal<std::vector<_, _> >
, proto::_make_terminal(
vector_iterator<begin(proto::_value)>(begin(proto::_value))
)
>
, proto::when<
proto::terminal<_>
, proto::_make_terminal(proto::_byval(proto::_value))
>
, proto::otherwise<
proto::_byval(proto::nary_expr<_, proto::vararg<vector_begin_algo> >)
>
>
{};

最后一个 proto::_byval 应该是不需要的。 proto::nary_expr 使用的 pass_through 转换不应创建 const 临时节点。对此感到抱歉。

这是就地推进所有迭代器的算法。当你完全理解这一点时,你将成为真正的 Proto 大师。

// Mutate in-place by advancing all vector iterators the amount
// in the state parameter
struct vector_advance_algo
: proto::or_<
proto::when<
proto::terminal<vector_iterator<_> >
, advance(proto::_value, proto::_state)
>
, proto::when<
proto::terminal<_>
, proto::_void
>
, proto::otherwise<
proto::and_<
proto::fold<
_
, proto::_state
, proto::and_<
vector_advance_algo
, proto::_state
>
>
, proto::_void
>
>
>
{};

理解上述内容的诀窍是知道:

  1. proto::_void 什么都不做并返回 void
  2. proto::and_,当像这样用作转换时,执行所有指定的转换并返回最后的结果。

之后,您现在可以做您打算做的事情:将包含 vector 的树变成包含迭代器的树,然后就地推进所有迭代器:

proto::literal<std::vector<int> > vec1;
proto::value(vec1).assign(
boost::make_counting_iterator(0)
, boost::make_counting_iterator(16)
);

auto beg = vector_begin_algo()(2 * vec1 + vec1);
proto::display_expr(beg);

vector_advance_algo()(beg, 1u);
proto::display_expr(beg);

vector_advance_algo()(beg, 1u);
proto::display_expr(beg);

我认为如果您没有遇到常量问题,您的代码就可以正常工作。另外,我认为如果您编写普通的可调用对象而不是原始转换,您可能会更轻松。

希望这对您有所帮助。

关于c++ - boost.proto + 就地修改表达式树,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12122191/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com