1 //[ Vector
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 is an example of using BOOST_PROTO_DEFINE_OPERATORS to Protofy
8 // expressions using std::vector<>, a non-proto type. It is a port of the
9 // Vector example from PETE (http://www.codesourcery.com/pooma/download.html).
10
11 #include <vector>
12 #include <iostream>
13 #include <stdexcept>
14 #include <boost/mpl/bool.hpp>
15 #include <boost/proto/core.hpp>
16 #include <boost/proto/debug.hpp>
17 #include <boost/proto/context.hpp>
18 #include <boost/utility/enable_if.hpp>
19 namespace mpl = boost::mpl;
20 namespace proto = boost::proto;
21 using proto::_;
22
23 template<typename Expr>
24 struct VectorExpr;
25
26 // Here is an evaluation context that indexes into a std::vector
27 // expression and combines the result.
28 struct VectorSubscriptCtx
29 {
VectorSubscriptCtxVectorSubscriptCtx30 VectorSubscriptCtx(std::size_t i)
31 : i_(i)
32 {}
33
34 // Unless this is a vector terminal, use the
35 // default evaluation context
36 template<typename Expr, typename EnableIf = void>
37 struct eval
38 : proto::default_eval<Expr, VectorSubscriptCtx const>
39 {};
40
41 // Index vector terminals with our subscript.
42 template<typename Expr>
43 struct eval<
44 Expr
45 , typename boost::enable_if<
46 proto::matches<Expr, proto::terminal<std::vector<_, _> > >
47 >::type
48 >
49 {
50 typedef typename proto::result_of::value<Expr>::type::value_type result_type;
51
operator ()VectorSubscriptCtx::eval52 result_type operator ()(Expr &expr, VectorSubscriptCtx const &ctx) const
53 {
54 return proto::value(expr)[ctx.i_];
55 }
56 };
57
58 std::size_t i_;
59 };
60
61 // Here is an evaluation context that verifies that all the
62 // vectors in an expression have the same size.
63 struct VectorSizeCtx
64 {
VectorSizeCtxVectorSizeCtx65 VectorSizeCtx(std::size_t size)
66 : size_(size)
67 {}
68
69 // Unless this is a vector terminal, use the
70 // null evaluation context
71 template<typename Expr, typename EnableIf = void>
72 struct eval
73 : proto::null_eval<Expr, VectorSizeCtx const>
74 {};
75
76 // Index array terminals with our subscript. Everything
77 // else will be handled by the default evaluation context.
78 template<typename Expr>
79 struct eval<
80 Expr
81 , typename boost::enable_if<
82 proto::matches<Expr, proto::terminal<std::vector<_, _> > >
83 >::type
84 >
85 {
86 typedef void result_type;
87
operator ()VectorSizeCtx::eval88 result_type operator ()(Expr &expr, VectorSizeCtx const &ctx) const
89 {
90 if(ctx.size_ != proto::value(expr).size())
91 {
92 throw std::runtime_error("LHS and RHS are not compatible");
93 }
94 }
95 };
96
97 std::size_t size_;
98 };
99
100 // A grammar which matches all the assignment operators,
101 // so we can easily disable them.
102 struct AssignOps
103 : proto::switch_<struct AssignOpsCases>
104 {};
105
106 // Here are the cases used by the switch_ above.
107 struct AssignOpsCases
108 {
109 template<typename Tag, int D = 0> struct case_ : proto::not_<_> {};
110
111 template<int D> struct case_< proto::tag::plus_assign, D > : _ {};
112 template<int D> struct case_< proto::tag::minus_assign, D > : _ {};
113 template<int D> struct case_< proto::tag::multiplies_assign, D > : _ {};
114 template<int D> struct case_< proto::tag::divides_assign, D > : _ {};
115 template<int D> struct case_< proto::tag::modulus_assign, D > : _ {};
116 template<int D> struct case_< proto::tag::shift_left_assign, D > : _ {};
117 template<int D> struct case_< proto::tag::shift_right_assign, D > : _ {};
118 template<int D> struct case_< proto::tag::bitwise_and_assign, D > : _ {};
119 template<int D> struct case_< proto::tag::bitwise_or_assign, D > : _ {};
120 template<int D> struct case_< proto::tag::bitwise_xor_assign, D > : _ {};
121 };
122
123 // A vector grammar is a terminal or some op that is not an
124 // assignment op. (Assignment will be handled specially.)
125 struct VectorGrammar
126 : proto::or_<
127 proto::terminal<_>
128 , proto::and_<proto::nary_expr<_, proto::vararg<VectorGrammar> >, proto::not_<AssignOps> >
129 >
130 {};
131
132 // Expressions in the vector domain will be wrapped in VectorExpr<>
133 // and must conform to the VectorGrammar
134 struct VectorDomain
135 : proto::domain<proto::generator<VectorExpr>, VectorGrammar>
136 {};
137
138 // Here is VectorExpr, which extends a proto expr type by
139 // giving it an operator [] which uses the VectorSubscriptCtx
140 // to evaluate an expression with a given index.
141 template<typename Expr>
142 struct VectorExpr
143 : proto::extends<Expr, VectorExpr<Expr>, VectorDomain>
144 {
VectorExprVectorExpr145 explicit VectorExpr(Expr const &expr)
146 : proto::extends<Expr, VectorExpr<Expr>, VectorDomain>(expr)
147 {}
148
149 // Use the VectorSubscriptCtx to implement subscripting
150 // of a Vector expression tree.
151 typename proto::result_of::eval<Expr const, VectorSubscriptCtx const>::type
operator []VectorExpr152 operator []( std::size_t i ) const
153 {
154 VectorSubscriptCtx const ctx(i);
155 return proto::eval(*this, ctx);
156 }
157 };
158
159 // Define a trait type for detecting vector terminals, to
160 // be used by the BOOST_PROTO_DEFINE_OPERATORS macro below.
161 template<typename T>
162 struct IsVector
163 : mpl::false_
164 {};
165
166 template<typename T, typename A>
167 struct IsVector<std::vector<T, A> >
168 : mpl::true_
169 {};
170
171 namespace VectorOps
172 {
173 // This defines all the overloads to make expressions involving
174 // std::vector to build expression templates.
175 BOOST_PROTO_DEFINE_OPERATORS(IsVector, VectorDomain)
176
177 typedef VectorSubscriptCtx const CVectorSubscriptCtx;
178
179 // Assign to a vector from some expression.
180 template<typename T, typename A, typename Expr>
assign(std::vector<T,A> & arr,Expr const & expr)181 std::vector<T, A> &assign(std::vector<T, A> &arr, Expr const &expr)
182 {
183 VectorSizeCtx const size(arr.size());
184 proto::eval(proto::as_expr<VectorDomain>(expr), size); // will throw if the sizes don't match
185 for(std::size_t i = 0; i < arr.size(); ++i)
186 {
187 arr[i] = proto::as_expr<VectorDomain>(expr)[i];
188 }
189 return arr;
190 }
191
192 // Add-assign to a vector from some expression.
193 template<typename T, typename A, typename Expr>
operator +=(std::vector<T,A> & arr,Expr const & expr)194 std::vector<T, A> &operator +=(std::vector<T, A> &arr, Expr const &expr)
195 {
196 VectorSizeCtx const size(arr.size());
197 proto::eval(proto::as_expr<VectorDomain>(expr), size); // will throw if the sizes don't match
198 for(std::size_t i = 0; i < arr.size(); ++i)
199 {
200 arr[i] += proto::as_expr<VectorDomain>(expr)[i];
201 }
202 return arr;
203 }
204 }
205
main()206 int main()
207 {
208 using namespace VectorOps;
209
210 int i;
211 const int n = 10;
212 std::vector<int> a,b,c,d;
213 std::vector<double> e(n);
214
215 for (i = 0; i < n; ++i)
216 {
217 a.push_back(i);
218 b.push_back(2*i);
219 c.push_back(3*i);
220 d.push_back(i);
221 }
222
223 VectorOps::assign(b, 2);
224 VectorOps::assign(d, a + b * c);
225 a += if_else(d < 30, b, c);
226
227 VectorOps::assign(e, c);
228 e += e - 4 / (c + 1);
229
230 for (i = 0; i < n; ++i)
231 {
232 std::cout
233 << " a(" << i << ") = " << a[i]
234 << " b(" << i << ") = " << b[i]
235 << " c(" << i << ") = " << c[i]
236 << " d(" << i << ") = " << d[i]
237 << " e(" << i << ") = " << e[i]
238 << std::endl;
239 }
240 }
241 //]
242