boost.proto +修改到位前pression树 [英] boost.proto + modify expression tree in place
问题描述
背景问题:建设前pression树之前 boost.proto +检测无效的终端。
我想要实现的是
- 创建一个前pression树,所有的向量取代的副本
他们开始迭代器(对我来说是一个原始指针) - 递增迭代器在
- 在树上解引用迭代器,但是这部分应该是比较容易的。
因此,1。我结束了与此code
////////////////////////////////////// /////////////////////////////////////////
//一个转换,所有的矢量转换节点在树节点迭代器
结构vector_begin:原::变换< vector_begin>
{
模板< typename的Expr的,typename的Unused1,类型名Unused2>
结构IMPL:升压::原:: transform_impl< Expr的,Unused1,Unused2>
{
//必须剥去参考预选赛(安培)
的typedef typename的原:: ::的result_of值LT;
类型名的boost :: remove_reference< Expr的> ::类型
> ::类型vector_type; 的typedef typename的原:: ::的result_of as_expr
< typename的vector_type ::为const_iterator> :: result_type的类型; result_type的运算符()(
类型名IMPL :: expr_param变种
,类型名IMPL :: state_param
,类型名IMPL :: data_param)常量
{
类型名vector_type ::为const_iterator国际热核实验堆(原::值(VAR).begin());
返回原:: as_expr(ITER); //店迭代通过值
}
};
};结构vector_grammar_begin
:原:: or_<
原::当< vector_terminal,vector_begin>
//标量要由值(原以const&放大器将它们存储)存储,如果不是code不编译...
,原::当< scalar_terminal,提振::原:: _ make_terminal(升压::原:: _ BYVAL(升压::原:: _值))>
//下降树转换向量开始()迭代器
,原::当<原:: nary_expr< _,原::可变参数< vector_grammar_begin> > >
>
{};
以上成功创建,所有矢量被替换的指针一棵树。到现在为止还挺好。现在,尝试递增
迭代器。我意识到,是将更好地推进迭代器,所以只有一个变换,我能得到大部分
随机访问迭代器的行为(反引用是其他缺少的部分)。为2,所需要的变换应
////////////////////////////////////// /////////////////////////////////////////
//变换为促进所有迭代器在树上
结构iter_advance:原::变换< iter_advance>
{
模板< typename的Expr的,typename的索引,类型名假>
结构IMPL:升压::原:: transform_impl< Expr的,指数虚>
{
无效的typedef result_type的;
result_type的运算符()(
类型名IMPL :: expr_param变种
,类型名IMPL :: state_param指数//我使用状态来传递数据:(
,类型名IMPL :: data_param)常量
{
原::值(VAR)+ =指数; //没有好的...这里编译出错:(
}
};
};//好吧,这是脆的,如果我决定改变矢量< D,T>的迭代器类型?
结构iter_terminal
:原:: and_<
原::终端LT; _>
,原:: if_<提高:: is_pointer<原:: _值>()>
>
{};
结构vector_grammar_advance
:原:: or_<
原::当< iter_terminal,iter_advance>
,原::终端LT; _>
,原::当<原:: nary_expr< _,原::可变参数< vector_grammar_advance> > >
>
{};
现在,在主函数
模板<类Expr的>
无效check_advance(表达式常量急症)
{
原:: display_expr(E); 的typedef typename的提振::的result_of< vector_grammar_begin(表达式)> ::类型iterator_type;
iterator_type ITER = vector_grammar_begin()(E);
原:: display_expr(ITER); vector_grammar_advance()(ITER,1);
原:: display_expr(ITER);
} INT主(INT,CHAR **)
{
VEC 3;双>一(1),B(2),C(3);
check_advance(2 * A + B / C);
返回0;
}
我收到以下错误消息(过滤掉垃圾):
array.cpp:361:13:错误:分配只读位置
<$p$p><$c$c>'boost::proto::value<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal,提高::原:: argsns _ ::术语LT;常量双*&gt;中0升&GT; &GT;((*放大器; VAR))'
让我困扰的是((*放大器; VAR))的一部分......不明白怎样做才能解决这个问题。
在此先感谢,最诚挚的问候
PS
无关的事:打一点与转换之后,我使用的一般模式是:
- 决定做什么树
- 写原始的变换,它执行操作
- 写语法识别其中变换的应用,可使用pviously定义的$ P $变换
你觉得这是合理的?我的意思是,这是一个很多code的执行只是一个基本的运算单个
样的节点。有了上下文,就可以一次定义多个OPS,去伪存真节点类型。
这是可能的转换也这样做呢?这是将要使用的一般模式
您的直觉是正确的;你应该能够变异树在的地方。似乎有一些常量古怪与原始的 pass_through
变换,我需要调查,因此该解决方案是有点不明显。首先,我定义一些可调用,我会在原算法使用。我preFER可调用本原变换,因为它们是简单的神交,更具重用性,并导致更加容易阅读的原始算法。
结构开始
:原::赎回
{
模板&LT; typename的西格&GT;
结构的结果; 模板&LT; TYPENAME这类型名RNG&GT;
结构的结果&LT;这个(RNG)&GT;
:提高:: range_iterator&LT;&RNG GT;
{}; 模板&LT; TYPENAME这类型名RNG&GT;
结构的结果&LT;这个(RNG&安培;)&GT;
:提高:: range_iterator&LT;&RNG GT;
{}; 模板&LT; typename的RNG&GT;
类型名的boost :: range_iterator&LT;&RNG GT; ::类型
运算符()(RNG&安培; RNG)常量
{
返回的boost ::开始(RNG);
} 模板&LT; typename的RNG&GT;
类型名的boost :: range_iterator&LT; RNG常量&GT; ::类型
运算符()(RNG常量和放大器; RNG)常量
{
返回的boost ::开始(RNG);
}
};推进结构
:原::赎回
{
无效的typedef result_type的; 模板&LT; typename的Iter项目&GT;
void运算符()(ITER&安培;它,无符号D)常量
{
这+ = D;
}
};
现在,我解决您的问题脆性用一个简单的迭代器适配器:
模板&LT; TYPENAME Iter项目&GT;
结构vector_iterator
:提高:: iterator_adaptor&LT; vector_iterator&LT; Iter项目&gt;中Iter项目&GT;
{
vector_iterator()
:提高:: iterator_adaptor&LT; vector_iterator&LT; Iter项目&gt;中Iter项目&GT;()
{} 明确vector_iterator(ITER ITER)
:提高:: iterator_adaptor&LT; vector_iterator&LT; Iter项目&gt;中Iter项目&GT;(ITER)
{} 朋友的std :: ostream的和放大器;运营商LT;≤(的std :: ostream的和放大器; SOUT,vector_iterator吧)
{
返回SOUT&LT;&LT; vector_iterator(价值:&LT;&LT; *它&LT;&LT;);
}
};
这里的算法把含有载体导入包含载体迭代器一棵树一棵树。
//将所有矢量终端到载体迭代器终端
结构vector_begin_algo
:原:: or_&LT;
原::当&LT;
原::终端下;性病::矢量&lt; _,_&GT; &GT;
,原:: _ make_terminal(
vector_iterator&LT;开始(原:: _值)&GT;(开始(原:: _值))
)
&GT;
,原::当&LT;
原::终端LT; _&GT;
,原:: _ make_terminal(原:: _ BYVAL(原:: _值))
&GT;
,原::否则&LT;
原:: _ BYVAL(原:: nary_expr&LT; _,原::可变参数&LT; vector_begin_algo&GT;&GT;)
&GT;
&GT;
{};
最后一个原:: _ BYVAL
应该没有必要。在 pass_through
变换使用原:: nary_expr
不应该创建常量临时节点。我们对此深感抱歉。
这里是算法推进所有的迭代器就地。当你能完全神交这一点,你将真正成为一个原主人。
//就地通过促进所有矢量变异迭代器数量
//在状态参数
结构vector_advance_algo
:原:: or_&LT;
原::当&LT;
原::终端LT; vector_iterator&LT; _&GT; &GT;
,提前(原:: _值,原:: _状态)
&GT;
,原::当&LT;
原::终端LT; _&GT;
,原:: _无效
&GT;
,原::否则&LT;
原:: and_&LT;
原::折叠&LT;
_
,原:: _状态
,原:: and_&LT;
vector_advance_algo
,原:: _状态
&GT;
&GT;
,原:: _无效
&GT;
&GT;
&GT;
{};
诀窍理解上面的是要知道:
-
原:: _无效
不执行任何操作,并返回无效
-
原::和_
,作为转换使用这样的时候,执行所有指定的变换和返回最后的结果。
毕竟,你现在可以做您设置了什么做出来的:将含载体到含有迭代器一树一树,然后推进所有的迭代器就地:
原::文字&LT;的std ::矢量&lt;&INT GT; &GT; VEC 1;
原::值(VEC 1).assign(
提高:: make_counting_iterator(0)
,提振:: make_counting_iterator(16)
);汽车BEG = vector_begin_algo()(2 * VEC 1 + VEC 1);
原:: display_expr(BEG);vector_advance_algo()(BEG,1U);
原:: display_expr(BEG);vector_advance_algo()(BEG,1U);
原:: display_expr(BEG);
我觉得你的code会工作过,你不会遇到的怪事常量。另外,我觉得如果你写普通可调用,而不是原始的变换你可能有它的一个更容易的时间。
希望这有助于。
Background question: boost.proto + detect invalid terminal before building the expression tree.
Hi, what i'm trying to achieve is
- create a copy of an expression tree, where all vectors are substituted with their begin iterators (in my case is a raw pointer)
- increment the iterators in place
- dereference iterators in the tree, but that part should be relatively easy.
So, for 1. I ended up with this code
///////////////////////////////////////////////////////////////////////////////
// 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> > >
>
{};
The above succeeds to create a tree where all vectors are replaced by pointers. So far, so good. Now, try to increment iterators. I realized that is would be better to advance iterators, so with just one transform, i could get most of the behavior of a random access iterator (dereference is the other missing piece). For 2., the required transform should be
///////////////////////////////////////////////////////////////////////////////
// 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> > >
>
{};
Now, in the main function
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;
}
I get the following error message (filtered out the junk):
array.cpp:361:13: error: assignment of read-only location
'boost::proto::value<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal,
boost::proto::argsns_::term<const double*>, 0l> >((* & var))'
What bothers me is the '((* & var))' part... cannot understand what to do to fix this. Thanks in advance, best regards
PS Unrelated thing: after playing a little with transforms, the general pattern i'm using is:
- Decide what to do to the tree
- Write a primitive transform that performs the operation
- Write a grammar that recognizes where the transform should be applied, use the previously defined transform
Do you think this is reasonable? I mean, it is a lot of code to perform just an elementary op to a single kind of node. With contexts, it is possible to define several ops at once, discriminating on the node type. It is possible to do this with transforms also ? What is the general pattern to be used?
Your intuition is correct; you should be able to mutate the tree in-place. There seems to be some const weirdness with Proto's pass_through
transform that I need to investigate, so the solution is a little non-obvious. First, I define some callables that I will use in the Proto algorithms. I prefer callables to primitive transforms because they are simpler to grok, more reusable, and result in easier-to-read Proto algorithms.
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;
}
};
Now, I solve your brittleness problem with a simple iterator adaptor:
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 << " )";
}
};
Here's the algorithm to turn a tree containing vectors into a tree containing vector iterators.
// 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> >)
>
>
{};
The last proto::_byval
shouldn't be needed. The pass_through
transform used by proto::nary_expr
shouldn't be creating const temporary nodes. Sorry about that.
And here is the algorithm to advance all the iterators in-place. When you can fully grok this, you will truly be a Proto master.
// 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
>
>
>
{};
The trick to understanding the above is knowing:
proto::_void
does nothing and returnsvoid
proto::and_
, when used as a transform like this, executes all the specified transforms and returns the result of the last.
After all that, you can now do what you had set out to do: Turn a tree containing vectors into a tree containing iterators, and then advance all the iterators in-place:
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);
I think your code would have worked had you not run into the const weirdness. Also, I think you might have an easier time of it if you write ordinary callables instead of primitive transforms.
Hope this helps.
这篇关于boost.proto +修改到位前pression树的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!