//[ LazyVector /////////////////////////////////////////////////////////////////////////////// // 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 example constructs a mini-library for linear algebra, using // expression templates to eliminate the need for temporaries when // adding vectors of numbers. // // This example uses a domain with a grammar to prune the set // of overloaded operators. Only those operators that produce // valid lazy vector expressions are allowed. #include <vector> #include <iostream> #include <boost/mpl/int.hpp> #include <boost/proto/core.hpp> #include <boost/proto/context.hpp> namespace mpl = boost::mpl; namespace proto = boost::proto; using proto::_; // This grammar describes which lazy vector expressions // are allowed; namely, vector terminals and addition // and subtraction of lazy vector expressions. struct LazyVectorGrammar : proto::or_< proto::terminal< std::vector<_> > , proto::plus< LazyVectorGrammar, LazyVectorGrammar > , proto::minus< LazyVectorGrammar, LazyVectorGrammar > > {}; // Expressions in the lazy vector domain must conform // to the lazy vector grammar struct lazy_vector_domain; // Here is an evaluation context that indexes into a lazy vector // expression, and combines the result. template<typename Size = std::size_t> struct lazy_subscript_context { lazy_subscript_context(Size subscript) : subscript_(subscript) {} // Use default_eval for all the operations ... template<typename Expr, typename Tag = typename Expr::proto_tag> struct eval : proto::default_eval<Expr, lazy_subscript_context> {}; // ... except for terminals, which we index with our subscript template<typename Expr> struct eval<Expr, proto::tag::terminal> { typedef typename proto::result_of::value<Expr>::type::value_type result_type; result_type operator ()( Expr const & expr, lazy_subscript_context & ctx ) const { return proto::value( expr )[ ctx.subscript_ ]; } }; Size subscript_; }; // Here is the domain-specific expression wrapper, which overrides // operator [] to evaluate the expression using the lazy_subscript_context. template<typename Expr> struct lazy_vector_expr : proto::extends<Expr, lazy_vector_expr<Expr>, lazy_vector_domain> { typedef proto::extends<Expr, lazy_vector_expr<Expr>, lazy_vector_domain> base_type; lazy_vector_expr( Expr const & expr = Expr() ) : base_type( expr ) {} // Use the lazy_subscript_context<> to implement subscripting // of a lazy vector expression tree. template< typename Size > typename proto::result_of::eval< Expr, lazy_subscript_context<Size> >::type operator []( Size subscript ) const { lazy_subscript_context<Size> ctx(subscript); return proto::eval(*this, ctx); } }; // Here is our lazy_vector terminal, implemented in terms of lazy_vector_expr template< typename T > struct lazy_vector : lazy_vector_expr< typename proto::terminal< std::vector<T> >::type > { typedef typename proto::terminal< std::vector<T> >::type expr_type; lazy_vector( std::size_t size = 0, T const & value = T() ) : lazy_vector_expr<expr_type>( expr_type::make( std::vector<T>( size, value ) ) ) {} // Here we define a += operator for lazy vector terminals that // takes a lazy vector expression and indexes it. expr[i] here // uses lazy_subscript_context<> under the covers. template< typename Expr > lazy_vector &operator += (Expr const & expr) { std::size_t size = proto::value(*this).size(); for(std::size_t i = 0; i < size; ++i) { proto::value(*this)[i] += expr[i]; } return *this; } }; // Tell proto that in the lazy_vector_domain, all // expressions should be wrapped in laxy_vector_expr<> struct lazy_vector_domain : proto::domain<proto::generator<lazy_vector_expr>, LazyVectorGrammar> {}; int main() { // lazy_vectors with 4 elements each. lazy_vector< double > v1( 4, 1.0 ), v2( 4, 2.0 ), v3( 4, 3.0 ); // Add two vectors lazily and get the 2nd element. double d1 = ( v2 + v3 )[ 2 ]; // Look ma, no temporaries! std::cout << d1 << std::endl; // Subtract two vectors and add the result to a third vector. v1 += v2 - v3; // Still no temporaries! std::cout << '{' << v1[0] << ',' << v1[1] << ',' << v1[2] << ',' << v1[3] << '}' << std::endl; // This expression is disallowed because it does not conform // to the LazyVectorGrammar //(v2 + v3) += v1; return 0; } //]