1 //[ LazyVector
2 ///////////////////////////////////////////////////////////////////////////////
3 //  Copyright 2008 Eric Niebler. Distributed under the Boost
4 //  Software License, Version 1.0. (See accompanying file
5 //  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // This example constructs a mini-library for linear algebra, using
8 // expression templates to eliminate the need for temporaries when
9 // adding vectors of numbers.
10 //
11 // This example uses a domain with a grammar to prune the set
12 // of overloaded operators. Only those operators that produce
13 // valid lazy vector expressions are allowed.
14 
15 #include <vector>
16 #include <iostream>
17 #include <boost/mpl/int.hpp>
18 #include <boost/proto/core.hpp>
19 #include <boost/proto/context.hpp>
20 namespace mpl = boost::mpl;
21 namespace proto = boost::proto;
22 using proto::_;
23 
24 template<typename Expr>
25 struct lazy_vector_expr;
26 
27 // This grammar describes which lazy vector expressions
28 // are allowed; namely, vector terminals and addition
29 // and subtraction of lazy vector expressions.
30 struct LazyVectorGrammar
31   : proto::or_<
32         proto::terminal< std::vector<_> >
33       , proto::plus< LazyVectorGrammar, LazyVectorGrammar >
34       , proto::minus< LazyVectorGrammar, LazyVectorGrammar >
35     >
36 {};
37 
38 // Tell proto that in the lazy_vector_domain, all
39 // expressions should be wrapped in laxy_vector_expr<>
40 // and must conform to the lazy vector grammar.
41 struct lazy_vector_domain
42   : proto::domain<proto::generator<lazy_vector_expr>, LazyVectorGrammar>
43 {};
44 
45 // Here is an evaluation context that indexes into a lazy vector
46 // expression, and combines the result.
47 template<typename Size = std::size_t>
48 struct lazy_subscript_context
49 {
lazy_subscript_contextlazy_subscript_context50     lazy_subscript_context(Size subscript)
51       : subscript_(subscript)
52     {}
53 
54     // Use default_eval for all the operations ...
55     template<typename Expr, typename Tag = typename Expr::proto_tag>
56     struct eval
57       : proto::default_eval<Expr, lazy_subscript_context>
58     {};
59 
60     // ... except for terminals, which we index with our subscript
61     template<typename Expr>
62     struct eval<Expr, proto::tag::terminal>
63     {
64         typedef typename proto::result_of::value<Expr>::type::value_type result_type;
65 
operator ()lazy_subscript_context::eval66         result_type operator ()( Expr const & expr, lazy_subscript_context & ctx ) const
67         {
68             return proto::value( expr )[ ctx.subscript_ ];
69         }
70     };
71 
72     Size subscript_;
73 };
74 
75 // Here is the domain-specific expression wrapper, which overrides
76 // operator [] to evaluate the expression using the lazy_subscript_context.
77 template<typename Expr>
78 struct lazy_vector_expr
79   : proto::extends<Expr, lazy_vector_expr<Expr>, lazy_vector_domain>
80 {
lazy_vector_exprlazy_vector_expr81     lazy_vector_expr( Expr const & expr = Expr() )
82       : lazy_vector_expr::proto_extends( expr )
83     {}
84 
85     // Use the lazy_subscript_context<> to implement subscripting
86     // of a lazy vector expression tree.
87     template< typename Size >
88     typename proto::result_of::eval< Expr, lazy_subscript_context<Size> >::type
operator []lazy_vector_expr89     operator []( Size subscript ) const
90     {
91         lazy_subscript_context<Size> ctx(subscript);
92         return proto::eval(*this, ctx);
93     }
94 };
95 
96 // Here is our lazy_vector terminal, implemented in terms of lazy_vector_expr
97 template< typename T >
98 struct lazy_vector
99   : lazy_vector_expr< typename proto::terminal< std::vector<T> >::type >
100 {
101     typedef typename proto::terminal< std::vector<T> >::type expr_type;
102 
lazy_vectorlazy_vector103     lazy_vector( std::size_t size = 0, T const & value = T() )
104       : lazy_vector_expr<expr_type>( expr_type::make( std::vector<T>( size, value ) ) )
105     {}
106 
107     // Here we define a += operator for lazy vector terminals that
108     // takes a lazy vector expression and indexes it. expr[i] here
109     // uses lazy_subscript_context<> under the covers.
110     template< typename Expr >
operator +=lazy_vector111     lazy_vector &operator += (Expr const & expr)
112     {
113         std::size_t size = proto::value(*this).size();
114         for(std::size_t i = 0; i < size; ++i)
115         {
116             proto::value(*this)[i] += expr[i];
117         }
118         return *this;
119     }
120 };
121 
main()122 int main()
123 {
124     // lazy_vectors with 4 elements each.
125     lazy_vector< double > v1( 4, 1.0 ), v2( 4, 2.0 ), v3( 4, 3.0 );
126 
127     // Add two vectors lazily and get the 2nd element.
128     double d1 = ( v2 + v3 )[ 2 ];   // Look ma, no temporaries!
129     std::cout << d1 << std::endl;
130 
131     // Subtract two vectors and add the result to a third vector.
132     v1 += v2 - v3;                  // Still no temporaries!
133     std::cout << '{' << v1[0] << ',' << v1[1]
134               << ',' << v1[2] << ',' << v1[3] << '}' << std::endl;
135 
136     // This expression is disallowed because it does not conform
137     // to the LazyVectorGrammar
138     //(v2 + v3) += v1;
139 
140     return 0;
141 }
142 //]
143