boost::proto short example

This is a short example that I put together to show how to use boost::proto for modifying the semantics of C++ builtin datatypes. In particolar we define unsigned integers for which we can define a width (less than sizof(unsigned int)*8). Values are masked and bits outside the declared width are discarded. We also redefine the shift operator so that both positive and negative shift counts are supported. The code compiles fine with GCC 4.1.2. It fails miserably under the Intel C++ compiler 9.1.046, which is really really unhappy about the context being a template class. I haven't investigated further, but given the fact that the Intel compiler is EDG based I suspect there's something wrong either in my code or in boost::proto. I hope Eric can find the time to review it and maybe add it to the boost::proto examples. I would certainly have benefited from something like this being available. On the other hand, I've learned more by doing it. Thanks to Eric for having helped me and for making boost::proto available. I'm sure I'll have more questions as I proceed in proto-izing my numeric library. Regards, Maurizio #include <iostream> #include <boost/xpressive/proto/proto.hpp> #include <boost/xpressive/proto/context.hpp> #include <boost/xpressive/proto/extends.hpp> #include <boost/xpressive/proto/debug.hpp> #include <boost/typeof/typeof.hpp> #include <boost/typeof/std/ostream.hpp> #include <boost/mpl/integral_c.hpp> namespace proto=boost::proto; namespace mpl=boost::mpl; 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 =; typedef unsigned int result_type; operator result_type () const { return proto::eval(*this, my_context<Expr> ()); } }; 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 T> struct number; template<typename T> std::ostream& operator << (std::ostream& os, const number<T> o); template<typename T> struct number { friend std::ostream& operator << <> (std::ostream& os, const number<T> o); unsigned int m_data; }; template<typename T> std::ostream& operator << (std::ostream& os, const number<T> n) { os << "~" << n.m_data << "~"; return os; } template<typename Expr> struct my_context : proto::callable_context<const my_context<Expr> > { typedef int result_type; // later we'll compute this from Expr template<typename T> int operator () (proto::tag::terminal, number<T> n) const { return n.m_data; } template<typename Left, typename Right> int operator () (proto::tag::left_shift, const Left& left, const Right& right) const { int shift = proto::eval (right, *this); if (shift >= 0) return proto::eval (left, *this) << shift; else return proto::eval (left, *this) >> -shift; } template<typename Left, typename Right> int operator () (proto::tag::right_shift, const Left& left, const Right& right) const { int shift = proto::eval (right, *this); if (shift >= 0) return proto::eval (left, *this) >> shift; else return proto::eval (left, *this) << -shift; } }; template<int N> struct my_int : my_expr<typename proto::terminal<number<mpl::int_<N> > >::type> { typedef my_expr<typename proto::terminal<number<mpl::int_<N> > >::type> expr_type; my_int () {} my_int (int i) : expr_type (expr_type::type::make (mask (i))) {} template<typename Expr> my_int& operator = (const Expr& e) { proto::arg (*this).m_data = mask (proto::eval(e, my_context<Expr> ())); return *this; } unsigned int mask (unsigned int value) { return value & ((1U << N) - 1); } }; int main (int, char**) { my_int<4> b(18); my_int<2> c; int i; c=b*b+1; // c = 1 i = c << -1; // i = 0 std::cout << "i=" << i << "\n"; i = b << -1; // i = 1 std::cout << "i=" << i << "\n"; i = b << 5; // i = 64 std::cout << "i=" << i << "\n"; }

On Fri, 13 Apr 2007 14:11:49 +0200, Maurizio Vitale <mav@cuma.polymath-solutions.lan> wrote:
This is a short example that I put together to show how to use boost::proto for modifying the semantics of C++ builtin datatypes.
In particolar we define unsigned integers for which we can define a width (less than sizof(unsigned int)*8). Values are masked and bits outside the declared width are discarded. We also redefine the shift operator so that both positive and negative shift counts are supported.
Really nice ! You used boost::proto to make up integers modulo N with N a power of 2. Marco -- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/

Maurizio Vitale wrote:
This is a short example that I put together to show how to use boost::proto for modifying the semantics of C++ builtin datatypes.
<snip>
It fails miserably under the Intel C++ compiler 9.1.046, which is really really unhappy about the context being a template class. I haven't investigated further, but given the fact that the Intel compiler is EDG based I suspect there's something wrong either in my code or in boost::proto.
Yes, I've noticed from the HEAD regressions for xpressive that Intel is not happy with proto. I don't have Intel at the moment so I can't debug. Patches welcome.
I hope Eric can find the time to review it and maybe add it to the boost::proto examples. I would certainly have benefited from something like this being available. On the other hand, I've learned more by doing it. Thanks to Eric for having helped me and for making boost::proto available.
<snip code> I have no comments about your use of proto -- it looks a lot like the lazy_vector example from the users' guide, in fact. My only question is about why you want to use expression templates here at all. It's certainly not to avoid the cost of temporaries -- your number<> type holds nothing more than an int! Have I missed something? -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler wrote:
Maurizio Vitale wrote:
It fails miserably under the Intel C++ compiler 9.1.046, which is really really unhappy about the context being a template class. I haven't investigated further, but given the fact that the Intel compiler is EDG based I suspect there's something wrong either in my code or in boost::proto.
Yes, I've noticed from the HEAD regressions for xpressive that Intel is not happy with proto. I don't have Intel at the moment so I can't debug. Patches welcome.
(Replying to myself...) I downloaded an evaluation copy of the Intel compiler for Windows and found some work-arounds for various problems. Your code now compiles with Intel, as do xpressive's latest tests. -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler <eric@boost-consulting.com> writes:
I hope Eric can find the time to review it and maybe add it to the boost::proto examples. I would certainly have benefited from something like this being available. On the other hand, I've learned more by doing it. Thanks to Eric for having helped me and for making boost::proto available.
<snip code>
I have no comments about your use of proto -- it looks a lot like the lazy_vector example from the users' guide, in fact.
Another difference, again probably obvious to the seasoned user, but that the average newcomer might miss is that in order to allow an assignment of the form: int v = ...expr involving extended types... you seem to need an operator int () in the wrapping expression. That was the best I managed to do and the only reason I needed to wrap boost::proto expressions in my example. If there's a better way, I'd like to know. The lazy vector example doesn't show anything of sort. The nearest is the line: double d1 = (v1 + v2)[2] but there the double comes from the evaluation of operator[]. I still think that an example on lightweight DSLs, where you're not drastically change domain, but what you need is almost plain C++ expression would be useful. The example I posted shows how to wrap a UDT and how to slightly modify the semantics of a few operators. The next example I'm working on will show how to define multiple version of these and control what can be mixed with what (and furthemore define the type of the result when they mix together). Again, small things, but having a cookbook of these may be very helpful to newcomers to proto. I think that the effort it takes to people to understand concepts can be minimized by moving from the territory they know up in reasonably small steps. A big-bang example showing the mighty of proto is very good for catching the attention and convincing people that investing time in proto is worthwhile, but then it is important not to lose them off the learning curve. I'll keep posting what I'm learning with your help. I hope boost.devel is the right place, as boost::proto has not been officially included in boost yet and this is probably where early adopters hang around. Maurizio

Yes, I've noticed from the HEAD regressions for xpressive that Intel is not happy with proto. I don't have Intel at the moment so I can't debug. Patches welcome.
I'll try to see if I can understand what the problem is, at least in this case for which I have a short example which fails.
I hope Eric can find the time to review it and maybe add it to the boost::proto examples. I would certainly have benefited from something like this being available. On the other hand, I've learned more by doing it. Thanks to Eric for having helped me and for making boost::proto available.
<snip code>
I have no comments about your use of proto -- it looks a lot like the lazy_vector example from the users' guide, in fact.
It is very similar indeed, and probably they can be merged. If you remember, one of my problems was were to put use defined data. In the lazy vector example, terminals are parametrized with a std::vector and that's where the wrapped data is. The example I've sent makes, imo, more clear how to address the simple use case of using an udt with proto. In the end they are the same, but understanding that that's the case might require more understanding of proto than a novice user probably has.
My only question is about why you want to use expression templates here at all. It's certainly not to avoid the cost of temporaries -- your number<> type holds nothing more than an int! Have I missed something?
Well, I'm trying to isolate the problems I'm facing in small fragments of code. I've sent you a larger piece, not using proto, and even that is only part of what I need. In the end I'll have integers and fixed-point numbers with different precision (including large and unbounded ones based on the GNU mp library). Removing temporaries will be interesting for large numbers, but removing masking, shifting, quantization and overflow handling for intermediate expressions is where I need expression templates. So, yes, if all you need is implementing modular arithmetic like my example, you don't need expression templates.
-- Eric Niebler Boost Consulting www.boost-consulting.com _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/ listinfo.cgi/boost
participants (4)
-
Eric Niebler
-
Marco
-
Maurizio Vitale
-
Maurizio Vitale