[boost::proto] how to get the type of a proto expression?

Eric, I can get the type of a proto expression (wrapped in a user-defined skin) using BOOST_TYPEOF, but then I noticed BOOST_PROTO_TYPEOF in context.hpp. Which one is supposed to be used? BOOST_PROTO_TYPEOF fails on me when trying to emulate the way it is used inside context.hpp. Here is code that shows what I'm trying. If BOOST_PROTO_TYPEOF is internal only, just let me know and forget the code below. Regards, Maurizio #include <iostream> #define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS #define BOOST_PROTO_MAX_ARITY 8 #define BOOST_MPL_LIMIT_METAFUNCTION_ARITY 8 // GCC bug workaround, needs to be at least BOOST_PROTO_MAX_ARITY #include <boost/xpressive/proto/proto.hpp> #include <boost/xpressive/proto/context.hpp> #include <boost/xpressive/proto/extends.hpp> #include <boost/xpressive/proto/transform/arg.hpp> #include <boost/xpressive/proto/transform/construct.hpp> namespace proto=boost::proto; namespace mpl=boost::mpl; using proto::_; struct my_domain : proto::domain<struct my_grammar> {}; //struct my_domain : proto::domain<> {}; template<typename> struct my_context; template <typename Expr> struct my_expr : proto::extends<Expr, my_expr<Expr>, my_domain> { typedef proto::extends<Expr, my_expr<Expr>, my_domain> base_type; my_expr (Expr const& expr = Expr()) : base_type (expr) {}; using base_type::operator =; operator int () const { return static_cast<int>(proto::eval(*this, my_context<Expr> ())); } operator unsigned int () const { return static_cast<unsigned int>(proto::eval(*this, my_context<Expr> ())); } }; template<typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H> struct number { unsigned int m_data; }; struct my_grammar : proto::or_ < proto::terminal< number<_,_,_,_,_,_,_,_> > , proto::terminal<int>, proto::terminal<unsigned int>, proto::unary_expr<proto::_, my_grammar> , proto::binary_expr<proto::_, my_grammar, my_grammar>
{}; namespace boost { namespace proto { template<typename Expr> struct generate<my_domain, Expr> { typedef my_expr<Expr> type; static type make (Expr const& expr) { return type (expr); } }; } } // end namespace boost::proto template<typename Expr> struct my_context : proto::callable_context<const my_context<Expr> > { typedef unsigned int result_type; template<typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H> unsigned int operator () (proto::tag::terminal, number<A,B,C,D,E,F,G,H> n) const { return n.m_data; } }; template<int N> struct my_int : my_expr<typename proto::terminal< number<mpl::int_<0>,mpl::int_<0>, mpl::int_<0>, mpl::int_<0>, mpl::int_<0>, mpl::int_<0>, mpl::int_<0>, mpl::int_<0> > >::type> { typedef number<mpl::int_<0>,mpl::int_<0>, mpl::int_<0>, mpl::int_<0>, mpl::int_<0>, mpl::int_<0>, mpl::int_<0>, mpl::int_<0> > number_type; typedef my_expr<typename proto::terminal<number_type>::type> expr_type; my_int () {} my_int (int i) : expr_type (expr_type::type::make (i)) {} template<typename Expr> my_int& operator = (const my_expr<Expr>& e) { proto::arg (*this).m_data = static_cast<int>(proto::eval(e, my_context<Expr> ())); return *this; } template<typename T> my_int& operator = (T value) { proto::arg (*this).m_data = value; return *this; } }; template<typename T> struct dump; #define DUMP(T) typedef dump<T>::type t int main (int,char**) { my_int<6> i4(-22); int i; unsigned int j; i4 = 5; i4 = 5*i4; i = i4/i4+4; j = i4/i4; BOOST_PROTO_TYPEOF (i4+j+2, a_type); DUMP (a_type); // typedef BOOST_TYPEOF (i4*2) another_type; // DUMP(another_type); } /// Local Variables: /// mode:c++ /// comment-column:80 /// fill-column:160 /// compilation-read-command:nil /// compile-command:"g++ -I. -ope1 pe1.cpp" /// End:

Maurizio Vitale wrote:
Eric, I can get the type of a proto expression (wrapped in a user-defined skin) using BOOST_TYPEOF, but then I noticed BOOST_PROTO_TYPEOF in context.hpp.
Which one is supposed to be used?
BOOST_PROTO_TYPEOF fails on me when trying to emulate the way it is used inside context.hpp.
Here is code that shows what I'm trying. If BOOST_PROTO_TYPEOF is internal only, just let me know and forget the code below.
BOOST_PROTO_TYPEOF is internal only. I'll rename it, and #undef it at the end of context.hpp. You should #include proto_typeof.hpp and use BOOST_PROTO_AUTO(), like this: BOOST_PROTO_AUTO( expr, a+b*c ); This is equivalent to: BOOST_AUTO( expr, boost::proto::deep_copy(a+b*c) ); Nodes in proto's expression tree are held by reference, so that building an expression tree requires no copying. But some nodes are temporaries. deep_copy() forces all nodes to be held by value, which is necessary if you're storing them in a local variable like this. HTH, -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler <eric@boost-consulting.com> writes:
BOOST_PROTO_TYPEOF is internal only. I'll rename it, and #undef it at the end of context.hpp.
Ok. Thanks.
Nodes in proto's expression tree are held by reference, so that building an expression tree requires no copying. But some nodes are temporaries. deep_copy() forces all nodes to be held by value, which is necessary if you're storing them in a local variable like this.
Actually, I'm trying to do the opposite: I have expressions used to represent the way to do quantization and manage overflow which have noting to refer to when they're created, so they're made of operators and terminals with no data in. The type is all that is needed for evaluating them, and the evaluation context will know where to grab the data from. Although evaluation is driven by the type, it is a run-time evaluation. I hope to be able to use evaluation contexts for this, if it doesn't work either transforms or an homebrewed solutions (but still using proto for describing the expression). Since you mentioned by-ref/by-value, there's something I meant to ask: proto stores by ref: wouldn't be possible to let the terminal decide? When data is a simple builtin, by-value would be more efficient. I haven't checked the implementation, so I'm not sure the above makes sense. Best regards, Maurizio

Maurizio Vitale wrote:
Eric Niebler <eric@boost-consulting.com> writes:
Nodes in proto's expression tree are held by reference, so that building an expression tree requires no copying. But some nodes are temporaries. deep_copy() forces all nodes to be held by value, which is necessary if you're storing them in a local variable like this.
Actually, I'm trying to do the opposite: I have expressions used to represent the way to do quantization and manage overflow which have noting to refer to when they're created, so they're made of operators and terminals with no data in. The type is all that is needed for evaluating them, and the evaluation context will know where to grab the data from. Although evaluation is driven by the type, it is a run-time evaluation.
I hope to be able to use evaluation contexts for this, if it doesn't work either transforms or an homebrewed solutions (but still using proto for describing the expression).
proto::eval() takes an expression and a context. So you need an expression, not just the expression's type. You can write a transform that works with types, but if you want to manipulate runtime data (and you say you do), the transforms will also need an expression object. (Recall that the transforms take an expression, in addition to the state and a visitor.) But if you want it, BOOST_TYPEOF() should get you the type of a proto expression, once you include proto_typeof.hpp to get the type registrations.
Since you mentioned by-ref/by-value, there's something I meant to ask: proto stores by ref: wouldn't be possible to let the terminal decide? When data is a simple builtin, by-value would be more efficient. I haven't checked the implementation, so I'm not sure the above makes sense.
I considered that, but decided against it. It would take an extra compile-time conditional to decide whether to store by value or reference (that is, at least one extra template instantiation per terminal type). And lots of stuff would still be stored by reference, so people will still need to use deep_copy(). I don't see an obvious win to storing some terminals by value and others by reference. I'm happy to be proven wrong, however. -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler <eric@boost-consulting.com> writes:
Maurizio Vitale wrote:
Eric Niebler <eric@boost-consulting.com> writes:
Nodes in proto's expression tree are held by reference, so that building an expression tree requires no copying. But some nodes are temporaries. deep_copy() forces all nodes to be held by value, which is necessary if you're storing them in a local variable like this.
Actually, I'm trying to do the opposite: I have expressions used to represent the way to do quantization and manage overflow which have noting to refer to when they're created, so they're made of operators and terminals with no data in. The type is all that is needed for evaluating them, and the evaluation context will know where to grab the data from. Although evaluation is driven by the type, it is a run-time evaluation.
I hope to be able to use evaluation contexts for this, if it doesn't work either transforms or an homebrewed solutions (but still using proto for describing the expression).
proto::eval() takes an expression and a context. So you need an expression, not just the expression's type. You can write a transform that works with types, but if you want to manipulate runtime data (and you say you do), the transforms will also need an expression object. (Recall that the transforms take an expression, in addition to the state and a visitor.)
I was sure evaluation contexts wouldn't do. Still it is a pity, I would have loved the default operators, rather than rolling my own suite. But on transforms you caught me, I did indeed forgot the first argument to call() :-(. I think I'll take a good look at the implementation and see if something springs to mind. Here's what I need for quantization handling (and similar things for overflow handling): enum quantization_mode_enum {round_to_positive_infinity, round_to_zero, round_to_negative_infinity, round_to_infinity, round_to_nearest, round_convergent, truncate_value, truncate_magnitude}; typedef mpl::integral_c<quantization_mode_enum, round_to_positive_infinity> round_to_positive_infinity_c; typedef mpl::integral_c<quantization_mode_enum, round_to_zero> round_to_zero_c; typedef mpl::integral_c<quantization_mode_enum, round_to_negative_infinity> round_to_negative_infinity_c; typedef mpl::integral_c<quantization_mode_enum, round_to_infinity> round_to_infinity_c; typedef mpl::integral_c<quantization_mode_enum, round_to_nearest> round_to_nearest_c; typedef mpl::integral_c<quantization_mode_enum, round_convergent> round_convergent_c; typedef mpl::integral_c<quantization_mode_enum, truncate_value> truncate_value_c; typedef mpl::integral_c<quantization_mode_enum, truncate_magnitude> truncate_magnitude_c; template<typename T> struct quantization_spillover; #define DECLARE_QUANTIZATION_TERMINAL(NAME) \ struct NAME ## _c {}; \ proto::terminal< NAME ## _c >::type NAME = {{}} #define DECLARE_QUANTIZATION_MODE(NAME, EXPRESSION) \ template<> struct quantization_spillover<NAME ## _c> { \ typedef BOOST_TYPEOF (EXPRESSION) type; \ } DECLARE_QUANTIZATION_TERMINAL (deleted_msb); DECLARE_QUANTIZATION_TERMINAL (deleted_lsbs); DECLARE_QUANTIZATION_TERMINAL (preserved_msb); DECLARE_QUANTIZATION_TERMINAL (preserved_lsb); DECLARE_QUANTIZATION_TERMINAL (sign); DECLARE_QUANTIZATION_MODE (round_to_positive_infinity, deleted_msb); DECLARE_QUANTIZATION_MODE (round_to_infinity, deleted_msb); DECLARE_QUANTIZATION_MODE (round_convergent, deleted_msb & (preserved_lsb | !deleted_lsbs)); DECLARE_QUANTIZATION_MODE (round_to_zero, deleted_msb & (sign | !deleted_lsbs)); DECLARE_QUANTIZATION_MODE (round_to_negative_infinity, deleted_msb & !deleted_lsbs); DECLARE_QUANTIZATION_MODE (truncate_value, 0); DECLARE_QUANTIZATION_MODE (truncate_magnitude, sign & (deleted_msb | !deleted_lsbs)); Then with quantization_spillover<MODE>::type I get the (type of the) expression that needs to be evaluated. The value of terminals will be themselves function of the source and target type and value. For instance, deleted_msb will be the bit at position I in the source, where I is target'scale - source'scale. I will be in most cases a compile-time constant, but I'll have to support the case where it si a run-time value as well. Evaluation iself shouldn't require to build an object of that type. This is what I'd do by hand: template<typename Expr> struct eval {}; template<> struct eval< boost::proto::expression<boost::proto::tags::bitwise_and ....> { int call () { typedef ...some boost::proto type selector... left; typedef ...some boost::proto type selector... right; return left::call () & right::call (); } It would be wonderful if boost could help me here, leaving to me only the task to define the semantics of terminals (and of those operators (like << and >>) for which the default semantics wont do it for me).
I considered that, but decided against it. It would take an extra compile-time conditional to decide whether to store by value or reference (that is, at least one extra template instantiation per terminal type). And lots of stuff would still be stored by reference, so people will still need to use deep_copy(). I don't see an obvious win to storing some terminals by value and others by reference. I'm happy to be proven wrong, however.
Mhhh. I'll be back on this when I start looking seriously at the assembly code produced for my expressions. For now it looks good (seems like in simple cases the compiler manages to optimize away lot of stuff), although I still have mov instructions I'd rather not have. If proven useful a possibility would be a CPP #define that says one of three: - all by ref - all by value - terminal decide. Maybe "terminal decide" can be made so that you do not get a compile type conditional: what happens if terminal defines explicitely the way it want to be stored, so that terminal< >::stored_as directly give you the value/ref behaviour?
participants (2)
-
Eric Niebler
-
Maurizio Vitale