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