
Ok, I painfully crawled my way to something that has the functionality I needed. The code attached does indeed compute the sum of all widths for expressions involving a user defined integer and binary operations. The good news are that the generated assembly is good (actually, as it is often the case with template metaprogramming is better than what I expected: remember I wanted one addition when only one term has a run-time width, well I've got no additions, for this case where the compiler had all the information local). This is to say that the following fragment: MARK ("B rt_width"); int j = get_rt_width (b+i+c+b+b); b is 4 bit, compile-time; c is 8 bit, compile-time; i is 8 bit, run-time. Total width=28 MARK ("E rt_width"); std::cout << j << "\n"; Get's translated into: #APP #### B rt_width #NO_APP leaq 64(%rsp), %r13 leaq 96(%rsp), %r12 leaq 80(%rsp), %rbx leaq 16(%rsp), %rax movq %r13, 24(%rsp) movq %r12, 16(%rsp) movq %rbx, 8(%rsp) movq %rax, (%rsp) #APP #### E rt_width #NO_APP movl $28, %esi <-- Look me, no sums! movl $_ZSt4cout, %edi call _ZNSolsEi On the not so bright side, there's more copying that I would have liked (the part between #### mark), but I haven't tracked down where they come from. Now, enough with the good stuff. The code below, imo, sucks. It takes me two transforms and many backflips to compute what should be a very simple thing. I'm not sure it is the right way to go about it. At some point I've also toyed with the idea of using a special evaluation context for this, rather than the two transform. Eric, which would be the way you'd have gone? Is there a way to have a single transform? [I've tried many variations, but it seemed like I could never have the type "blanket" in the transform covering both apply and call: when one was type-happy the other was type-cold and vice-versa] ----------------------------------------------------------------------------- #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/plus.hpp> #include <boost/mpl/vector.hpp> #include <boost/mpl/contains.hpp> using namespace boost; struct run_time {}; struct my_domain : proto::domain<> {}; 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 int result_type; operator result_type () const { return 10; } }; 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=run_time> struct number; template<typename T> std::ostream& operator << (std::ostream& os, const number<T> o); template<typename T> struct number { typedef T width; number () : m_data (0) {} number (int i) : m_data (i) {} friend std::ostream& operator << <> (std::ostream& os, const number<T> o); unsigned int m_data; }; template<> struct number<run_time> { typedef run_time width; number () : m_data (0) {} number (int w, int i) : m_data (i), m_width (w) {} friend std::ostream& operator << <> (std::ostream& os, const number<run_time> o); unsigned int m_data; unsigned int m_width; }; template<typename T> std::ostream& operator << (std::ostream& os, const number<T> o) { os << "~" << o.m_data << "~"; return os; } template<template<typename,typename> class Op, typename A1, typename A2> struct unless_run_time : mpl::if_<mpl::contains<mpl::vector<A1,A2>, run_time>, run_time, Op<A1,A2> > {}; template<typename Grammar> struct binary_width : Grammar { binary_width(); template<typename Expr, typename State, typename Visitor> struct apply { typedef typename mpl::apply_wrap3<Grammar,Expr,State,Visitor>::type expr_type; typedef typename proto::result_of::left<expr_type>::type _1; typedef typename proto::result_of::right<expr_type>::type _2; typedef typename unless_run_time<mpl::plus, _1, _2>::type type; }; }; template<typename Grammar> struct terminal_width : Grammar { terminal_width(); template<typename Expr, typename State, typename Visitor> struct apply { typedef typename proto::result_of::arg_c<Expr,0>::type number; typedef typename number::width type; }; }; using proto::_; struct width_transform : proto::or_ < terminal_width<proto::terminal< number<_> > >, binary_width<proto::binary_expr<_, width_transform, width_transform> >
{};
template<typename Grammar> struct rt_binary_width : Grammar { rt_binary_width(); template<typename Expr, typename State, typename Visitor> struct apply { typedef int type; }; template<typename Expr, typename State, typename Visitor> static typename apply<Expr,State,Visitor>::type call(Expr const &expr, State const &, Visitor &) { typedef typename proto::result_of::left<Expr>::type left; typedef typename proto::result_of::left<Expr>::type right; return get_rt_width(proto::left (expr)) + get_rt_width(proto::right (expr)); } }; template<typename Grammar> struct rt_terminal_width : Grammar { rt_terminal_width(); template<typename Expr, typename State, typename Visitor> struct apply { typedef int type; }; template<typename Expr, typename State, typename Visitor> static typename apply<Expr,State,Visitor>::type call(Expr const &expr, State const &, Visitor &) { return proto::arg (expr).m_width; } }; struct rt_width_transform : proto::or_ < rt_terminal_width<proto::terminal< number<_> > >, rt_binary_width<proto::binary_expr<_, rt_width_transform, rt_width_transform> >
{};
template<typename Expr> struct width : width_transform::template apply<Expr, mpl::true_, mpl::void_> {}; template<typename T> struct aux { template<typename U> static int call (U) { return T::value; } }; template<> struct aux<run_time> { template<typename T> static int call (T e) { mpl::void_ null; return rt_width_transform::call (e, null, null); } }; template<typename Expr> int get_rt_width (Expr e) { return aux<typename width<Expr>::type >::call (e); } template<typename Expr> int get_width (Expr e) { mpl::void_ null; return width<Expr>::type::value; } template<typename Expr> struct my_context : proto::callable_context<const my_context<Expr> > { typedef int result_type; // later we'll compute this from Expr }; int foo (int,int) { return 0; } template<int N> struct my_int : my_expr<typename proto::terminal<number<mpl::int_<N> > >::type > { typedef number<mpl::int_<N> > 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 (number_type (i))) {} template<typename Expr> my_int& operator = (const Expr& e) { proto::arg (*this).m_data = proto::eval(e, my_context<Expr> ()); return *this; } }; struct my_int_base : my_expr<proto::terminal<number<run_time> >::type > { typedef number<run_time> number_type; typedef my_expr<proto::terminal<number_type>::type > expr_type; my_int_base () {} my_int_base (int w, int i) : expr_type (expr_type::type::make (number_type(w,i))) {} template<typename Expr> my_int_base& operator = (const Expr& e) { proto::arg (*this).m_data = proto::eval(e, my_context<Expr> ()); return *this; } }; #define MARK(s) __asm__ __volatile__ ("#### " s) int main (int, char**) { my_int<4> b(3); my_int<8> c; my_int_base i(8,42); MARK ("B rt_width"); int j = get_rt_width (b+i+c+b+b); MARK ("E rt_width"); std::cout << j << "\n"; std::cout << "bounds(b)=" << get_rt_width (b) << "\n"; std::cout << "bounds(b+c)=" << get_rt_width (b+c) << "\n"; std::cout << "bounds(i+i)=" << get_rt_width (i+i) << "\n"; std::cout << "bounds(b+i+c+b+b)=" << get_rt_width (b+i+c+b+b) << "\n"; } /// Local Variables: /// mode:c++ /// comment-column:80 /// fill-column:160 /// compilation-read-command:nil /// compile-command:"g++ -I. -oex4 ex4.cpp" /// End: