
I just made a change to proto in the effort to reduce compile times. There's a new expression type, proto::basic_expr, that is lighter weight than proto::expr in that it doesn't define the operator=, operator[] and operator() member functions. It's ideal as a base when extending expressions with proto::extends or BOOST_PROTO_EXTENDS. You'll get basic_expr instead of expr automatically if you use either of proto::generator or proto::pod_generator. That could surprise you if you were expecting to get an expr, so beware. Most normal uses of Proto will just work with this change, though, and if you measure you should see a modest improvement in your compile times. Please let me know if this breaks your code. -- Eric Niebler BoostPro Computing http://www.boostpro.com

Eric Niebler wrote:
I just made a change to proto in the effort to reduce compile times. There's a new expression type, proto::basic_expr, that is lighter weight than proto::expr in that it doesn't define the operator=, operator[] and operator() member functions. It's ideal as a base when extending expressions with proto::extends or BOOST_PROTO_EXTENDS.
Nice idea !
You'll get basic_expr instead of expr automatically if you use either of proto::generator or proto::pod_generator. That could surprise you if you were expecting to get an expr, so beware. Most normal uses of Proto will just work with this change, though, and if you measure you should see a modest improvement in your compile times What's the protocol to get the old expr back ? Should we write a custom generator or is there any already ?

On 6/9/2010 2:05 AM, joel falcou wrote:
Eric Niebler wrote:
You'll get basic_expr instead of expr automatically if you use either of proto::generator or proto::pod_generator. That could surprise you if you were expecting to get an expr, so beware. Most normal uses of Proto will just work with this change, though, and if you measure you should see a modest improvement in your compile times
What's the protocol to get the old expr back ? Should we write a custom generator or is there any already ?
I'll add one. Can you say why you want it? -- Eric Niebler BoostPro Computing http://www.boostpro.com

Eric Niebler wrote:
I'll add one. Can you say why you want it?
I think I may have missed soem details but basically I need my expression to overload assignment to generate a expr<assign,..> or is inheritng from basic_expr AND use BOOST_PROTO_EXTENDS_ASSIGN enough to get it back ?

On 6/9/2010 8:54 AM, joel falcou wrote:
Eric Niebler wrote:
I'll add one. Can you say why you want it?
I think I may have missed soem details but basically I need my expression to overload assignment to generate a expr<assign,..> or is inheritng from basic_expr AND use BOOST_PROTO_EXTENDS_ASSIGN enough to get it back ?
You typically don't inherit from Proto's expression types; you use proto::extends or BOOST_PROTO_(BASIC_)EXTENDS. And if you do that, then the Proto expression you're extending is just a tuple data member that holds the child nodes. It doesn't need any fancy member functions. If you're using proto::extends or BOOST_PROTO_EXTENDS (or BOOST_PROTO_BASIC_EXTENDS with BOOST_PROTO_EXTENDS_ASSIGN), then you'll have the right overloaded assignment operator regardless of whether you're extending expr or basic_expr. -- Eric Niebler BoostPro Computing http://www.boostpro.com

Eric Niebler wrote:
You typically don't inherit from Proto's expression types; you use proto::extends or BOOST_PROTO_(BASIC_)EXTENDS. And if you do that, then the Proto expression you're extending is just a tuple data member that holds the child nodes. It doesn't need any fancy member functions.
Yeah, thongue slipped
If you're using proto::extends or BOOST_PROTO_EXTENDS (or BOOST_PROTO_BASIC_EXTENDS with BOOST_PROTO_EXTENDS_ASSIGN), then you'll have the right overloaded assignment operator regardless of whether you're extending expr or basic_expr OK so I just continue doing w/e I am doing at the moment.

OK back from testing the changes in NT2 NT2 table use a custom generator and seems like as_child don't like it: /home/joel/metascale/lib/boost/boost/proto/detail/as_expr.hpp:36: error: no type named ‘proto_base_generator’ in ‘struct nt2::containers::table_generator’ I guess something new is needed for making new generator ?

joel falcou wrote:
OK back from testing the changes in NT2
NT2 table use a custom generator and seems like as_child don't like it:
/home/joel/metascale/lib/boost/boost/proto/detail/as_expr.hpp:36: error: no type named ‘proto_base_generator’ in ‘struct nt2::containers::table_generator’
I guess something new is needed for making new generator ? The code is at :
http://codepad.org/22GRQvnx for reference

On 6/10/2010 3:52 PM, joel falcou wrote:
OK back from testing the changes in NT2
NT2 table use a custom generator and seems like as_child don't like it:
/home/joel/metascale/lib/boost/boost/proto/detail/as_expr.hpp:36: error: no type named ‘proto_base_generator’ in ‘struct nt2::containers::table_generator’
I guess something new is needed for making new generator ?
No, that's a bug. I'll fix it. Thanks for the report. -- Eric Niebler BoostPro Computing http://www.boostpro.com

On 6/10/2010 4:32 PM, Eric Niebler wrote:
On 6/10/2010 3:52 PM, joel falcou wrote:
OK back from testing the changes in NT2
NT2 table use a custom generator and seems like as_child don't like it:
/home/joel/metascale/lib/boost/boost/proto/detail/as_expr.hpp:36: error: no type named ‘proto_base_generator’ in ‘struct nt2::containers::table_generator’
I guess something new is needed for making new generator ?
No, that's a bug. I'll fix it. Thanks for the report.
It should be fixed now. Give it a shot. Just curious, what does your custom generator do? -- Eric Niebler BoostPro Computing http://www.boostpro.com

Eric Niebler wrote:
It should be fixed now. Give it a shot. Fixed indeed.
Just curious, what does your custom generator do?
Given a proto expression X, it builds a table_expr< typename proto::tag_of<X>::type, X> basically I add the tag of the expression in the expression wrapper type. Sounds silly but later, we need to know if a given expression is of tag A or B and doing so cut a lot of compile time compared to calling tag_of each time. It also allow us to overload table_expr behavior based on tag without relying on the internal type of proto expression, making it easy for developper to add such special behavior on some non-trivial expression node.

On 6/11/2010 1:57 AM, Joel Falcou wrote:
Eric Niebler wrote:
Just curious, what does your custom generator do?
Given a proto expression X, it builds a table_expr< typename proto::tag_of<X>::type, X>
basically I add the tag of the expression in the expression wrapper type. Sounds silly but later, we need to know if a given expression is of tag A or B and doing so cut a lot of compile time compared to calling tag_of each time.
You could access the nested proto_tag typedef directly instead of going through tag_of (e.g. typename X::proto_tag). Compile-time matters to me too, which is why I have (so far) avoided allowing arbitrary types declare themselves Proto expressions. Once you do that, you start forcing the extensive use of these trait metafunctions, and I worry about what it might do to compile times.
It also allow us to overload table_expr behavior based on tag without relying on the internal type of proto expression, making it easy for developper to add such special behavior on some non-trivial expression node.
OK. Interesting, thanks. -- Eric Niebler BoostPro Computing http://www.boostpro.com

I'm now playing with the new sub-domain compatibility mode and I got some error. The code here is what I am actually compiling: http://codepad.org/lDUYCVHY Line 11 should have worked as I understood the subdomain thing but it bails out as : /usr/local/include/boost-trunk/boost/proto/detail/as_expr.hpp:203: error: conversion from ‘const nt2::containers::expr<nt2::containers::vector<int>, nt2::containers::table_tag, boost::proto::tag::terminal, boost::proto::exprns_::is_proto_expr>’ to non-scalar type ‘nt2::containers::expr<nt2::containers::expr<boost::proto::exprns_::expr<boost::proto::tag::terminal, boost::proto::term<std::vector<int, std::allocator<int> > >, 0l>, nt2::containers::vector_tag, boost::proto::tag::terminal, boost::proto::exprns_::is_proto_expr>, nt2::containers::table_tag, boost::proto::tag::terminal, boost::proto::exprns_::is_proto_expr>’ requested Did I miss a point in the domain/sub-domain relationship and hwo to use it or is it a bug ? Thanks in advance

On 6/13/2010 4:33 PM, joel falcou wrote:
I'm now playing with the new sub-domain compatibility mode and I got some error. The code here is what I am actually compiling:
Line 11 should have worked as I understood the subdomain thing but it bails out as :
<snip>
Did I miss a point in the domain/sub-domain relationship and hwo to use it or is it a bug ?
It's a bug, but not in the subdomain thing. It's to do with some new code to give hooks for per-domain customization of as_expr/as_child behavior. I am being too cute in my tricks to avoid an extra remove_const instantiation. I'll fix it tomorrow. (There's a lot of churn in Proto right now as I add features needed for Phoenix 3. That's why you're hitting more bugs than usual.) -- Eric Niebler BoostPro Computing http://www.boostpro.com

On 6/14/2010 1:11 AM, Eric Niebler wrote:
On 6/13/2010 4:33 PM, joel falcou wrote:
Did I miss a point in the domain/sub-domain relationship and hwo to use it or is it a bug ?
It's a bug, but not in the subdomain thing. It's to do with some new code to give hooks for per-domain customization of as_expr/as_child behavior. I am being too cute in my tricks to avoid an extra remove_const instantiation. I'll fix it tomorrow.
Heck, it's simple enough. I just checked in the fix on trunk. Let me know how that works for you. -- Eric Niebler BoostPro Computing http://www.boostpro.com

Eric Niebler wrote:
Heck, it's simple enough. I just checked in the fix on trunk. Let me know how that works for you.
OK the base case is working now. I just got a last small problem. If I try to evaluate : m = l+k, x = x + y; with the former code given earlier and the latest trunk, the display of the evaluation is: assign( terminal(St6vectorIiSaIiEE) , plus( terminal(St6vectorIiSaIiEE) , terminal(St6vectorIiSaIiEE) ) ) assign( terminal(St6vectorIiSaIiEE) , plus( terminal(St6vectorIiSaIiEE) , terminal(St6vectorIiSaIiEE) ) ) comma( assign( terminal(St6vectorIiSaIiEE) , plus( terminal(St6vectorIiSaIiEE) , terminal(St6vectorIiSaIiEE) ) ) , assign( terminal(St6vectorIiSaIiEE) , plus( terminal(St6vectorIiSaIiEE) , terminal(St6vectorIiSaIiEE) ) ) ) assign( terminal(St6vectorIiSaIiEE) , plus( terminal(St6vectorIiSaIiEE) , terminal(St6vectorIiSaIiEE) ) ) assign( terminal(St6vectorIiSaIiEE) , plus( terminal(St6vectorIiSaIiEE) , terminal(St6vectorIiSaIiEE) ) ) where it should have been only : comma( assign( terminal(St6vectorIiSaIiEE) , plus( terminal(St6vectorIiSaIiEE) , terminal(St6vectorIiSaIiEE) ) ) , assign( terminal(St6vectorIiSaIiEE) , plus( terminal(St6vectorIiSaIiEE) , terminal(St6vectorIiSaIiEE) ) ) ) Which means that the x = x+y expression is copied and destroyed four time despite my "no-eval" policy in the constructor of expr. I assume some temporary copy are made which are not captured by my no-eval code in nt2::containers::expr. Did I missed some customization to make subdomain/domain expression be captured by reference or should I had some new copy constructor/operator= in my expression class in which I have to toggle the "no-eval" switch ?

Did I missed some customization to make subdomain/domain expression be captured by reference or should I had some new copy constructor/operator= in my expression class in which I have to toggle the "no-eval" switch ?
After looking around, could it be so that as_child/as_expr makes deep copy ?

On 6/14/2010 2:10 AM, joel falcou wrote:
Eric Niebler wrote:
Heck, it's simple enough. I just checked in the fix on trunk. Let me know how that works for you.
OK the base case is working now. I just got a last small problem.
If I try to evaluate :
m = l+k, x = x + y;
I suspect that this is due to a recent change in as_child/as_expr. They used to return references even when they clamed (via result_of) to return values. This was to elide unnecessary copies. But this doesn't work in a world where decltype and auto exist and can independently compute the actual return type of a function. I had thought RVO would eliminate the copies for me, but I must be doing it wrong. I'll try to put it back like it was before until I can't figure it out. -- Eric Niebler BoostPro Computing http://www.boostpro.com

On 6/14/2010 2:10 AM, joel falcou wrote:
Eric Niebler wrote:
Heck, it's simple enough. I just checked in the fix on trunk. Let me know how that works for you.
OK the base case is working now. I just got a last small problem.
If I try to evaluate :
m = l+k, x = x + y;
with the former code given earlier and the latest trunk, the display of the evaluation is:
<snip>
Which means that the x = x+y expression is copied and destroyed four time despite my "no-eval" policy in the constructor of expr. I assume some temporary copy are made which are not captured by my no-eval code in nt2::containers::expr.
Did I missed some customization to make subdomain/domain expression be captured by reference or should I had some new copy constructor/operator= in my expression class in which I have to toggle the "no-eval" switch ?
I was mistaken about falling foul of RVO. Here is what is happening. You have two domains in play, both with different generators. You compose some expressions in different domains, A and B. Proto decides the resulting expression R should be in domain B. Proto prefers things to be nice and uniform, so before making A a child of R, it puts it also into B's domain by applying B's generator. That causes the extra copy you're seeing. If A and B had had the same generator, no extra invocation of the generator would be performed. Why does Proto put expression A into B's domain? Good question. It just seemed like the right thing to do when I wrote the code. Right now, that looks like a dubious decision. Lemme think on it for a bit. -- Eric Niebler BoostPro Computing http://www.boostpro.com

Eric Niebler wrote:
I was mistaken about falling foul of RVO. Here is what is happening. You have two domains in play, both with different generators. You compose some expressions in different domains, A and B. Proto decides the resulting expression R should be in domain B. Proto prefers things to be nice and uniform, so before making A a child of R, it puts it also into B's domain by applying B's generator. That causes the extra copy you're seeing. If A and B had had the same generator, no extra invocation of the generator would be performed.
Why does Proto put expression A into B's domain? Good question. It just seemed like the right thing to do when I wrote the code. Right now, that looks like a dubious decision. Lemme think on it for a bit
Don't know if it'll help you but, back in the day where subdomain didn't exist, NT2 used a as(a,b) function to make a comaptible with b. It was like : template<class Y,class C,class X> static inline typename boost::proto::result_of:: make_expr < tags::as_ , containers::domain<typename Y::nt2_category_tag> , containers::expr<X,C> const& >::type const as( containers::expr<X,C> const& xpr, Y const& ) { return boost::proto:: make_expr < tags::as_ , containers::domain<typename Y::nt2_category_tag> > ( boost::cref(xpr) ); } and it worked without any copy as a was stored as a reference in a special node.

On 6/14/2010 4:09 PM, joel falcou wrote:
Eric Niebler wrote:
I was mistaken about falling foul of RVO. Here is what is happening. You have two domains in play, both with different generators. You compose some expressions in different domains, A and B. Proto decides the resulting expression R should be in domain B. Proto prefers things to be nice and uniform, so before making A a child of R, it puts it also into B's domain by applying B's generator. That causes the extra copy you're seeing. If A and B had had the same generator, no extra invocation of the generator would be performed.
Why does Proto put expression A into B's domain? Good question. It just seemed like the right thing to do when I wrote the code. Right now, that looks like a dubious decision. Lemme think on it for a bit
Don't know if it'll help you but, back in the day where subdomain didn't exist, NT2 used a as(a,b) function to make a comaptible with b. It was like :
template<class Y,class C,class X> static inline typename boost::proto::result_of:: make_expr < tags::as_ , containers::domain<typename Y::nt2_category_tag> , containers::expr<X,C> const& >::type const as( containers::expr<X,C> const& xpr, Y const& ) { return boost::proto:: make_expr < tags::as_ , containers::domain<typename Y::nt2_category_tag> > ( boost::cref(xpr) ); }
and it worked without any copy as a was stored as a reference in a special node.
This jogged my memory a bit. My reasoning at the time was that someone might want to use as_expr to explicitly put a proto expression into a particular domain -- something like your as() function. But in hindsight, a separate function for that might have been better. as_expr should just do one thing: adapt non-proto types to proto. If something is already a proto type, just leave it alone. If the user wants to apply some other domain's generator to it, she can just do that. I've made the change on trunk. This could potentially be a breaking change. For instance, it slightly changes the behavior of your as() function; it no longer will invoke Y's generator before making X a child of the new node. I don't think it makes a difference, but we'll see who hollers. -- Eric Niebler BoostPro Computing http://www.boostpro.com

Eric Niebler wrote:
I've made the change on trunk. This could potentially be a breaking change. For instance, it slightly changes the behavior of your as() function; it no longer will invoke Y's generator before making X a child of the new node. I don't think it makes a difference, but we'll see who hollers.
Works like a charm now. Thnaks for you efforts :)

Eric Niebler wrote:
It's a bug, but not in the subdomain thing. It's to do with some new code to give hooks for per-domain customization of as_expr/as_child behavior. I am being too cute in my tricks to avoid an extra remove_const instantiation. I'll fix it tomorrow.
OK.
(There's a lot of churn in Proto right now as I add features needed for Phoenix 3. That's why you're hitting more bugs than usual.)
I'm fully aware of that, no problem. It's part of our routine ot test our code w/r to trunk proto anyway :) Thanks again
participants (3)
-
Eric Niebler
-
joel falcou
-
Joel Falcou