]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /////////////////////////////////////////////////////////////////////////////// |
2 | // examples.hpp | |
3 | // | |
4 | // Copyright 2008 Eric Niebler. Distributed under the Boost | |
5 | // Software License, Version 1.0. (See accompanying file | |
6 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
7 | ||
8 | #include <iostream> | |
9 | #include <boost/config.hpp> | |
10 | #include <boost/mpl/min_max.hpp> | |
11 | #include <boost/proto/core.hpp> | |
12 | #include <boost/proto/transform.hpp> | |
13 | #include <boost/proto/functional/fusion.hpp> | |
14 | #include <boost/utility/result_of.hpp> | |
15 | #include <boost/fusion/include/cons.hpp> | |
16 | #include <boost/fusion/include/tuple.hpp> | |
17 | #include <boost/fusion/include/pop_front.hpp> | |
18 | #include <boost/test/unit_test.hpp> | |
19 | ||
20 | namespace mpl = boost::mpl; | |
21 | namespace proto = boost::proto; | |
22 | namespace fusion = boost::fusion; | |
23 | using proto::_; | |
24 | ||
25 | template<int I> | |
26 | struct placeholder | |
27 | {}; | |
28 | ||
29 | namespace test1 | |
30 | { | |
31 | //[ CalcGrammar | |
32 | // This is the grammar for calculator expressions, | |
33 | // to which we will attach transforms for computing | |
34 | // the expressions' arity. | |
35 | /*<< A Calculator expression is ... >>*/ | |
36 | struct CalcArity | |
37 | : proto::or_< | |
38 | /*<< _1, or ... >>*/ | |
39 | proto::terminal< placeholder<0> > | |
40 | /*<< _2, or ... >>*/ | |
41 | , proto::terminal< placeholder<1> > | |
42 | /*<< some other terminal, or ... >>*/ | |
43 | , proto::terminal< _ > | |
44 | /*<< a unary expression where the operand is a calculator expression, or ... >>*/ | |
45 | , proto::unary_expr< _, CalcArity > | |
46 | /*<< a binary expression where the operands are calculator expressions >>*/ | |
47 | , proto::binary_expr< _, CalcArity, CalcArity > | |
48 | > | |
49 | {}; | |
50 | //] | |
51 | } | |
52 | ||
53 | //[ binary_arity | |
54 | /*<< The `CalculatorArity` is a transform for calculating | |
55 | the arity of a calculator expression. It will be define in | |
56 | terms of `binary_arity`, which is defined in terms of | |
57 | `CalculatorArity`; hence, the definition is recursive.>>*/ | |
58 | struct CalculatorArity; | |
59 | ||
60 | // A custom transform that returns the arity of a unary | |
61 | // calculator expression by finding the arity of the | |
62 | // child expression. | |
63 | struct unary_arity | |
64 | /*<< Custom transforms should inherit from | |
65 | transform<>. In some cases, (e.g., when the transform | |
66 | is a template), it is also necessary to specialize | |
67 | the proto::is_callable<> trait. >>*/ | |
68 | : proto::transform<unary_arity> | |
69 | { | |
70 | template<typename Expr, typename State, typename Data> | |
71 | /*<< Transforms have a nested `impl<>` that is | |
72 | a valid TR1 function object. >>*/ | |
73 | struct impl | |
74 | : proto::transform_impl<Expr, State, Data> | |
75 | { | |
76 | /*<< Get the child. >>*/ | |
77 | typedef typename proto::result_of::child<Expr>::type child_expr; | |
78 | ||
79 | /*<< Apply `CalculatorArity` to find the arity of the child. >>*/ | |
80 | typedef typename boost::result_of<CalculatorArity(child_expr, State, Data)>::type result_type; | |
81 | ||
82 | /*<< The `unary_arity` transform doesn't have an interesting | |
83 | runtime counterpart, so just return a default-constructed object | |
84 | of the correct type. >>*/ | |
85 | result_type operator ()(proto::ignore, proto::ignore, proto::ignore) const | |
86 | { | |
87 | return result_type(); | |
88 | } | |
89 | }; | |
90 | }; | |
91 | ||
92 | // A custom transform that returns the arity of a binary | |
93 | // calculator expression by finding the maximum of the | |
94 | // arities of the mpl::int_<2> child expressions. | |
95 | struct binary_arity | |
96 | /*<< All custom transforms should inherit from | |
97 | transform. In some cases, (e.g., when the transform | |
98 | is a template), it is also necessary to specialize | |
99 | the proto::is_callable<> trait. >>*/ | |
100 | : proto::transform<binary_arity> | |
101 | { | |
102 | template<typename Expr, typename State, typename Data> | |
103 | /*<< Transforms have a nested `impl<>` that is | |
104 | a valid TR1 function object. >>*/ | |
105 | struct impl | |
106 | : proto::transform_impl<Expr, State, Data> | |
107 | { | |
108 | /*<< Get the left and right children. >>*/ | |
109 | typedef typename proto::result_of::left<Expr>::type left_expr; | |
110 | typedef typename proto::result_of::right<Expr>::type right_expr; | |
111 | ||
112 | /*<< Apply `CalculatorArity` to find the arity of the left and right children. >>*/ | |
113 | typedef typename boost::result_of<CalculatorArity(left_expr, State, Data)>::type left_arity; | |
114 | typedef typename boost::result_of<CalculatorArity(right_expr, State, Data)>::type right_arity; | |
115 | ||
116 | /*<< The return type is the maximum of the children's arities. >>*/ | |
117 | typedef typename mpl::max<left_arity, right_arity>::type result_type; | |
118 | ||
119 | /*<< The `unary_arity` transform doesn't have an interesting | |
120 | runtime counterpart, so just return a default-constructed object | |
121 | of the correct type. >>*/ | |
122 | result_type operator ()(proto::ignore, proto::ignore, proto::ignore) const | |
123 | { | |
124 | return result_type(); | |
125 | } | |
126 | }; | |
127 | }; | |
128 | //] | |
129 | ||
92f5a8d4 TL |
130 | proto::terminal< placeholder<0> >::type const _1 = {}; |
131 | proto::terminal< placeholder<1> >::type const _2 = {}; | |
7c673cae FG |
132 | |
133 | //[ CalculatorArityGrammar | |
134 | struct CalculatorArity | |
135 | : proto::or_< | |
136 | proto::when< proto::terminal< placeholder<0> >, mpl::int_<1>() > | |
137 | , proto::when< proto::terminal< placeholder<1> >, mpl::int_<2>() > | |
138 | , proto::when< proto::terminal<_>, mpl::int_<0>() > | |
139 | , proto::when< proto::unary_expr<_, _>, unary_arity > | |
140 | , proto::when< proto::binary_expr<_, _, _>, binary_arity > | |
141 | > | |
142 | {}; | |
143 | //] | |
144 | ||
145 | //[ CalcArity | |
146 | struct CalcArity | |
147 | : proto::or_< | |
148 | proto::when< proto::terminal< placeholder<0> >, | |
149 | mpl::int_<1>() | |
150 | > | |
151 | , proto::when< proto::terminal< placeholder<1> >, | |
152 | mpl::int_<2>() | |
153 | > | |
154 | , proto::when< proto::terminal<_>, | |
155 | mpl::int_<0>() | |
156 | > | |
157 | , proto::when< proto::unary_expr<_, CalcArity>, | |
158 | CalcArity(proto::_child) | |
159 | > | |
160 | , proto::when< proto::binary_expr<_, CalcArity, CalcArity>, | |
161 | mpl::max<CalcArity(proto::_left), | |
162 | CalcArity(proto::_right)>() | |
163 | > | |
164 | > | |
165 | {}; | |
166 | //] | |
167 | ||
168 | // BUGBUG find workaround for this | |
169 | #if BOOST_WORKAROUND(BOOST_MSVC, == 1310) | |
170 | #define _pop_front(x) call<proto::_pop_front(x)> | |
171 | #define _value(x) call<proto::_value(x)> | |
172 | #endif | |
173 | ||
174 | //[ AsArgList | |
175 | // This transform matches function invocations such as foo(1,'a',"b") | |
176 | // and transforms them into Fusion cons lists of their arguments. In this | |
177 | // case, the result would be cons(1, cons('a', cons("b", nil()))). | |
178 | struct ArgsAsList | |
179 | : proto::when< | |
180 | proto::function<proto::terminal<_>, proto::vararg<proto::terminal<_> > > | |
92f5a8d4 TL |
181 | /*<< Use a `fold<>` transform to iterate over the children of this |
182 | node in forward order, building a fusion list from front to back. >>*/ | |
183 | , proto::fold< | |
7c673cae FG |
184 | /*<< The first child expression of a `function<>` node is the |
185 | function being invoked. We don't want that in our list, so use | |
186 | `pop_front()` to remove it. >>*/ | |
187 | proto::_pop_front(_) | |
92f5a8d4 | 188 | /*<< `nil` is the initial state used by the `fold<>` transform. >>*/ |
7c673cae FG |
189 | , fusion::nil() |
190 | /*<< Put the rest of the function arguments in a fusion cons | |
191 | list. >>*/ | |
92f5a8d4 | 192 | , proto::functional::push_back(proto::_state, proto::_value) |
7c673cae FG |
193 | > |
194 | > | |
195 | {}; | |
196 | //] | |
197 | ||
198 | //[ FoldTreeToList | |
199 | // This transform matches expressions of the form (_1=1,'a',"b") | |
200 | // (note the use of the comma operator) and transforms it into a | |
201 | // Fusion cons list of their arguments. In this case, the result | |
202 | // would be cons(1, cons('a', cons("b", nil()))). | |
203 | struct FoldTreeToList | |
204 | : proto::or_< | |
205 | // This grammar describes what counts as the terminals in expressions | |
206 | // of the form (_1=1,'a',"b"), which will be flattened using | |
207 | // reverse_fold_tree<> below. | |
208 | proto::when< proto::assign<_, proto::terminal<_> > | |
209 | , proto::_value(proto::_right) | |
210 | > | |
211 | , proto::when< proto::terminal<_> | |
212 | , proto::_value | |
213 | > | |
214 | , proto::when< | |
215 | proto::comma<FoldTreeToList, FoldTreeToList> | |
216 | /*<< Fold all terminals that are separated by commas into a Fusion cons list. >>*/ | |
217 | , proto::reverse_fold_tree< | |
218 | _ | |
219 | , fusion::nil() | |
220 | , fusion::cons<FoldTreeToList, proto::_state>(FoldTreeToList, proto::_state) | |
221 | > | |
222 | > | |
223 | > | |
224 | {}; | |
225 | //] | |
226 | ||
227 | //[ Promote | |
228 | // This transform finds all float terminals in an expression and promotes | |
229 | // them to doubles. | |
230 | struct Promote | |
231 | : proto::or_< | |
232 | /*<< Match a `terminal<float>`, then construct a | |
233 | `terminal<double>::type` with the `float`. >>*/ | |
234 | proto::when<proto::terminal<float>, proto::terminal<double>::type(proto::_value) > | |
235 | , proto::when<proto::terminal<_> > | |
236 | /*<< `nary_expr<>` has a pass-through transform which | |
237 | will transform each child sub-expression using the | |
238 | `Promote` transform. >>*/ | |
239 | , proto::when<proto::nary_expr<_, proto::vararg<Promote> > > | |
240 | > | |
241 | {}; | |
242 | //] | |
243 | ||
244 | //[ LazyMakePair | |
245 | struct make_pair_tag {}; | |
246 | proto::terminal<make_pair_tag>::type const make_pair_ = {{}}; | |
247 | ||
248 | // This transform matches lazy function invocations like | |
249 | // `make_pair_(1, 3.14)` and actually builds a `std::pair<>` | |
250 | // from the arguments. | |
251 | struct MakePair | |
252 | : proto::when< | |
253 | /*<< Match expressions like `make_pair_(1, 3.14)` >>*/ | |
254 | proto::function< | |
255 | proto::terminal<make_pair_tag> | |
256 | , proto::terminal<_> | |
257 | , proto::terminal<_> | |
258 | > | |
259 | /*<< Return `std::pair<F,S>(f,s)` where `f` and `s` are the | |
260 | first and second arguments to the lazy `make_pair_()` function. | |
261 | (This uses `proto::make<>` under the covers to evaluate the | |
262 | transform.)>>*/ | |
263 | , std::pair< | |
264 | proto::_value(proto::_child1) | |
265 | , proto::_value(proto::_child2) | |
266 | >( | |
267 | proto::_value(proto::_child1) | |
268 | , proto::_value(proto::_child2) | |
269 | ) | |
270 | > | |
271 | {}; | |
272 | //] | |
273 | ||
274 | namespace lazy_make_pair2 | |
275 | { | |
276 | //[ LazyMakePair2 | |
277 | struct make_pair_tag {}; | |
278 | proto::terminal<make_pair_tag>::type const make_pair_ = {{}}; | |
279 | ||
280 | // Like std::make_pair(), only as a function object. | |
281 | /*<<Inheriting from `proto::callable` lets Proto know | |
282 | that this is a callable transform, so we can use it | |
283 | without having to wrap it in `proto::call<>`.>>*/ | |
284 | struct make_pair : proto::callable | |
285 | { | |
286 | template<typename Sig> struct result; | |
287 | ||
288 | template<typename This, typename First, typename Second> | |
289 | struct result<This(First, Second)> | |
290 | { | |
291 | typedef | |
292 | std::pair< | |
293 | BOOST_PROTO_UNCVREF(First) | |
294 | , BOOST_PROTO_UNCVREF(Second) | |
295 | > | |
296 | type; | |
297 | }; | |
298 | ||
299 | template<typename First, typename Second> | |
300 | std::pair<First, Second> | |
301 | operator()(First const &first, Second const &second) const | |
302 | { | |
303 | return std::make_pair(first, second); | |
304 | } | |
305 | }; | |
306 | ||
307 | // This transform matches lazy function invocations like | |
308 | // `make_pair_(1, 3.14)` and actually builds a `std::pair<>` | |
309 | // from the arguments. | |
310 | struct MakePair | |
311 | : proto::when< | |
312 | /*<< Match expressions like `make_pair_(1, 3.14)` >>*/ | |
313 | proto::function< | |
314 | proto::terminal<make_pair_tag> | |
315 | , proto::terminal<_> | |
316 | , proto::terminal<_> | |
317 | > | |
318 | /*<< Return `make_pair()(f,s)` where `f` and `s` are the | |
319 | first and second arguments to the lazy `make_pair_()` function. | |
320 | (This uses `proto::call<>` under the covers to evaluate the | |
321 | transform.)>>*/ | |
322 | , make_pair( | |
323 | proto::_value(proto::_child1) | |
324 | , proto::_value(proto::_child2) | |
325 | ) | |
326 | > | |
327 | {}; | |
328 | //] | |
329 | } | |
330 | ||
331 | ||
332 | //[ NegateInt | |
333 | struct NegateInt | |
334 | : proto::when<proto::terminal<int>, proto::negate<_>(_)> | |
335 | {}; | |
336 | //] | |
337 | ||
338 | #ifndef BOOST_MSVC | |
339 | //[ SquareAndPromoteInt | |
340 | struct SquareAndPromoteInt | |
341 | : proto::when< | |
342 | proto::terminal<int> | |
343 | , proto::_make_multiplies( | |
344 | proto::terminal<long>::type(proto::_value) | |
345 | , proto::terminal<long>::type(proto::_value) | |
346 | ) | |
347 | > | |
348 | {}; | |
349 | //] | |
350 | #endif | |
351 | ||
352 | namespace lambda_transform | |
353 | { | |
354 | //[LambdaTransform | |
355 | template<typename N> | |
92f5a8d4 | 356 | struct placeholder : N {}; |
7c673cae FG |
357 | |
358 | // A function object that calls fusion::at() | |
359 | struct at : proto::callable | |
360 | { | |
361 | template<typename Sig> | |
362 | struct result; | |
363 | ||
364 | template<typename This, typename Cont, typename Index> | |
365 | struct result<This(Cont, Index)> | |
366 | : fusion::result_of::at< | |
367 | typename boost::remove_reference<Cont>::type | |
368 | , typename boost::remove_reference<Index>::type | |
369 | > | |
370 | {}; | |
371 | ||
372 | template<typename Cont, typename Index> | |
373 | typename fusion::result_of::at<Cont, Index>::type | |
374 | operator ()(Cont &cont, Index const &) const | |
375 | { | |
376 | return fusion::at<Index>(cont); | |
377 | } | |
378 | }; | |
379 | ||
380 | // A transform that evaluates a lambda expression. | |
381 | struct LambdaEval | |
382 | : proto::or_< | |
383 | /*<<When you match a placeholder ...>>*/ | |
384 | proto::when< | |
385 | proto::terminal<placeholder<_> > | |
386 | /*<<... call at() with the data parameter, which | |
387 | is a tuple, and the placeholder, which is an MPL | |
388 | Integral Constant.>>*/ | |
389 | , at(proto::_data, proto::_value) | |
390 | > | |
391 | /*<<Otherwise, use the _default<> transform, which | |
392 | gives the operators their usual C++ meanings.>>*/ | |
393 | , proto::otherwise< proto::_default<LambdaEval> > | |
394 | > | |
395 | {}; | |
396 | ||
397 | // Define the lambda placeholders | |
92f5a8d4 TL |
398 | proto::terminal<placeholder<mpl::int_<0> > >::type const _1 = {}; |
399 | proto::terminal<placeholder<mpl::int_<1> > >::type const _2 = {}; | |
7c673cae FG |
400 | |
401 | void test_lambda() | |
402 | { | |
403 | // a tuple that contains the values | |
404 | // of _1 and _2 | |
405 | fusion::tuple<int, int> tup(2,3); | |
406 | ||
407 | // Use LambdaEval to evaluate a lambda expression | |
408 | int j = LambdaEval()( _2 - _1, 0, tup ); | |
409 | BOOST_CHECK_EQUAL(j, 1); | |
410 | ||
411 | // You can mutate leaves in an expression tree | |
412 | proto::literal<int> k(42); | |
413 | int &l = LambdaEval()( k += 4, 0, tup ); | |
414 | BOOST_CHECK_EQUAL(k.get(), 46); | |
415 | BOOST_CHECK_EQUAL(&l, &k.get()); | |
416 | ||
417 | // You can mutate the values in the tuple, too. | |
418 | LambdaEval()( _1 += 4, 0, tup ); | |
419 | BOOST_CHECK_EQUAL(6, fusion::at_c<0>(tup)); | |
420 | } | |
421 | //] | |
422 | } | |
423 | ||
424 | void test_examples() | |
425 | { | |
426 | //[ CalculatorArityTest | |
427 | int i = 0; // not used, dummy state and data parameter | |
428 | ||
429 | std::cout << CalculatorArity()( proto::lit(100) * 200, i, i) << '\n'; | |
430 | std::cout << CalculatorArity()( (_1 - _1) / _1 * 100, i, i) << '\n'; | |
431 | std::cout << CalculatorArity()( (_2 - _1) / _2 * 100, i, i) << '\n'; | |
432 | //] | |
433 | ||
434 | BOOST_CHECK_EQUAL(0, CalculatorArity()( proto::lit(100) * 200, i, i)); | |
435 | BOOST_CHECK_EQUAL(1, CalculatorArity()( (_1 - _1) / _1 * 100, i, i)); | |
436 | BOOST_CHECK_EQUAL(2, CalculatorArity()( (_2 - _1) / _2 * 100, i, i)); | |
437 | ||
438 | BOOST_CHECK_EQUAL(0, CalcArity()( proto::lit(100) * 200, i, i)); | |
439 | BOOST_CHECK_EQUAL(1, CalcArity()( (_1 - _1) / _1 * 100, i, i)); | |
440 | BOOST_CHECK_EQUAL(2, CalcArity()( (_2 - _1) / _2 * 100, i, i)); | |
441 | ||
442 | using boost::fusion::cons; | |
443 | using boost::fusion::nil; | |
444 | cons<int, cons<char, cons<std::string> > > args(ArgsAsList()( _1(1, 'a', std::string("b")), i, i )); | |
445 | BOOST_CHECK_EQUAL(args.car, 1); | |
446 | BOOST_CHECK_EQUAL(args.cdr.car, 'a'); | |
447 | BOOST_CHECK_EQUAL(args.cdr.cdr.car, std::string("b")); | |
448 | ||
449 | cons<int, cons<char, cons<std::string> > > lst(FoldTreeToList()( (_1 = 1, 'a', std::string("b")), i, i )); | |
450 | BOOST_CHECK_EQUAL(lst.car, 1); | |
451 | BOOST_CHECK_EQUAL(lst.cdr.car, 'a'); | |
452 | BOOST_CHECK_EQUAL(lst.cdr.cdr.car, std::string("b")); | |
453 | ||
454 | proto::plus< | |
455 | proto::terminal<double>::type | |
456 | , proto::terminal<double>::type | |
457 | >::type p = Promote()( proto::lit(1.f) + 2.f, i, i ); | |
458 | ||
459 | //[ LazyMakePairTest | |
460 | int j = 0; // not used, dummy state and data parameter | |
461 | ||
462 | std::pair<int, double> p2 = MakePair()( make_pair_(1, 3.14), j, j ); | |
463 | ||
464 | std::cout << p2.first << std::endl; | |
465 | std::cout << p2.second << std::endl; | |
466 | //] | |
467 | ||
468 | BOOST_CHECK_EQUAL(p2.first, 1); | |
469 | BOOST_CHECK_EQUAL(p2.second, 3.14); | |
470 | ||
471 | std::pair<int, double> p3 = lazy_make_pair2::MakePair()( lazy_make_pair2::make_pair_(1, 3.14), j, j ); | |
472 | ||
473 | std::cout << p3.first << std::endl; | |
474 | std::cout << p3.second << std::endl; | |
475 | ||
476 | BOOST_CHECK_EQUAL(p3.first, 1); | |
477 | BOOST_CHECK_EQUAL(p3.second, 3.14); | |
478 | ||
479 | NegateInt()(proto::lit(1), i, i); | |
480 | #ifndef BOOST_MSVC | |
481 | SquareAndPromoteInt()(proto::lit(1), i, i); | |
482 | #endif | |
483 | ||
484 | lambda_transform::test_lambda(); | |
485 | } | |
486 | ||
487 | using namespace boost::unit_test; | |
488 | /////////////////////////////////////////////////////////////////////////////// | |
489 | // init_unit_test_suite | |
490 | // | |
491 | test_suite* init_unit_test_suite( int argc, char* argv[] ) | |
492 | { | |
493 | test_suite *test = BOOST_TEST_SUITE("test examples from the documentation"); | |
494 | ||
495 | test->add(BOOST_TEST_CASE(&test_examples)); | |
496 | ||
497 | return test; | |
498 | } |