BoostPhoenix3: miniphoenix.cpp

File miniphoenix.cpp, 14.5 KB (added by Eric Niebler, 10 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}