BoostPhoenix3: miniphoenix.cpp

File miniphoenix.cpp, 14.5 KB (added by Eric Niebler, 13 years ago)

A working mini-phoenix-on-proto implementation that demonstrates an extensible core and if_/else_ statement implemented as an extension

Line 
1#include <iosfwd>
2#include <boost/mpl/int.hpp>
3#include <boost/fusion/container/vector/vector10.hpp>
4#include <boost/proto/proto.hpp>
5
6#define BOOST_PHOENIX_LIMIT 10
7#define BOOST_PHOENIX_ARG_LIMIT BOOST_PHOENIX_LIMIT
8
9namespace boost
10{
11 namespace phoenix
12 {
13 ////////////////////////////////////////////////////////////////////////////////////////////
14 struct evaluator;
15
16 ////////////////////////////////////////////////////////////////////////////////////////////
17 template<typename Expr>
18 struct actor;
19
20 ////////////////////////////////////////////////////////////////////////////////////////////
21 struct make_actor
22 : proto::call<proto::pod_generator<actor>(proto::_)>
23 {};
24
25 ////////////////////////////////////////////////////////////////////////////////////////////
26 struct generator_cases
27 {
28 template<typename>
29 struct case_
30 : proto::when<proto::_, make_actor>
31 {};
32 };
33
34 namespace detail
35 {
36 ////////////////////////////////////////////////////////////////////////////////////////////
37 struct generator
38 : proto::switch_<generator_cases>
39 {};
40
41 ////////////////////////////////////////////////////////////////////////////////////////////
42 struct domain
43 : proto::domain<generator>
44 {};
45
46 ////////////////////////////////////////////////////////////////////////////////////////
47 template<typename Int>
48 struct argument
49 {
50 typedef typename Int::tag tag;
51 typedef typename Int::value_type value_type;
52 typedef argument<Int> type;
53 typedef argument<typename Int::next> next;
54 typedef argument<typename Int::prior> prior;
55 static value_type const value = Int::value;
56
57 friend std::ostream &operator<<(std::ostream &sout, argument<Int>)
58 {
59 return sout << "boost::phoenix::arg_names::arg" << (Int::value+1);
60 }
61 };
62
63 template<typename Int>
64 typename Int::value_type const argument<Int>::value;
65
66 ////////////////////////////////////////////////////////////////////////////////////////
67 struct at : proto::callable
68 {
69 template<typename Sig>
70 struct result;
71
72 template<typename This, typename Cont, typename Arg>
73 struct result<This(Cont &, Arg &)>
74 : fusion::result_of::at<Cont, Arg>
75 {};
76
77 template<typename Cont, typename Arg>
78 typename fusion::result_of::at<Cont, Arg>::type
79 operator ()(Cont &cont, Arg &) const
80 {
81 return fusion::at<Arg>(cont);
82 }
83 };
84
85 ////////////////////////////////////////////////////////////////////////////////////////
86 template<typename Sig>
87 struct result;
88
89 template<typename This>
90 struct result<This()>
91 : result_of<evaluator(This &, int const &, fusion::vector0 &)>
92 {};
93
94 #define BOOST_PROTO_LOCAL_MACRO(N, typename_A, A, A_const_ref_a, ref_a) \
95 template<typename This, typename_A(N)> \
96 struct result<This(A(N))> \
97 : result_of<evaluator(This &, int const &, BOOST_PP_CAT(fusion::vector, N)<A(N)> &)> \
98 {}; \
99 /**/
100
101 #define BOOST_PROTO_LOCAL_A BOOST_PROTO_A
102 #define BOOST_PROTO_LOCAL_LIMITS (1, BOOST_PHOENIX_ARG_LIMIT)
103 #include BOOST_PROTO_LOCAL_ITERATE()
104 }
105
106 ////////////////////////////////////////////////////////////////////////////////////////////
107 struct evaluator_cases
108 {
109 template<typename>
110 struct case_
111 : proto::when<proto::_, proto::_default<evaluator> >
112 {};
113 };
114
115 template<>
116 struct evaluator_cases::case_<proto::tag::terminal>
117 : proto::or_<
118 proto::when<
119 proto::terminal<detail::argument<proto::_> >
120 , detail::at(proto::_data, proto::_value)
121 >
122 , proto::otherwise<proto::_default<evaluator> >
123 >
124 {};
125
126 ////////////////////////////////////////////////////////////////////////////////////////////
127 struct evaluator
128 : proto::switch_<evaluator_cases>
129 {};
130
131 #define BOOST_PHOENIX_INVOKE(N, typename_A, A_const_ref, A_const_ref_a, a) \
132 template<typename_A(N)> \
133 typename detail::result<proto_derived_expr const(A_const_ref(N))>::type \
134 operator()(A_const_ref_a(N)) const \
135 { \
136 BOOST_PP_CAT(fusion::vector, N)<A_const_ref(N)> args(a(N)); \
137 return evaluator()(*this, 0, args); \
138 } \
139 /**/
140
141 ////////////////////////////////////////////////////////////////////////////////////////////
142 template<typename Expr>
143 struct actor
144 {
145 BOOST_PROTO_BASIC_EXTENDS(Expr, actor<Expr>, detail::domain)
146
147 template<typename Sig>
148 struct result : detail::result<Sig>
149 {};
150
151 // TODO: handle nullary lambdas
152 #define BOOST_PROTO_LOCAL_MACRO BOOST_PHOENIX_INVOKE
153 #define BOOST_PROTO_LOCAL_a BOOST_PROTO_a
154 #define BOOST_PROTO_LOCAL_LIMITS (1, BOOST_PHOENIX_ARG_LIMIT)
155 #include BOOST_PROTO_LOCAL_ITERATE()
156 };
157
158 ////////////////////////////////////////////////////////////////////////////////////////////
159 template<int I, typename _ = proto::is_proto_expr>
160 struct argument
161 {
162 typedef
163 typename proto::terminal<detail::argument<mpl::int_<I> > >::type
164 base_type;
165
166 BOOST_PROTO_BASIC_EXTENDS(base_type, argument<I>, detail::domain)
167
168 // For backwards compatibility
169 operator actor<argument<I> > const &() const
170 {
171 return argument<I,_>::argI;
172 }
173
174 static actor<argument<I> > const argI;
175 };
176
177 template<int I, typename _>
178 actor<argument<I> > const argument<I,_>::argI = {{{{}}}};
179
180 ////////////////////////////////////////////////////////////////////////////////////////////
181 namespace arg_names
182 {
183 // Phoenix style names
184 actor<argument<0> > const arg1 = {{{{}}}};
185 actor<argument<1> > const arg2 = {{{{}}}};
186 actor<argument<2> > const arg3 = {{{{}}}};
187
188 // BLL style names
189 actor<argument<0> > const _1 = {{{{}}}};
190 actor<argument<1> > const _2 = {{{{}}}};
191 actor<argument<2> > const _3 = {{{{}}}};
192
193 // Bring in the rest or the Phoenix style arguments (arg4 .. argN+1)
194 // and BLL style arguments (_4 .. _N+1), using PP
195 #define BOOST_PHOENIX_DECLARE_ARG(Z, N, DATA) \
196 actor<argument<N> > const BOOST_PP_CAT(arg, BOOST_PP_INC(N)) = {{{{}}}}; \
197 actor<argument<N> > const BOOST_PP_CAT(_, BOOST_PP_INC(N)) = {{{{}}}}; \
198 /**/
199
200 BOOST_PP_REPEAT_FROM_TO(3, BOOST_PHOENIX_ARG_LIMIT, BOOST_PHOENIX_DECLARE_ARG, _)
201 #undef BOOST_PHOENIX_DECLARE_ARG
202 }
203
204 //
205 // Begin implementation of if_/else_ here
206 //
207
208 namespace tag
209 {
210 struct if_ {};
211 struct else_ {};
212 }
213
214 namespace detail
215 {
216 // Proto transform for evaluating lambdas like: if_(foo)[bar]
217 struct if_evaluator : proto::transform<if_evaluator>
218 {
219 template<typename Expr, typename State, typename Data>
220 struct impl : proto::transform_impl<Expr, State, Data>
221 {
222 typedef void result_type;
223 void operator()(
224 typename impl::expr_param e
225 , typename impl::state_param s
226 , typename impl::data_param d
227 ) const
228 {
229 if(evaluator()(proto::child_c<0>(e), s, d))
230 {
231 evaluator()(proto::child_c<1>(e), s, d);
232 }
233 }
234 };
235 };
236
237 // Proto transform for evaluating lambdas like: if_(foo)[bar].else_[baz]
238 struct else_evaluator : proto::transform<else_evaluator>
239 {
240 template<typename Expr, typename State, typename Data>
241 struct impl : proto::transform_impl<Expr, State, Data>
242 {
243 typedef void result_type;
244 void operator()(
245 typename impl::expr_param e
246 , typename impl::state_param s
247 , typename impl::data_param d
248 ) const
249 {
250 if(evaluator()(proto::child_c<0>(e), s, d))
251 {
252 evaluator()(proto::child_c<1>(e), s, d);
253 }
254 else
255 {
256 evaluator()(proto::child_c<2>(e), s, d);
257 }
258 }
259 };
260 };
261 }
262
263 ////////////////////////////////////////////////////////////////////////////////////////////
264 template<typename Expr>
265 struct actor_with_else
266 {
267 BOOST_PROTO_BASIC_EXTENDS(Expr, actor_with_else<Expr>, detail::domain)
268
269 template<typename Sig>
270 struct result : detail::result<Sig>
271 {};
272
273 // TODO: handle nullary lambdas
274 #define BOOST_PROTO_LOCAL_MACRO BOOST_PHOENIX_INVOKE
275 #define BOOST_PROTO_LOCAL_a BOOST_PROTO_a
276 #define BOOST_PROTO_LOCAL_LIMITS (1, BOOST_PHOENIX_ARG_LIMIT)
277 #include BOOST_PROTO_LOCAL_ITERATE()
278
279 typedef typename proto::result_of::child_c<Expr const &, 0>::type A;
280 typedef typename proto::result_of::child_c<Expr const &, 1>::type B;
281
282 // if_ statements get an else_ defined thusly:
283 struct else_type
284 {
285 template<typename C>
286 typename proto::result_of::make_expr<tag::else_, detail::domain, A, B, C const &>::type
287 operator[](C const &c) const
288 {
289 actor_with_else const & cond
290 = *(actor_with_else const *)
291 ((char *)this - BOOST_PROTO_OFFSETOF(actor_with_else, else_));
292
293 return proto::make_expr<tag::else_, detail::domain>(
294 boost::ref(proto::child_c<0>(cond))
295 , boost::ref(proto::child_c<1>(cond))
296 , boost::ref(c)
297 );
298 }
299 } else_;
300 };
301
302 ////////////////////////////////////////////////////////////////////////////////////////////
303 struct make_actor_with_else
304 : proto::call<proto::pod_generator<actor_with_else>(proto::_)>
305 {};
306
307 ////////////////////////////////////////////////////////////////////////////////////////////
308 struct if_type
309 {
310 template<typename A>
311 struct stmt
312 {
313 template<typename B>
314 typename proto::result_of::make_expr<tag::if_, detail::domain, A const &, B const &>::type
315 operator[](B const &b) const
316 {
317 return proto::make_expr<tag::if_, detail::domain>(boost::ref(a), boost::ref(b));
318 }
319
320 A const &a;
321 };
322
323 template<typename A>
324 stmt<A> operator()(A const &a) const
325 {
326 stmt<A> ret = {a};
327 return ret;
328 }
329 } const if_ = {};
330
331 ////////////////////////////////////////////////////////////////////////////////////////////
332 // This instructs Proto to wrap if_ expressions in actor_with_else
333 template<>
334 struct generator_cases::case_<tag::if_>
335 : proto::when<proto::_, make_actor_with_else>
336 {};
337
338 ////////////////////////////////////////////////////////////////////////////////////////////
339 // This tells Proto how to evaluate if_ statements with the if_evaluator
340 template<>
341 struct evaluator_cases::case_<tag::if_>
342 : proto::when<proto::_, detail::if_evaluator>
343 {};
344
345 ////////////////////////////////////////////////////////////////////////////////////////////
346 // This tells Proto how to evaluate else_ statements with the else_evaluator
347 template<>
348 struct evaluator_cases::case_<tag::else_>
349 : proto::when<proto::_, detail::else_evaluator>
350 {};
351 }
352}
353
354#include <iostream>
355#include <typeinfo>
356namespace proto = boost::proto;
357namespace phoenix = boost::phoenix;
358using namespace phoenix::arg_names;
359
360int main()
361{
362 proto::display_expr(_1 + _2);
363
364 int j = (_1 + _2)(1, 2);
365 std::cout << j << "\n";
366
367 j = (_2 * 2)(1, 2);
368 std::cout << j << "\n";
369
370 char const *name = typeid(phoenix::if_(_1)[_2].else_).name();
371 std::cout << name << "\n";
372
373 name = typeid(phoenix::if_(_1)[_2].else_[_1]).name();
374 std::cout << name << "\n";
375
376 phoenix::if_(_1)[_2].else_[_3](1,2,3);
377 phoenix::if_(_1)[_2].else_[_3](0,2,3);
378}