//[ Vector /////////////////////////////////////////////////////////////////////////////// // 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<>, a non-proto type. It is a port of the // Vector example from PETE (http://www.codesourcery.com/pooma/download.html). #include <vector> #include <iostream> #include <stdexcept> #include <boost/mpl/bool.hpp> #include <boost/proto/core.hpp> #include <boost/proto/debug.hpp> #include <boost/proto/context.hpp> #include <boost/utility/enable_if.hpp> namespace mpl = boost::mpl; namespace proto = boost::proto; using proto::_; template<typename Expr> struct VectorExpr; // Here is an evaluation context that indexes into a std::vector // expression and combines the result. struct VectorSubscriptCtx { VectorSubscriptCtx(std::size_t i) : i_(i) {} // Unless this is a vector terminal, use the // default evaluation context template<typename Expr, typename EnableIf = void> struct eval : proto::default_eval<Expr, VectorSubscriptCtx const> {}; // Index vector terminals with our subscript. template<typename Expr> struct eval< Expr , typename boost::enable_if< proto::matches<Expr, proto::terminal<std::vector<_, _> > > >::type > { typedef typename proto::result_of::value<Expr>::type::value_type result_type; result_type operator ()(Expr &expr, VectorSubscriptCtx const &ctx) const { return proto::value(expr)[ctx.i_]; } }; std::size_t i_; }; // Here is an evaluation context that verifies that all the // vectors in an expression have the same size. struct VectorSizeCtx { VectorSizeCtx(std::size_t size) : size_(size) {} // Unless this is a vector terminal, use the // null evaluation context template<typename Expr, typename EnableIf = void> struct eval : proto::null_eval<Expr, VectorSizeCtx const> {}; // Index array terminals with our subscript. Everything // else will be handled by the default evaluation context. template<typename Expr> struct eval< Expr , typename boost::enable_if< proto::matches<Expr, proto::terminal<std::vector<_, _> > > >::type > { typedef void result_type; result_type operator ()(Expr &expr, VectorSizeCtx const &ctx) const { if(ctx.size_ != proto::value(expr).size()) { throw std::runtime_error("LHS and RHS are not compatible"); } } }; std::size_t size_; }; // A grammar which matches all the assignment operators, // so we can easily disable them. struct AssignOps : proto::switch_<struct AssignOpsCases> {}; // Here are the cases used by the switch_ above. struct AssignOpsCases { template<typename Tag, int D = 0> struct case_ : proto::not_<_> {}; template<int D> struct case_< proto::tag::plus_assign, D > : _ {}; template<int D> struct case_< proto::tag::minus_assign, D > : _ {}; template<int D> struct case_< proto::tag::multiplies_assign, D > : _ {}; template<int D> struct case_< proto::tag::divides_assign, D > : _ {}; template<int D> struct case_< proto::tag::modulus_assign, D > : _ {}; template<int D> struct case_< proto::tag::shift_left_assign, D > : _ {}; template<int D> struct case_< proto::tag::shift_right_assign, D > : _ {}; template<int D> struct case_< proto::tag::bitwise_and_assign, D > : _ {}; template<int D> struct case_< proto::tag::bitwise_or_assign, D > : _ {}; template<int D> struct case_< proto::tag::bitwise_xor_assign, D > : _ {}; }; // A vector grammar is a terminal or some op that is not an // assignment op. (Assignment will be handled specially.) struct VectorGrammar : proto::or_< proto::terminal<_> , proto::and_<proto::nary_expr<_, proto::vararg<VectorGrammar> >, proto::not_<AssignOps> > > {}; // Expressions in the vector domain will be wrapped in VectorExpr<> // and must conform to the VectorGrammar struct VectorDomain : proto::domain<proto::generator<VectorExpr>, VectorGrammar> {}; // Here is VectorExpr, which extends a proto expr type by // giving it an operator [] which uses the VectorSubscriptCtx // to evaluate an expression with a given index. template<typename Expr> struct VectorExpr : proto::extends<Expr, VectorExpr<Expr>, VectorDomain> { explicit VectorExpr(Expr const &expr) : proto::extends<Expr, VectorExpr<Expr>, VectorDomain>(expr) {} // Use the VectorSubscriptCtx to implement subscripting // of a Vector expression tree. typename proto::result_of::eval<Expr const, VectorSubscriptCtx const>::type operator []( std::size_t i ) const { VectorSubscriptCtx const ctx(i); return proto::eval(*this, ctx); } }; // Define a trait type for detecting vector terminals, to // be used by the BOOST_PROTO_DEFINE_OPERATORS macro below. template<typename T> struct IsVector : mpl::false_ {}; template<typename T, typename A> struct IsVector<std::vector<T, A> > : mpl::true_ {}; namespace VectorOps { // This defines all the overloads to make expressions involving // std::vector to build expression templates. BOOST_PROTO_DEFINE_OPERATORS(IsVector, VectorDomain) typedef VectorSubscriptCtx const CVectorSubscriptCtx; // Assign to a vector from some expression. template<typename T, typename A, typename Expr> std::vector<T, A> &assign(std::vector<T, A> &arr, Expr const &expr) { VectorSizeCtx const size(arr.size()); proto::eval(proto::as_expr<VectorDomain>(expr), size); // will throw if the sizes don't match for(std::size_t i = 0; i < arr.size(); ++i) { arr[i] = proto::as_expr<VectorDomain>(expr)[i]; } return arr; } // Add-assign to a vector from some expression. template<typename T, typename A, typename Expr> std::vector<T, A> &operator +=(std::vector<T, A> &arr, Expr const &expr) { VectorSizeCtx const size(arr.size()); proto::eval(proto::as_expr<VectorDomain>(expr), size); // will throw if the sizes don't match for(std::size_t i = 0; i < arr.size(); ++i) { arr[i] += proto::as_expr<VectorDomain>(expr)[i]; } return arr; } } int main() { using namespace VectorOps; int i; const int n = 10; std::vector<int> a,b,c,d; std::vector<double> e(n); for (i = 0; i < n; ++i) { a.push_back(i); b.push_back(2*i); c.push_back(3*i); d.push_back(i); } VectorOps::assign(b, 2); VectorOps::assign(d, a + b * c); a += if_else(d < 30, b, c); VectorOps::assign(e, c); e += e - 4 / (c + 1); 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 << ") = " << e[i] << std::endl; } } //]