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