//[ Mixed /////////////////////////////////////////////////////////////////////////////// // Copyright 2008 Eric Niebler. Distributed under the Boost // Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // This is an example of using BOOST_PROTO_DEFINE_OPERATORS to Protofy // expressions using std::vector<> and std::list, non-proto types. It is a port // of the Mixed example from PETE. // (http://www.codesourcery.com/pooma/download.html). #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace proto = boost::proto; namespace mpl = boost::mpl; using proto::_; template struct MixedExpr; template struct iterator_wrapper { typedef Iter iterator; explicit iterator_wrapper(Iter iter) : it(iter) {} mutable Iter it; }; struct begin : proto::callable { template struct result; template struct result : proto::result_of::as_expr< iterator_wrapper::type::const_iterator> > {}; template typename result::type operator ()(Cont const &cont) const { iterator_wrapper it(cont.begin()); return proto::as_expr(it); } }; // Here is a grammar that replaces vector and list terminals with their // begin iterators struct Begin : proto::or_< proto::when< proto::terminal< std::vector<_, _> >, begin(proto::_value) > , proto::when< proto::terminal< std::list<_, _> >, begin(proto::_value) > , proto::when< proto::terminal<_> > , proto::when< proto::nary_expr<_, proto::vararg > > > {}; // Here is an evaluation context that dereferences iterator // terminals. struct DereferenceCtx { // Unless this is an iterator terminal, use the // default evaluation context template struct eval : proto::default_eval {}; // Dereference iterator terminals. template struct eval< Expr , typename boost::enable_if< proto::matches > > >::type > { typedef typename proto::result_of::value::type IteratorWrapper; typedef typename IteratorWrapper::iterator iterator; typedef typename std::iterator_traits::reference result_type; result_type operator ()(Expr &expr, DereferenceCtx const &) const { return *proto::value(expr).it; } }; }; // Here is an evaluation context that increments iterator // terminals. struct IncrementCtx { // Unless this is an iterator terminal, use the // default evaluation context template struct eval : proto::null_eval {}; // advance iterator terminals. template struct eval< Expr , typename boost::enable_if< proto::matches > > >::type > { typedef void result_type; result_type operator ()(Expr &expr, IncrementCtx const &) const { ++proto::value(expr).it; } }; }; // A grammar which matches all the assignment operators, // so we can easily disable them. struct AssignOps : proto::switch_ {}; // Here are the cases used by the switch_ above. struct AssignOpsCases { template struct case_ : proto::not_<_> {}; template struct case_< proto::tag::plus_assign, D > : _ {}; template struct case_< proto::tag::minus_assign, D > : _ {}; template struct case_< proto::tag::multiplies_assign, D > : _ {}; template struct case_< proto::tag::divides_assign, D > : _ {}; template struct case_< proto::tag::modulus_assign, D > : _ {}; template struct case_< proto::tag::shift_left_assign, D > : _ {}; template struct case_< proto::tag::shift_right_assign, D > : _ {}; template struct case_< proto::tag::bitwise_and_assign, D > : _ {}; template struct case_< proto::tag::bitwise_or_assign, D > : _ {}; template struct case_< proto::tag::bitwise_xor_assign, D > : _ {}; }; // An expression conforms to the MixedGrammar if it is a terminal or some // op that is not an assignment op. (Assignment will be handled specially.) struct MixedGrammar : proto::or_< proto::terminal<_> , proto::and_< proto::nary_expr<_, proto::vararg > , proto::not_ > > {}; // Expressions in the MixedDomain will be wrapped in MixedExpr<> // and must conform to the MixedGrammar struct MixedDomain : proto::domain, MixedGrammar> {}; // Here is MixedExpr, a wrapper for expression types in the MixedDomain. template struct MixedExpr : proto::extends, MixedDomain> { explicit MixedExpr(Expr const &expr) : MixedExpr::proto_extends(expr) {} private: // hide this: using proto::extends, MixedDomain>::operator []; }; // Define a trait type for detecting vector and list terminals, to // be used by the BOOST_PROTO_DEFINE_OPERATORS macro below. template struct IsMixed : mpl::false_ {}; template struct IsMixed > : mpl::true_ {}; template struct IsMixed > : mpl::true_ {}; namespace MixedOps { // This defines all the overloads to make expressions involving // std::vector to build expression templates. BOOST_PROTO_DEFINE_OPERATORS(IsMixed, MixedDomain) struct assign_op { template void operator ()(T &t, U const &u) const { t = u; } }; struct plus_assign_op { template void operator ()(T &t, U const &u) const { t += u; } }; struct minus_assign_op { template void operator ()(T &t, U const &u) const { t -= u; } }; struct sin_ { template struct result; template struct result : boost::remove_const::type> {}; template Arg operator ()(Arg const &a) const { return std::sin(a); } }; template typename proto::result_of::make_expr< proto::tag::function , MixedDomain , sin_ const , A const & >::type sin(A const &a) { return proto::make_expr(sin_(), boost::ref(a)); } template void evaluate(FwdIter begin, FwdIter end, Expr const &expr, Op op) { IncrementCtx const inc = {}; DereferenceCtx const deref = {}; typename boost::result_of::type expr2 = Begin()(expr); for(; begin != end; ++begin) { op(*begin, proto::eval(expr2, deref)); proto::eval(expr2, inc); } } // Add-assign to a vector from some expression. template std::vector &assign(std::vector &arr, Expr const &expr) { evaluate(arr.begin(), arr.end(), proto::as_expr(expr), assign_op()); return arr; } // Add-assign to a list from some expression. template std::list &assign(std::list &arr, Expr const &expr) { evaluate(arr.begin(), arr.end(), proto::as_expr(expr), assign_op()); return arr; } // Add-assign to a vector from some expression. template std::vector &operator +=(std::vector &arr, Expr const &expr) { evaluate(arr.begin(), arr.end(), proto::as_expr(expr), plus_assign_op()); return arr; } // Add-assign to a list from some expression. template std::list &operator +=(std::list &arr, Expr const &expr) { evaluate(arr.begin(), arr.end(), proto::as_expr(expr), plus_assign_op()); return arr; } // Minus-assign to a vector from some expression. template std::vector &operator -=(std::vector &arr, Expr const &expr) { evaluate(arr.begin(), arr.end(), proto::as_expr(expr), minus_assign_op()); return arr; } // Minus-assign to a list from some expression. template std::list &operator -=(std::list &arr, Expr const &expr) { evaluate(arr.begin(), arr.end(), proto::as_expr(expr), minus_assign_op()); return arr; } } int main() { using namespace MixedOps; int n = 10; std::vector a,b,c,d; std::list e; std::list > f; int i; for(i = 0;i < n; ++i) { a.push_back(i); b.push_back(2*i); c.push_back(3*i); d.push_back(i); e.push_back(0.0); f.push_back(std::complex(1.0, 1.0)); } MixedOps::assign(b, 2); MixedOps::assign(d, a + b * c); a += if_else(d < 30, b, c); MixedOps::assign(e, c); e += e - 4 / (c + 1); f -= sin(0.1 * e * std::complex(0.2, 1.2)); std::list::const_iterator ei = e.begin(); std::list >::const_iterator fi = f.begin(); for (i = 0; i < n; ++i) { std::cout << "a(" << i << ") = " << a[i] << " b(" << i << ") = " << b[i] << " c(" << i << ") = " << c[i] << " d(" << i << ") = " << d[i] << " e(" << i << ") = " << *ei++ << " f(" << i << ") = " << *fi++ << std::endl; } } //]