1 //[ VirtualMember
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 example demonstrates how to use BOOST_PROTO_EXTENDS_MEMBERS()
7 // to add "virtual" data members to expressions within a domain. For
8 // instance, with Phoenix you can create a lambda expression such as
9 //
10 // if_(_1 > 0)[ std::cout << _2 ].else_[ std::cout << _3 ]
11 //
12 // In the above expression, "else_" is a so-called virtual data member
13 // of the expression "if_(_1 > 0)[ std::cout << _2 ]". This example
14 // shows how to implement the ".else_" syntax with Proto.
15 //
16 // ****WARNING****WARNING****WARNING****WARNING****WARNING****WARNING****
17 // * The virtual data member feature is experimental and can change at *
18 // * any time. Use it at your own risk. *
19 // **********************************************************************
20
21 #if defined(_MSC_VER) && _MSC_VER == 1310
22 #error "Sorry, this example doesn\'t work with MSVC 7.1"
23 #endif
24
25 #include <iostream>
26 #include <boost/config.hpp>
27 #include <boost/detail/workaround.hpp>
28 #include <boost/mpl/eval_if.hpp>
29 #include <boost/mpl/min_max.hpp>
30 #include <boost/mpl/next_prior.hpp>
31 #include <boost/fusion/include/at.hpp>
32 #include <boost/fusion/include/vector.hpp>
33 #include <boost/typeof/std/ostream.hpp>
34 #include <boost/proto/proto.hpp>
35
36 namespace mpl = boost::mpl;
37 namespace proto = boost::proto;
38 namespace fusion = boost::fusion;
39 using proto::_;
40
41 namespace mini_lambda
42 {
43 // A callable PolymorphicFunctionObject that wraps
44 // fusion::at()
45 struct at : proto::callable
46 {
47 template<class Sig>
48 struct result;
49
50 template<class This, class Vector, class N>
51 struct result<This(Vector, N)>
52 : fusion::result_of::at<
53 typename boost::remove_reference<Vector>::type
54 , typename boost::remove_reference<N>::type
55 >
56 {};
57
58 template<class Vector, class N>
59 typename fusion::result_of::at<Vector const, N>::type
operator ()mini_lambda::at60 operator()(Vector const &vector, N) const
61 {
62 return fusion::at<N>(vector);
63 }
64 };
65
66 // An MPL IntegralConstant
67 template<class N>
68 struct placeholder
69 {
70 typedef N type;
71 typedef typename N::tag tag;
72 typedef typename N::next next;
73 typedef typename N::prior prior;
74 typedef typename N::value_type value_type;
75 static const value_type value = N::value;
76 };
77
78 // Some keyword types for our lambda EDSL
79 namespace keyword
80 {
81 struct if_ {};
82 struct else_ {};
83 struct do_ {};
84 struct while_ {};
85 struct try_ {};
86 struct catch_ {};
87 }
88
89 // Forward declaration for the mini-lambda grammar
90 struct eval_if_else;
91
92 // Forward declaration for the mini-lambda expression wrapper
93 template<class E>
94 struct expression;
95
96 // The grammar for mini-lambda expressions with transforms for
97 // evaluating the lambda expression.
98 struct grammar
99 : proto::or_<
100 // When evaluating a placeholder, use the placeholder
101 // to index into the "data" parameter, which is a fusion
102 // vector containing the arguments to the lambda expression.
103 proto::when<
104 proto::terminal<placeholder<_> >
105 , at(proto::_data, proto::_value)
106 >
107 // When evaluating if/then/else expressions of the form
108 // "if_( E0 )[ E1 ].else_[ E2 ]", pass E0, E1 and E2 to
109 // eval_if_else along with the "data" parameter. Note the
110 // use of proto::member<> to match binary expressions like
111 // "X.Y" where "Y" is a virtual data member.
112 , proto::when<
113 proto::subscript<
114 proto::member<
115 proto::subscript<
116 proto::function<
117 proto::terminal<keyword::if_>
118 , grammar
119 >
120 , grammar
121 >
122 , proto::terminal<keyword::else_>
123 >
124 , grammar
125 >
126 , eval_if_else(
127 proto::_right(proto::_left(proto::_left(proto::_left)))
128 , proto::_right(proto::_left(proto::_left))
129 , proto::_right
130 , proto::_data
131 )
132 >
133 , proto::otherwise<
134 proto::_default<grammar>
135 >
136 >
137 {};
138
139 // A callable PolymorphicFunctionObject that evaluates
140 // if/then/else expressions.
141 struct eval_if_else : proto::callable
142 {
143 typedef void result_type;
144
145 template<typename If, typename Then, typename Else, typename Args>
operator ()mini_lambda::eval_if_else146 void operator()(If const &if_, Then const &then_, Else const &else_, Args const &args) const
147 {
148 if(grammar()(if_, 0, args))
149 {
150 grammar()(then_, 0, args);
151 }
152 else
153 {
154 grammar()(else_, 0, args);
155 }
156 }
157 };
158
159 // Define the mini-lambda domain, in which all expressions are
160 // wrapped in mini_lambda::expression.
161 struct domain
162 : proto::domain<proto::pod_generator<expression> >
163 {};
164
165 // A simple transform for computing the arity of
166 // a lambda expression.
167 struct arity_of
168 : proto::or_<
169 proto::when<
170 proto::terminal< placeholder<_> >
171 , mpl::next<proto::_value>()
172 >
173 , proto::when<
174 proto::terminal<_>
175 , mpl::int_<0>()
176 >
177 , proto::otherwise<
178 proto::fold<
179 _
180 , mpl::int_<0>()
181 , mpl::max<arity_of, proto::_state>()
182 >
183 >
184 >
185 {};
186
187 // Here is the mini-lambda expression wrapper. It serves two purposes:
188 // 1) To define operator() overloads that evaluate the lambda expression, and
189 // 2) To define virtual data members like "else_" so that we can write
190 // expressions like "if_(X)[Y].else_[Z]".
191 template<class E>
192 struct expression
193 {
194 BOOST_PROTO_BASIC_EXTENDS(E, expression<E>, domain)
195 BOOST_PROTO_EXTENDS_ASSIGN()
196 BOOST_PROTO_EXTENDS_SUBSCRIPT()
197
198 // Use BOOST_PROTO_EXTENDS_MEMBERS() to define "virtual"
199 // data members that all expressions in the mini-lambda
200 // domain will have. They can be used to create expressions
201 // like "if_(x)[y].else_[z]" and "do_[y].while_(z)".
202 BOOST_PROTO_EXTENDS_MEMBERS(
203 ((keyword::else_, else_))
204 ((keyword::while_, while_))
205 ((keyword::catch_, catch_))
206 )
207
208 // Calculate the arity of this lambda expression
209 static int const arity = boost::result_of<arity_of(E)>::type::value;
210
211 // Define overloads of operator() that evaluate the lambda
212 // expression for up to 3 arguments.
213
214 // Don't try to compute the return type of the lambda if
215 // it isn't nullary.
216 typename mpl::eval_if_c<
217 0 != arity
218 , mpl::identity<void>
219 , boost::result_of<grammar(
220 E const &
221 , int const &
222 , fusion::vector<> &
223 )>
224 >::type
operator ()mini_lambda::expression225 operator()() const
226 {
227 BOOST_MPL_ASSERT_RELATION(arity, ==, 0);
228 fusion::vector<> args;
229 return grammar()(proto_base(), 0, args);
230 }
231
232 #define BOOST_PROTO_LOCAL_MACRO( \
233 N, typename_A, A_const_ref, A_const_ref_a, a \
234 ) \
235 template<typename_A(N)> \
236 typename boost::result_of<grammar( \
237 E const & \
238 , int const & \
239 , fusion::vector<A_const_ref(N)> & \
240 )>::type \
241 operator ()(A_const_ref_a(N)) const \
242 { \
243 BOOST_MPL_ASSERT_RELATION(arity, <=, N); \
244 fusion::vector<A_const_ref(N)> args(a(N)); \
245 return grammar()(proto_base(), 0, args); \
246 }
247 // Repeats BOOST_PROTO_LOCAL_MACRO macro for N=1 to 3
248 // inclusive (because there are only 3 placeholders)
249 #define BOOST_PROTO_LOCAL_a BOOST_PROTO_a
250 #define BOOST_PROTO_LOCAL_LIMITS (1, 3)
251 #include BOOST_PROTO_LOCAL_ITERATE()
252 };
253
254 namespace placeholders
255 {
256 typedef placeholder<mpl::int_<0> > _1_t;
257 typedef placeholder<mpl::int_<1> > _2_t;
258 typedef placeholder<mpl::int_<2> > _3_t;
259
260 // Define some placeholders
261 expression<proto::terminal<_1_t>::type> const _1 = {{{}}};
262 expression<proto::terminal<_2_t>::type> const _2 = {{{}}};
263 expression<proto::terminal<_3_t>::type> const _3 = {{{}}};
264
265 // Define the if_() statement
266 template<typename E>
267 typename proto::result_of::make_expr<proto::tag::function, domain
268 , keyword::if_
269 , E const &
270 >::type const
if_(E const & e)271 if_(E const &e)
272 {
273 return proto::make_expr<proto::tag::function, domain>(
274 keyword::if_()
275 , boost::ref(e)
276 );
277 }
278 }
279
280 using placeholders::if_;
281 }
282
main()283 int main()
284 {
285 using namespace mini_lambda::placeholders;
286
287 // OK, we can create if/then/else lambda expressions
288 // and evaluate them.
289 if_(_1 > 0)
290 [
291 std::cout << _2 << '\n'
292 ]
293 .else_
294 [
295 std::cout << _3 << '\n'
296 ]
297 (-42, "positive", "non-positive");
298
299 // Even though all expressions in the mini-lambda
300 // domain have members named else_, while_, and catch_,
301 // they all occupy the same byte in the expression.
302 BOOST_MPL_ASSERT_RELATION(sizeof(_1), ==, 2);
303
304 return 0;
305 }
306 //]
307