
Wonderful, I didn't think of two contexts. Here's the result, with only two changes to your suggestion: - I nested the two contexts inside each other - I've added arguments to the inner_context so that is able to use the outer_context for evaluation This is beacause when you have a function with more than one arguments is is possible that some of them are compile-time constants, so it is worth a trip to save run-time evaluations. Haven't checked the generated assembly, so I don't know the effect of passing target and source through the constructor of the inner_context. Maybe this can also be turned into an example. Thanks again, Maurizio -------------------------------------------------------------------------------------------------------- template<typename Target, typename Source> struct meta_context : proto::callable_context<const meta_context<Target,Source> > { typedef meta_context<Target, Source> self; typedef unsigned int result_type; meta_context (const Target& target, const Source& source) : m_inner_context(*this, target, source) {} template<typename Expr_> struct compile_time_eval : meta_transform<Target, Source>::template apply<Expr_,mpl::void_, mpl::void_> {}; template<typename Expr_> struct compile_time_unknown : boost::is_same<typename compile_time_eval<Expr_>::type, run_time> { }; template<typename Expr_, typename Enable=void> struct eval { typedef unsigned int result_type; result_type operator () (const Expr_& expr, self ctx) const { return compile_time_eval<Expr_>::type::value; } }; template<typename Expr_> struct eval<Expr_, typename boost::enable_if<typename compile_time_unknown<Expr_>::type >::type > { typedef unsigned int result_type; result_type operator () (const Expr_& expr, self ctx) const { return proto::eval (expr, ctx.m_inner_context); } }; template<typename Outer_Context> struct inner_context : proto::callable_context<const inner_context<Outer_Context> > { typedef meta_context<Target, Source> self; typedef unsigned int result_type; inner_context (const Outer_Context& outer_context, const Target& target, const Source& source) : m_outer_context (outer_context), m_target (target), m_source (source) {} #define RESULT_FOR(NAME, EXPR) result_type operator () (proto::tag::terminal, NAME##_tag NAME) const { return EXPR; } RESULT_FOR (source, proto::arg(m_source).m_data) RESULT_FOR (source_left, proto::arg(m_source).left ()) RESULT_FOR (source_right, proto::arg(m_source).right ()) RESULT_FOR (target, proto::arg(m_target).m_data) RESULT_FOR (target_left, proto::arg(m_target).left ()) RESULT_FOR (target_right, proto::arg(m_target).right ()) template<typename What, typename Left, typename Right> result_type operator () (mask_tag, const What& what, const Left& left, const Right& right) const { std::size_t what_value = proto::eval (what, m_outer_context); std::size_t left_value = proto::eval (left, m_outer_context); std::size_t right_value = proto::eval (right, m_outer_context); if (left_value == sizeof (unsigned long long)*8) return what_value & (~0ULL << right_value); else return what_value & (~(~1ull << (left_value-1) | ~(~0ull << right_value))); } template<typename Left, typename Right> result_type operator () (mask_tag, const Left& left, const Right& right) const { std::size_t left_value = proto::eval (left, m_outer_context); std::size_t right_value = proto::eval (right, m_outer_context); if (left_value == sizeof (unsigned long long)*8) return ~0ULL << right_value; else return ~(~1ull << (left_value-1) | ~(~0ull << right_value)); } #undef RESULT_FOR const Outer_Context& m_outer_context; const Target& m_target; const Source& m_source; }; inner_context<self> m_inner_context; };