[Proto] Copying expressions

Hi I'm creating lambda expressions using proto, using the extension mechanism to add my own operator() so that I can do: (_0 += _1)(x,y) The operator() does all the transforming of the expression tree, and evaluates the result. The above works ok, its pretty similar to your calculator examples. If I want to do something like function<int(int&,int&)> f(_0 + _1); // Copy lambda expression into a Boost.Function func Now I've got problems because proto trees are built out of references (to temporaries). So func will blow up later when the temporaries are off the stack (actually the example I've tried starts evaluating junk values). I believe the above code will work today with BLL and Phoenix, as the temporaries are copied rather than handled as references. As I cannot interfere with the Boost.Function constructors, how can I transform my expression tree to change the refs to copies, whilst retaining the syntax above? Is there some way to tell proto to build trees with copies? Or have I missed something clever in proto again :) It might make a good Calc4 example as I imagine this is a common requirement. Cheers Dan ___________________________________________________________ Yahoo! Mail is the world's favourite email. Don't settle for less, sign up for your free account today http://uk.rd.yahoo.com/evt=44106/*http://uk.docs.yahoo.com/mail/winter07.htm...

bitten by this in the past. There's a proto::deep_copy function that you can use for converting references to values. Regards, Maurizio On Jul 17, 2007, at 2:54 AM, dan marsden wrote:
Hi
I'm creating lambda expressions using proto, using the extension mechanism to add my own operator() so that I can do:
(_0 += _1)(x,y)
The operator() does all the transforming of the expression tree, and evaluates the result. The above works ok, its pretty similar to your calculator examples.
If I want to do something like
function<int(int&,int&)> f(_0 + _1); // Copy lambda expression into a Boost.Function func
Now I've got problems because proto trees are built out of references (to temporaries). So func will blow up later when the temporaries are off the stack (actually the example I've tried starts evaluating junk values). I believe the above code will work today with BLL and Phoenix, as the temporaries are copied rather than handled as references.
As I cannot interfere with the Boost.Function constructors, how can I transform my expression tree to change the refs to copies, whilst retaining the syntax above? Is there some way to tell proto to build trees with copies? Or have I missed something clever in proto again :) It might make a good Calc4 example as I imagine this is a common requirement.
Cheers
Dan
___________________________________________________________ Yahoo! Mail is the world's favourite email. Don't settle for less, sign up for your free account today http://uk.rd.yahoo.com/evt=44106/*http:// uk.docs.yahoo.com/mail/winter07.html _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/ listinfo.cgi/boost

Maurizio Vitale wrote:
On Jul 17, 2007, at 2:54 AM, dan marsden wrote:
function<int(int&,int&)> f(_0 + _1); // Copy lambda expression into a Boost.Function func
Now I've got problems because proto trees are built out of references (to temporaries). So func will blow up later when the temporaries are off the stack
bitten by this in the past. There's a proto::deep_copy function that you can use for converting references to values.
Maurizio, deep_copy() alone will not work here. The user is creating a lambda expression and assigning it directly to function<>: function<int(int&,int&)> f(_0 + _1); function<> knows nothing about proto expressions, and doesn't know to call proto::deep_copy(). Dan, this is a job for a custom generator. (Thanks, I haven't had need of one until now! This'll make a great example.) You're already using a generator to wrap your expressions. But you also need a deep_copy-ing generator. Here's the basic idea ... #include <iostream> #include <boost/xpressive/proto/proto.hpp> using namespace boost::proto; template<typename Expr> struct lambda_expr; struct lambda_generator { template<typename Expr> struct apply { typedef lambda_expr< typename result_of::deep_copy<Expr>::type > type; }; template<typename Expr> static typename apply<Expr>::type make(Expr const &expr) { typename apply<Expr>::type that = {deep_copy(expr)}; return that; } }; struct lambda_domain : domain<lambda_generator> {}; template<typename Expr> struct lambda_expr { BOOST_PROTO_EXTENDS(Expr, lambda_expr<Expr>, lambda_domain) BOOST_PROTO_EXTENDS_ASSIGN(Expr, lambda_expr<Expr>, lambda_domain) BOOST_PROTO_EXTENDS_SUBSCRIPT(Expr, lambda_expr<Expr>, lambda_domain) // Implement operator() here. }; struct one_tag {}; lambda_expr<terminal<one_tag>::type> const _1 = {{{}}}; int main() { _1 + 42; std::cout << typeid(_1+42).name() << std::endl; return 0; } Of course, calling deep_copy() in the generator is sub-optimal. Every part of the tree has been deep_copied already except the top-most node. When I find time, I'll write a by_value_generator which makes sure everything in the tree is held by value. (I'm using the BOOST_PROTO_EXTENDS macros here instead of proto::extends<> so that _1 can be statically initialized. You may want to do something similar, if you aren't already.) HTH, -- Eric Niebler Boost Consulting www.boost-consulting.com The Astoria Seminar ==> http://www.astoriaseminar.com

Eric Niebler <eric@boost-consulting.com> writes:
Maurizio, deep_copy() alone will not work here. The user is creating a lambda expression and assigning it directly to function<>:
function<int(int&,int&)> f(_0 + _1);
function<> knows nothing about proto expressions, and doesn't know to call proto::deep_copy().
Rats! (apologizes to Dan, I didn't intend to mislead) But now I'd appreciate you entering "grade school mode" and do some explaining. My mental model was: - proto::deep_copy traverse the expression top-down replacing references with value. - generators are used to wrap expressions as they are built bottom-up So, assuming that the expression is properly wrapped in lambda_expr<> why the result of calling proto::expr on the whole expression would be different from using the generator? And why this would tell function<> more about proto expressions? What am I missing? Thanks, Maurizio

Maurizio Vitale wrote:
Eric Niebler <eric@boost-consulting.com> writes:
Maurizio, deep_copy() alone will not work here. The user is creating a lambda expression and assigning it directly to function<>:
function<int(int&,int&)> f(_0 + _1);
function<> knows nothing about proto expressions, and doesn't know to call proto::deep_copy().
Rats! (apologizes to Dan, I didn't intend to mislead)
But now I'd appreciate you entering "grade school mode" and do some explaining.
My mental model was: - proto::deep_copy traverse the expression top-down replacing references with value.
Actually, bottom-up, but yes references are replaced with values.
- generators are used to wrap expressions as they are built bottom-up
Generators are used whenever you want to customize the result of proto's operator overloads.
So, assuming that the expression is properly wrapped in lambda_expr<> why the result of calling proto::expr on the whole expression would be different from using the generator?
Sorry, I don't understand the question.
And why this would tell function<> more about proto expressions?
It doesn't.
What am I missing?
Maybe you're not clear on how generators work. (Not surprising, it isn't documented yet.) By default, proto's operator overloads create proto::expr<> objects in which everything (terminals and non-terminals) are held by reference. Generators give you a hook where you can customize the operators' return types. The operator overload passes the generator the new node (bare proto::expr<>, sub-nodes held by reference) and the generator is free to do whatever it wants with it. It can wrap it in a domain-specific wrapper, deep_copy it, or whatever. How this helps is that as phoenix expressions are being built, the phoenix generator will ensure that all the nodes are held by-value. There will never be a need for the user to deep_copy phoenix expressions explicitly -- it's done piecemeal by phoenix. So function<> is passed an object that does not need to be deep-copied. HTH, -- Eric Niebler Boost Consulting www.boost-consulting.com The Astoria Seminar ==> http://www.astoriaseminar.com

Eric Niebler <eric@boost-consulting.com> writes:
Maurizio Vitale wrote: <snip>
My mental model was: - proto::deep_copy traverse the expression top-down replacing references with value.
Actually, bottom-up, but yes references are replaced with values.
It is probably terminology, but the following seems top-down to me: you're asked to deep-copy an expression and you assemble an expression which has the same shape (tag and argcount) of the original and as children the result of recursive evaluations of deep-copy on the children. #define N BOOST_PP_ITERATION() template<typename Expr> struct deep_copy_impl<Expr, N> { typedef expr<typename Expr::proto_tag, BOOST_PP_CAT(args, N)< BOOST_PP_ENUM(N, BOOST_PROTO_DEFINE_DEEP_COPY_TYPE, ~) > > expr_type; typedef typename Expr::proto_domain::template apply<expr_type>::type type; template<typename Expr2> static type call(Expr2 const &expr) { expr_type that = { BOOST_PP_ENUM(N, BOOST_PROTO_DEFINE_DEEP_COPY_FUN, ~) }; return Expr::proto_domain::make(that); } };
- generators are used to wrap expressions as they are built bottom-up
Generators are used whenever you want to customize the result of proto's operator overloads.
Yes, and my reading of the behaviour of the specific generator you wrote is that everytime you construct an expression you evaluate deep_copy on it. Modulo the number of evaluations of deep_copy, I still don't see any difference between using deep_copy on the toplevel expression and using the generator for building an already deep-copied solution (other than, as you point out later in one case you have to explicitely call deep_copy while in the other expressions naturally spring to life without references)
So, assuming that the expression is properly wrapped in lambda_expr<> why the result of calling proto::expr on the whole expression would be different from using the generator?
Sorry, I don't understand the question.
I guess my question is what is the difference between: struct lambda_domain : domain<generator<lambda_expression> > {}; auto expr = deep_copy(_1 + 42); and: auto expr_1 = _1 + 42; in the case where the generator you provided is used Aren't expr and expr_1 exactly the same?
How this helps is that as phoenix expressions are being built, the phoenix generator will ensure that all the nodes are held by-value. There will never be a need for the user to deep_copy phoenix expressions explicitly -- it's done piecemeal by phoenix. So function<> is passed an object that does not need to be deep-copied.
I see this difference now and understand the reasons behind your suggestion. Thanks for the explanation, Maurizio

Maurizio Vitale wrote:
Eric Niebler <eric@boost-consulting.com> writes:
Maurizio Vitale wrote: <snip>
My mental model was: - proto::deep_copy traverse the expression top-down replacing references with value. Actually, bottom-up, but yes references are replaced with values.
It is probably terminology, but the following seems top-down to me: you're asked to deep-copy an expression and you assemble an expression which has the same shape (tag and argcount) of the original and as children the result of recursive evaluations of deep-copy on the children.
Just terminology. You recurse all the way down to the leaves, deep-copy them and store the results by-value in new parent nodes, all the way back up to the root. So the first deep-copy is done to the leaves -- hence bottom-up. <snip>
I guess my question is what is the difference between:
struct lambda_domain : domain<generator<lambda_expression> > {}; auto expr = deep_copy(_1 + 42);
and:
auto expr_1 = _1 + 42; in the case where the generator you provided is used
Aren't expr and expr_1 exactly the same?
Exactly the same, yes.
How this helps is that as phoenix expressions are being built, the phoenix generator will ensure that all the nodes are held by-value. There will never be a need for the user to deep_copy phoenix expressions explicitly -- it's done piecemeal by phoenix. So function<> is passed an object that does not need to be deep-copied.
I see this difference now and understand the reasons behind your suggestion. Thanks for the explanation,
No problem. -- Eric Niebler Boost Consulting www.boost-consulting.com The Astoria Seminar ==> http://www.astoriaseminar.com
participants (4)
-
dan marsden
-
Eric Niebler
-
Maurizio Vitale
-
Maurizio Vitale