Imported Upstream version 1.57.0
[platform/upstream/boost.git] / libs / proto / example / virtual_member.cpp
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
60         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>
146         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
225         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
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
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 //]