1///////////////////////////////////////////////////////////////////////////////
2// lambda.hpp
3//
4// Copyright 2008 Eric Niebler. Distributed under the Boost
5// Software License, Version 1.0. (See accompanying file
6// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7
8#include <sstream>
9#include <boost/mpl/int.hpp>
10#include <boost/mpl/min_max.hpp>
11#include <boost/mpl/eval_if.hpp>
12#include <boost/mpl/identity.hpp>
13#include <boost/mpl/next_prior.hpp>
14#include <boost/fusion/tuple.hpp>
15#include <boost/typeof/typeof.hpp>
16#include <boost/typeof/std/sstream.hpp>
17#include <boost/typeof/std/ostream.hpp>
18#include <boost/typeof/std/iostream.hpp>
19#include <boost/type_traits/add_const.hpp>
20#include <boost/type_traits/add_reference.hpp>
21#include <boost/proto/core.hpp>
22#include <boost/proto/context.hpp>
23#include <boost/proto/transform.hpp>
24#include <boost/test/unit_test.hpp>
25#include <boost/test/floating_point_comparison.hpp>
26
27using namespace boost;
28
29// Forward declaration of the lambda expression wrapper
30template<typename T>
31struct lambda;
32
33struct lambda_domain
34 : proto::domain<proto::pod_generator<lambda> >
35{};
36
37template<typename I>
38struct placeholder
39{
40 typedef I arity;
41};
42
43template<typename T>
44struct placeholder_arity
45{
46 typedef typename T::arity type;
47};
48
49namespace grammar
50{
51 using namespace proto;
52
53 // The lambda grammar, with the transforms for calculating the max arity
54 struct Lambda
55 : or_<
56 when< terminal< placeholder<_> >, mpl::next<placeholder_arity<_value> >() >
57 , when< terminal<_>, mpl::int_<0>() >
58 , when< nary_expr<_, vararg<_> >, fold<_, mpl::int_<0>(), mpl::max<Lambda,_state>()> >
59 >
60 {};
61}
62
63// simple wrapper for calculating a lambda expression's arity.
64template<typename Expr>
65struct lambda_arity
66 : boost::result_of<grammar::Lambda(Expr, mpl::void_, mpl::void_)>
67{};
68
69// The lambda context is the same as the default context
70// with the addition of special handling for lambda placeholders
71template<typename Tuple>
72struct lambda_context
73 : proto::callable_context<lambda_context<Tuple> const>
74{
75 lambda_context(Tuple const &args)
76 : args_(args)
77 {}
78
79 template<typename Sig>
80 struct result;
81
82 template<typename This, typename I>
83 struct result<This(proto::tag::terminal, placeholder<I> const &)>
84 : fusion::result_of::at<Tuple, I>
85 {};
86
87 template<typename I>
88 typename fusion::result_of::at<Tuple, I>::type
89 operator ()(proto::tag::terminal, placeholder<I> const &) const
90 {
91 return fusion::at<I>(this->args_);
92 }
93
94 Tuple args_;
95};
96
97// The lambda<> expression wrapper makes expressions polymorphic
98// function objects
99template<typename T>
100struct lambda
101{
102 BOOST_PROTO_BASIC_EXTENDS(T, lambda<T>, lambda_domain)
103 BOOST_PROTO_EXTENDS_ASSIGN()
104 BOOST_PROTO_EXTENDS_SUBSCRIPT()
105
106 // Careful not to evaluate the return type of the nullary function
107 // unless we have a nullary lambda!
108 typedef typename mpl::eval_if<
109 typename lambda_arity<T>::type
110 , mpl::identity<void>
111 , proto::result_of::eval<T const, lambda_context<fusion::tuple<> > >
112 >::type nullary_type;
113
114 // Define our operator () that evaluates the lambda expression.
115 nullary_type operator ()() const
116 {
117 fusion::tuple<> args;
118 lambda_context<fusion::tuple<> > ctx(args);
119 return proto::eval(*this, ctx);
120 }
121
122 #define M0(N, typename_A, A_const_ref, A_const_ref_a, ref_a) \
123 template<typename_A(N)> \
124 typename proto::result_of::eval<T const, lambda_context<fusion::tuple<A_const_ref(N)> > >::type \
125 operator ()(A_const_ref_a(N)) const \
126 { \
127 fusion::tuple<A_const_ref(N)> args(ref_a(N)); \
128 lambda_context<fusion::tuple<A_const_ref(N)> > ctx(args); \
129 return proto::eval(*this, ctx); \
130 } \
131 /**/
132 BOOST_PROTO_REPEAT_FROM_TO(1, 4, M0)
133 #undef M0
134};
135
136// Define some lambda placeholders
137lambda<proto::terminal<placeholder<mpl::int_<0> > >::type> const _1 = {.proto_expr_: {}};
138lambda<proto::terminal<placeholder<mpl::int_<1> > >::type> const _2 = {.proto_expr_: {}};
139lambda<proto::terminal<placeholder<mpl::int_<3> > >::type> const _3 = {.proto_expr_: {}};
140
141template<typename T>
142lambda<typename proto::terminal<T>::type> const val(T const &t)
143{
144 lambda<typename proto::terminal<T>::type> that = {{t}};
145 return that;
146}
147
148template<typename T>
149lambda<typename proto::terminal<T &>::type> const var(T &t)
150{
151 lambda<typename proto::terminal<T &>::type> that = {{t}};
152 return that;
153}
154
155void test_lambda()
156{
157 BOOST_CHECK_EQUAL(11, ( (_1 + 2) / 4 )(42));
158 BOOST_CHECK_EQUAL(-11, ( (-(_1 + 2)) / 4 )(42));
159 BOOST_CHECK_CLOSE(2.58, ( (4 - _2) * 3 )(42, 3.14), 0.1);
160
161 // check non-const ref terminals
162 std::stringstream sout;
163 (sout << _1 << " -- " << _2)(42, "Life, the Universe and Everything!");
164 BOOST_CHECK_EQUAL("42 -- Life, the Universe and Everything!", sout.str());
165
166 // check nullary lambdas
167 BOOST_CHECK_EQUAL(3, (val(1) + val(2))());
168
169 // check array indexing for kicks
170 int integers[5] = {0};
171 (var(t&: integers)[2] = 2)();
172 (var(t&: integers)[_1] = _1)(3);
173 BOOST_CHECK_EQUAL(2, integers[2]);
174 BOOST_CHECK_EQUAL(3, integers[3]);
175}
176
177using namespace unit_test;
178///////////////////////////////////////////////////////////////////////////////
179// init_unit_test_suite
180//
181test_suite* init_unit_test_suite( int argc, char* argv[] )
182{
183 test_suite *test = BOOST_TEST_SUITE("test expression template domains");
184
185 test->add(BOOST_TEST_CASE(&test_lambda));
186
187 return test;
188}
189

source code of boost/libs/proto/test/lambda.cpp