[Proto] Forwarding evaluation via tag

I have the following grammar that handles the evaluation of various matrix classes: struct eval_xpr : bp::or_< bp::when< bp::terminal< block<bp::_,bp::_> > , _load(bp::_) > , bp::when< bp::plus< eval_xpr,eval_xpr > , bp::call< functor<bp::tag::plus>(eval_xpr(bp::_left ), eval_xpr(bp::_right) ) > > > {}; As you can see, the functor<T> class is a Callable object but not a primitive Transform so I use call to be able to forward the proper things to it. Currently I only play with + but I wanted to know how i can use proto::tag_of and a clever way to conserve the recursive look so it works for any operator of any tag. Basically what should I do to have : bp::when< bp::nary_expr< bp::_, bp::vararg< eval_xpr > > , bp::call< functor< ???? >( eval_xpr(????), ???, ??? ) > > to work for any tag and any number of parameters while typiong the less code possible. I *think* I should make a new Primitive Transform that extarct tags and arguments from bp::_expr and then perform the call to functor but doesn't this mean I have to explicitly write the code for 1,2,3 etc arguments ?

Joel Falcou wrote:
I have the following grammar that handles the evaluation of various matrix classes:
struct eval_xpr : bp::or_< bp::when< bp::terminal< block<bp::_,bp::_> > , _load(bp::_) > , bp::when< bp::plus< eval_xpr,eval_xpr > , bp::call< functor<bp::tag::plus>(eval_xpr(bp::_left ), eval_xpr(bp::_right) ) > > > {};
As you can see, the functor<T> class is a Callable object but not a primitive Transform so I use call to be able to forward the proper things to it. Currently I only play with + but I wanted to know how i can use proto::tag_of and a clever way to conserve the recursive look so it works for any operator of any tag. Basically what should I do to have :
bp::when< bp::nary_expr< bp::_, bp::vararg< eval_xpr > > , bp::call< functor< ???? >( eval_xpr(????), ???, ??? ) > >
to work for any tag and any number of parameters while typiong the less code possible. I *think* I should make a new Primitive Transform that extarct tags and arguments from bp::_expr and then perform the call to functor but doesn't this mean I have to explicitly write the code for 1,2,3 etc arguments ?
I don't have a good solution at my fingertips or the time to come up with something really whiz-bang, but you can get part of the way with the proto::lazy transform. For instance, you can handle all binary expressions with: bp::when< bp::nary_expr< bp::_, eval_xpr, eval_xpr > , bp::lazy< functor< bp::tag_of< bp::_ > >( eval_xpr(bp::_left) , eval_xpr(bp::_right) )> > This uses proto::make to turn functor< bp::tag_of< bp::_ > > into, e.g., function< proto::tag::plus > and then uses proto::call to invoke it. Hope that moves you in the right direction, -- Eric Niebler BoostPro Computing http://www.boostpro.com

Eric Niebler wrote:
I don't have a good solution at my fingertips or the time to come up with something really whiz-bang, but you can get part of the way with the proto::lazy transform. For instance, you can handle all binary expressions with:
bp::when< bp::nary_expr< bp::_, eval_xpr, eval_xpr > , bp::lazy< functor< bp::tag_of< bp::_ > >( eval_xpr(bp::_left) , eval_xpr(bp::_right) )> >
This uses proto::make to turn functor< bp::tag_of< bp::_ > > into, e.g., function< proto::tag::plus > and then uses proto::call to invoke it.
Hope that moves you in the right direction, Aaaah bp::lazy ! I was indeed trying to find such thing like that.Maybe I can combine this with some fusion::fused or unfused somehow. I'll give this a try.
-- ___________________________________________ Joel Falcou - Assistant Professor PARALL Team - LRI - Universite Paris Sud XI Tel : (+33)1 69 15 66 35

joel wrote:
Eric Niebler wrote:
I don't have a good solution at my fingertips or the time to come up with something really whiz-bang, but you can get part of the way with the proto::lazy transform. For instance, you can handle all binary expressions with:
bp::when< bp::nary_expr< bp::_, eval_xpr, eval_xpr > , bp::lazy< functor< bp::tag_of< bp::_ > >( eval_xpr(bp::_left) , eval_xpr(bp::_right) )> >
This uses proto::make to turn functor< bp::tag_of< bp::_ > > into, e.g., function< proto::tag::plus > and then uses proto::call to invoke it.
Hope that moves you in the right direction,
Aaaah bp::lazy ! I was indeed trying to find such thing like that.Maybe I can combine this with some fusion::fused or unfused somehow. I'll give this a try.
Also look into proto::functional::unpack_expr, which turns a Fusion sequence into a proto expression node. For instance, you might turn an N-ary expression into an (N+1)-ary expression representing a function invocation like (untested) ... when< nary_expr< _, vararg<eval_expr> > , function< vararg<eval_expr> >( // #1 functional::unpack_expr<tag::function>( // #2 push_front(_, terminal<functor<tag_of<_> > >()) // #3 ) ) > The idea here is to create an expression that, when evaluated by proto::function's pass-through transform (#1), does the Right Thing. You can accomplish that by first building a new expression from the old that (a) has an extra argument in the 0th position that is a terminal containing your function object, and (b) has a tag type of tag::function. First you build a new sequence with push_front (#3), and then you unpack that Fusion sequence into a new expression (#2). push_front doesn't exist yet; you'll have to write it. Just create a callable function object that does what fusion::push_front does. HTH, -- Eric Niebler BoostPro Computing http://www.boostpro.com

Eric Niebler wrote:
Also look into proto::functional::unpack_expr, which turns a Fusion sequence into a proto expression node. For instance, you might turn an N-ary expression into an (N+1)-ary expression representing a function invocation like (untested) ...
when< nary_expr< _, vararg<eval_expr> > , function< vararg<eval_expr> >( // #1 functional::unpack_expr<tag::function>( // #2 push_front(_, terminal<functor<tag_of<_> > >()) // #3 ) ) >
The idea here is to create an expression that, when evaluated by proto::function's pass-through transform (#1), does the Right Thing. You can accomplish that by first building a new expression from the old that (a) has an extra argument in the 0th position that is a terminal containing your function object, and (b) has a tag type of tag::function. First you build a new sequence with push_front (#3), and then you unpack that Fusion sequence into a new expression (#2).
push_front doesn't exist yet; you'll have to write it. Just create a callable function object that does what fusion::push_front does. OK, I like the idea. I tried to get it working but I stumple against the fatc that proto make_expr takes push_front() as a Sequence and don't try to evaulate it before hand, leading to a "no size for psuh_front" erro in fusion. I'll try to get this sorted though.
My main question is that, when and when not a transform call get evaluated ? Like if i do foo( bar ( _) ) is bar(_) evaluated before being evaluated by foo ? Sometimes it seems it does and sometimes no :/ -- ___________________________________________ Joel Falcou - Assistant Professor PARALL Team - LRI - Universite Paris Sud XI Tel : (+33)1 69 15 66 35

joel wrote:
Eric Niebler wrote:
Also look into proto::functional::unpack_expr, <snip>
OK, I like the idea. I tried to get it working but I stumple against the fatc that proto make_expr takes push_front() as a Sequence and don't try to evaulate it before hand, leading to a "no size for psuh_front" erro in fusion. I'll try to get this sorted though.
My main question is that, when and when not a transform call get evaluated ? Like if i do foo( bar ( _) )
is bar(_) evaluated before being evaluated by foo ? Sometimes it seems it does and sometimes no :/
You'll have to post your code ... I can't tell what problem you might be running into. -- Eric Niebler BoostPro Computing http://www.boostpro.com

You'll have to post your code ... I can't tell what problem you might be running into. Well, I decided to be a coward for today. I'll shelf this functional solution for later. I just added my own ternary_expr & quaternary_expr class for the moment as it seems it's all the client wants. I'll come back when I've digested those infos as I htink I'm just doing a small error.
For reference here is the code I wrote: namespace bp = boost::proto; struct push_front_ { template<class Sig> struct result; template<class This,class Seq,class Elem> struct result<This(Seq,Elem)> { typedef typename boost::fusion::result_of::push_front<Seq,Elem>::type type; }; template<class Seq,class Elem> typename result<push_front_(Seq,Elem)>::type operator()(Elem const& e, Seq& s ) const { return boost::fusion::push_front(s,e); } }; struct _load : bp::transform< _load > { template<typename Expr, typename State, typename Data> struct impl : bp::transform_impl<Expr, State, Data> { typedef typename meta::strip<Expr>::type::value_type result_type; result_type operator()( typename impl::expr_param expr , typename impl::state_param , typename impl::data_param data ) const { return expr(data); } }; }; struct eval_xpr : bp::or_< bp::when< bp::terminal< block<bp::_,bp::_> > , _load(bp::_) > , bp::when< bp::nary_expr< bp::_, bp::vararg<eval_xpr> > , bp::functional::unpack_expr<bp::tag::function>( push_front_(bp::_, bp::terminal<functor<bp::tag_of<bp::_> > >() ) ) ) > > {}; -- ___________________________________________ Joel Falcou - Assistant Professor PARALL Team - LRI - Universite Paris Sud XI Tel : (+33)1 69 15 66 35

joel wrote:
You'll have to post your code ... I can't tell what problem you might be running into. Well, I decided to be a coward for today. I'll shelf this functional solution for later. I just added my own ternary_expr & quaternary_expr class for the moment as it seems it's all the client wants. I'll come back when I've digested those infos as I htink I'm just doing a small error.
For reference here is the code I wrote:
namespace bp = boost::proto;
struct push_front_
Must inherit from bp::callable.
{ template<class Sig> struct result; template<class This,class Seq,class Elem> struct result<This(Seq,Elem)> { typedef typename boost::fusion::result_of::push_front<Seq,Elem>::type type; };
template<class Seq,class Elem> typename result<push_front_(Seq,Elem)>::type operator()(Elem const& e, Seq& s ) const
Sequence and Element argument order is reversed here.
{ return boost::fusion::push_front(s,e); } };
struct _load : bp::transform< _load > { template<typename Expr, typename State, typename Data> struct impl : bp::transform_impl<Expr, State, Data> { typedef typename meta::strip<Expr>::type::value_type result_type;
result_type operator()( typename impl::expr_param expr , typename impl::state_param , typename impl::data_param data ) const { return expr(data); } }; };
struct eval_xpr : bp::or_< bp::when< bp::terminal< block<bp::_,bp::_> > , _load(bp::_) > , bp::when< bp::nary_expr< bp::_, bp::vararg<eval_xpr> > , bp::functional::unpack_expr<bp::tag::function>(
This will create the new expression but not evaluate it. I had incorrectly told you to use bp::function<> here for its pass-through transform, but that's not actually what you want. You want Proto's _default transform.
push_front_(bp::_, bp::terminal<functor<bp::tag_of<bp::_> > >() )
) ) > > {};
Here is some code that should get you going again: #include <boost/proto/core.hpp> #include <boost/proto/transform.hpp> #include <boost/fusion/include/push_front.hpp> namespace bp = boost::proto; struct push_front : bp::callable { template<class Sig> struct result; template<class This, class Seq, class Elem> struct result<This(Seq, Elem)> : boost::fusion::result_of::push_front< typename boost::remove_reference<Seq>::type const , typename boost::remove_reference<Elem>::type > {}; template<class Seq, class Elem> typename result<push_front(Seq const, Elem)>::type operator()(Seq const& s, Elem const& e) const { return boost::fusion::push_front(s, e); } }; template<typename T> struct functor; template<> struct functor<bp::tag::plus> { typedef int result_type; int operator()(int x, int y) const { return x + y; } }; struct eval_xpr : bp::or_< bp::when< bp::terminal< bp::_ > , bp::_value > , bp::when< bp::nary_expr< bp::_, bp::vararg<eval_xpr> > , bp::_default<eval_xpr>( bp::functional::unpack_expr<bp::tag::function>( push_front( bp::_ , bp::_make_terminal(functor<bp::tag_of<bp::_> >()) ) ) ) > > {}; int main() { bp::literal<int> x(1); bp::literal<int> y(2); int z = eval_xpr()(x + y); } Hope that helps, -- Eric Niebler BoostPro Computing http://www.boostpro.com

Eric Niebler wrote:
Hope that helps, It did ;)
So everything is working right now. Thanks again. Samll question, for a given eval_xpr, if I do : eval_xpr()(x+y, state, data); it seems that the operator()(Expr,State,Data) of the transform require the Expr to be a reference and so, the former call fails as x+y is a temporary. Is it on, purpose or is it a missing feature ?

Joel Falcou wrote:
Samll question, for a given eval_xpr, if I do :
eval_xpr()(x+y, state, data);
it seems that the operator()(Expr,State,Data) of the transform require the Expr to be a reference and so, the former call fails as x+y is a temporary. Is it on, purpose or is it a missing feature ?
eval_xpr gets an operator() overload defined by proto::transform that looks like this: template<typename Expr, typename State, typename Data> .... operator ()(Expr &e, State &s, Data &d) const ----------------^^^^^^^ You are observing that the "Expr &e" is a non-const ref, and ordinary rvalues do not bind to it. But the rvalues that Proto generates are all const-qualified, which *do* bind to this reference. Notice that I used "eval_xpr()(x + y)" in the example I sent around yesterday, and it works there. So the question is: why doesn't this work for you? Have you defined your own operator+ overload? Could you send your code around again? -- Eric Niebler BoostPro Computing http://www.boostpro.com
participants (3)
-
Eric Niebler
-
joel
-
Joel Falcou