1 //[ FutureGroup
2 // Copyright 2008 Eric Niebler. Distributed under the Boost
3 // Software License, Version 1.0. (See accompanying file
4 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 //
6 // This is an example of using Proto transforms to implement
7 // Howard Hinnant's future group proposal.
8
9 #include <boost/fusion/include/vector.hpp>
10 #include <boost/fusion/include/as_vector.hpp>
11 #include <boost/fusion/include/joint_view.hpp>
12 #include <boost/fusion/include/single_view.hpp>
13 #include <boost/proto/core.hpp>
14 #include <boost/proto/transform.hpp>
15 namespace mpl = boost::mpl;
16 namespace proto = boost::proto;
17 namespace fusion = boost::fusion;
18 using proto::_;
19
20 template<class L,class R>
21 struct pick_left
22 {
23 BOOST_MPL_ASSERT((boost::is_same<L, R>));
24 typedef L type;
25 };
26
27 // Work-arounds for Microsoft Visual C++ 7.1
28 #if BOOST_WORKAROUND(BOOST_MSVC, == 1310)
29 #define FutureGroup(x) proto::call<FutureGroup(x)>
30 #endif
31
32 // Define the grammar of future group expression, as well as a
33 // transform to turn them into a Fusion sequence of the correct
34 // type.
35 struct FutureGroup
36 : proto::or_<
37 // terminals become a single-element Fusion sequence
38 proto::when<
39 proto::terminal<_>
40 , fusion::single_view<proto::_value>(proto::_value)
41 >
42 // (a && b) becomes a concatenation of the sequence
43 // from 'a' and the one from 'b':
44 , proto::when<
45 proto::logical_and<FutureGroup, FutureGroup>
46 , fusion::joint_view<
47 boost::add_const<FutureGroup(proto::_left) >
48 , boost::add_const<FutureGroup(proto::_right) >
49 >(FutureGroup(proto::_left), FutureGroup(proto::_right))
50 >
51 // (a || b) becomes the sequence for 'a', so long
52 // as it is the same as the sequence for 'b'.
53 , proto::when<
54 proto::logical_or<FutureGroup, FutureGroup>
55 , pick_left<
56 FutureGroup(proto::_left)
57 , FutureGroup(proto::_right)
58 >(FutureGroup(proto::_left))
59 >
60 >
61 {};
62
63 #if BOOST_WORKAROUND(BOOST_MSVC, == 1310)
64 #undef FutureGroup
65 #endif
66
67 template<class E>
68 struct future_expr;
69
70 struct future_dom
71 : proto::domain<proto::generator<future_expr>, FutureGroup>
72 {};
73
74 // Expressions in the future group domain have a .get()
75 // member function that (ostensibly) blocks for the futures
76 // to complete and returns the results in an appropriate
77 // tuple.
78 template<class E>
79 struct future_expr
80 : proto::extends<E, future_expr<E>, future_dom>
81 {
future_exprfuture_expr82 explicit future_expr(E const &e)
83 : future_expr::proto_extends(e)
84 {}
85
86 typename fusion::result_of::as_vector<
87 typename boost::result_of<FutureGroup(E)>::type
88 >::type
getfuture_expr89 get() const
90 {
91 return fusion::as_vector(FutureGroup()(*this));
92 }
93 };
94
95 // The future<> type has an even simpler .get()
96 // member function.
97 template<class T>
98 struct future
99 : future_expr<typename proto::terminal<T>::type>
100 {
futurefuture101 future(T const &t = T())
102 : future::proto_derived_expr(future::proto_base_expr::make(t))
103 {}
104
getfuture105 T get() const
106 {
107 return proto::value(*this);
108 }
109 };
110
111 // TEST CASES
112 struct A {};
113 struct B {};
114 struct C {};
115
main()116 int main()
117 {
118 using fusion::vector;
119 future<A> a;
120 future<B> b;
121 future<C> c;
122 future<vector<A,B> > ab;
123
124 // Verify that various future groups have the
125 // correct return types.
126 A t0 = a.get();
127 vector<A, B, C> t1 = (a && b && c).get();
128 vector<A, C> t2 = ((a || a) && c).get();
129 vector<A, B, C> t3 = ((a && b || a && b) && c).get();
130 vector<vector<A, B>, C> t4 = ((ab || ab) && c).get();
131
132 return 0;
133 }
134 //]
135