• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //[ Lambda
2 ///////////////////////////////////////////////////////////////////////////////
3 // Copyright 2008 Eric Niebler. Distributed under the Boost
4 // Software License, Version 1.0. (See accompanying file
5 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // This example builds a simple but functional lambda library using Proto.
8 
9 #include <iostream>
10 #include <algorithm>
11 #include <boost/mpl/int.hpp>
12 #include <boost/mpl/min_max.hpp>
13 #include <boost/mpl/eval_if.hpp>
14 #include <boost/mpl/identity.hpp>
15 #include <boost/mpl/next_prior.hpp>
16 #include <boost/fusion/tuple.hpp>
17 #include <boost/typeof/typeof.hpp>
18 #include <boost/typeof/std/ostream.hpp>
19 #include <boost/typeof/std/iostream.hpp>
20 #include <boost/proto/core.hpp>
21 #include <boost/proto/context.hpp>
22 #include <boost/proto/transform.hpp>
23 namespace mpl = boost::mpl;
24 namespace proto = boost::proto;
25 namespace fusion = boost::fusion;
26 using proto::_;
27 
28 // Forward declaration of the lambda expression wrapper
29 template<typename T>
30 struct lambda;
31 
32 struct lambda_domain
33   : proto::domain<proto::pod_generator<lambda> >
34 {};
35 
36 template<typename I>
37 struct placeholder
38 {
39     typedef I arity;
40 };
41 
42 template<typename T>
43 struct placeholder_arity
44 {
45     typedef typename T::arity type;
46 };
47 
48 // The lambda grammar, with the transforms for calculating the max arity
49 struct lambda_arity
50   : proto::or_<
51         proto::when<
52             proto::terminal< placeholder<_> >
53           , mpl::next<placeholder_arity<proto::_value> >()
54         >
55       , proto::when< proto::terminal<_>
56           , mpl::int_<0>()
57         >
58       , proto::when<
59             proto::nary_expr<_, proto::vararg<_> >
60           , proto::fold<_, mpl::int_<0>(), mpl::max<lambda_arity, proto::_state>()>
61         >
62     >
63 {};
64 
65 // The lambda context is the same as the default context
66 // with the addition of special handling for lambda placeholders
67 template<typename Tuple>
68 struct lambda_context
69   : proto::callable_context<lambda_context<Tuple> const>
70 {
lambda_contextlambda_context71     lambda_context(Tuple const &args)
72       : args_(args)
73     {}
74 
75     template<typename Sig>
76     struct result;
77 
78     template<typename This, typename I>
79     struct result<This(proto::tag::terminal, placeholder<I> const &)>
80       : fusion::result_of::at<Tuple, I>
81     {};
82 
83     template<typename I>
84     typename fusion::result_of::at<Tuple, I>::type
operator ()lambda_context85     operator ()(proto::tag::terminal, placeholder<I> const &) const
86     {
87         return fusion::at<I>(this->args_);
88     }
89 
90     Tuple args_;
91 };
92 
93 // The lambda<> expression wrapper makes expressions polymorphic
94 // function objects
95 template<typename T>
96 struct lambda
97 {
98     BOOST_PROTO_BASIC_EXTENDS(T, lambda<T>, lambda_domain)
99     BOOST_PROTO_EXTENDS_ASSIGN()
100     BOOST_PROTO_EXTENDS_SUBSCRIPT()
101 
102     // Calculate the arity of this lambda expression
103     static int const arity = boost::result_of<lambda_arity(T)>::type::value;
104 
105     template<typename Sig>
106     struct result;
107 
108     // Define nested result<> specializations to calculate the return
109     // type of this lambda expression. But be careful not to evaluate
110     // the return type of the nullary function unless we have a nullary
111     // lambda!
112     template<typename This>
113     struct result<This()>
114       : mpl::eval_if_c<
115             0 == arity
116           , proto::result_of::eval<T const, lambda_context<fusion::tuple<> > >
117           , mpl::identity<void>
118         >
119     {};
120 
121     template<typename This, typename A0>
122     struct result<This(A0)>
123       : proto::result_of::eval<T const, lambda_context<fusion::tuple<A0> > >
124     {};
125 
126     template<typename This, typename A0, typename A1>
127     struct result<This(A0, A1)>
128       : proto::result_of::eval<T const, lambda_context<fusion::tuple<A0, A1> > >
129     {};
130 
131     // Define our operator () that evaluates the lambda expression.
132     typename result<lambda()>::type
operator ()lambda133     operator ()() const
134     {
135         fusion::tuple<> args;
136         lambda_context<fusion::tuple<> > ctx(args);
137         return proto::eval(*this, ctx);
138     }
139 
140     template<typename A0>
141     typename result<lambda(A0 const &)>::type
operator ()lambda142     operator ()(A0 const &a0) const
143     {
144         fusion::tuple<A0 const &> args(a0);
145         lambda_context<fusion::tuple<A0 const &> > ctx(args);
146         return proto::eval(*this, ctx);
147     }
148 
149     template<typename A0, typename A1>
150     typename result<lambda(A0 const &, A1 const &)>::type
operator ()lambda151     operator ()(A0 const &a0, A1 const &a1) const
152     {
153         fusion::tuple<A0 const &, A1 const &> args(a0, a1);
154         lambda_context<fusion::tuple<A0 const &, A1 const &> > ctx(args);
155         return proto::eval(*this, ctx);
156     }
157 };
158 
159 // Define some lambda placeholders
160 lambda<proto::terminal<placeholder<mpl::int_<0> > >::type> const _1 = {{}};
161 lambda<proto::terminal<placeholder<mpl::int_<1> > >::type> const _2 = {{}};
162 
163 template<typename T>
val(T const & t)164 lambda<typename proto::terminal<T>::type> const val(T const &t)
165 {
166     lambda<typename proto::terminal<T>::type> that = {{t}};
167     return that;
168 }
169 
170 template<typename T>
var(T & t)171 lambda<typename proto::terminal<T &>::type> const var(T &t)
172 {
173     lambda<typename proto::terminal<T &>::type> that = {{t}};
174     return that;
175 }
176 
177 template<typename T>
178 struct construct_helper
179 {
180     typedef T result_type; // for TR1 result_of
181 
operator ()construct_helper182     T operator()() const
183     { return T(); }
184 
185     // Generate BOOST_PROTO_MAX_ARITY overloads of the
186     // following function call operator.
187 #define BOOST_PROTO_LOCAL_MACRO(N, typename_A, A_const_ref, A_const_ref_a, a)\
188     template<typename_A(N)>                                       \
189     T operator()(A_const_ref_a(N)) const                          \
190     { return T(a(N)); }
191 #define BOOST_PROTO_LOCAL_a BOOST_PROTO_a
192 #include BOOST_PROTO_LOCAL_ITERATE()
193 };
194 
195 // Generate BOOST_PROTO_MAX_ARITY-1 overloads of the
196 // following construct() function template.
197 #define M0(N, typename_A, A_const_ref, A_const_ref_a, ref_a)      \
198 template<typename T, typename_A(N)>                               \
199 typename proto::result_of::make_expr<                             \
200     proto::tag::function                                          \
201   , lambda_domain                                                 \
202   , construct_helper<T>                                           \
203   , A_const_ref(N)                                                \
204 >::type const                                                     \
205 construct(A_const_ref_a(N))                                       \
206 {                                                                 \
207     return proto::make_expr<                                      \
208         proto::tag::function                                      \
209       , lambda_domain                                             \
210     >(                                                            \
211         construct_helper<T>()                                     \
212       , ref_a(N)                                                  \
213     );                                                            \
214 }
215 BOOST_PROTO_REPEAT_FROM_TO(1, BOOST_PROTO_MAX_ARITY, M0)
216 #undef M0
217 
218 struct S
219 {
SS220     S() {}
SS221     S(int i, char c)
222     {
223         std::cout << "S(" << i << "," << c << ")\n";
224     }
225 };
226 
main()227 int main()
228 {
229     // Create some lambda objects and immediately
230     // invoke them by applying their operator():
231     int i = ( (_1 + 2) / 4 )(42);
232     std::cout << i << std::endl; // prints 11
233 
234     int j = ( (-(_1 + 2)) / 4 )(42);
235     std::cout << j << std::endl; // prints -11
236 
237     double d = ( (4 - _2) * 3 )(42, 3.14);
238     std::cout << d << std::endl; // prints 2.58
239 
240     // check non-const ref terminals
241     (std::cout << _1 << " -- " << _2 << '\n')(42, "Life, the Universe and Everything!");
242     // prints "42 -- Life, the Universe and Everything!"
243 
244     // "Nullary" lambdas work too
245     int k = (val(1) + val(2))();
246     std::cout << k << std::endl; // prints 3
247 
248     // check array indexing for kicks
249     int integers[5] = {0};
250     (var(integers)[2] = 2)();
251     (var(integers)[_1] = _1)(3);
252     std::cout << integers[2] << std::endl; // prints 2
253     std::cout << integers[3] << std::endl; // prints 3
254 
255     // Now use a lambda with an STL algorithm!
256     int rgi[4] = {1,2,3,4};
257     char rgc[4] = {'a','b','c','d'};
258     S rgs[4];
259 
260     std::transform(rgi, rgi+4, rgc, rgs, construct<S>(_1, _2));
261     return 0;
262 }
263 //]
264