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 
27 using namespace boost;
28 
29 // Forward declaration of the lambda expression wrapper
30 template<typename T>
31 struct lambda;
32 
33 struct lambda_domain
34   : proto::domain<proto::pod_generator<lambda> >
35 {};
36 
37 template<typename I>
38 struct placeholder
39 {
40     typedef I arity;
41 };
42 
43 template<typename T>
44 struct placeholder_arity
45 {
46     typedef typename T::arity type;
47 };
48 
49 namespace 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.
64 template<typename Expr>
65 struct 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
71 template<typename Tuple>
72 struct lambda_context
73   : proto::callable_context<lambda_context<Tuple> const>
74 {
lambda_contextlambda_context75     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
operator ()lambda_context89     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
99 template<typename T>
100 struct 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.
operator ()lambda115     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
137 lambda<proto::terminal<placeholder<mpl::int_<0> > >::type> const _1 = {{}};
138 lambda<proto::terminal<placeholder<mpl::int_<1> > >::type> const _2 = {{}};
139 lambda<proto::terminal<placeholder<mpl::int_<3> > >::type> const _3 = {{}};
140 
141 template<typename T>
val(T const & t)142 lambda<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 
148 template<typename T>
var(T & t)149 lambda<typename proto::terminal<T &>::type> const var(T &t)
150 {
151     lambda<typename proto::terminal<T &>::type> that = {{t}};
152     return that;
153 }
154 
test_lambda()155 void 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(integers)[2] = 2)();
172     (var(integers)[_1] = _1)(3);
173     BOOST_CHECK_EQUAL(2, integers[2]);
174     BOOST_CHECK_EQUAL(3, integers[3]);
175 }
176 
177 using namespace unit_test;
178 ///////////////////////////////////////////////////////////////////////////////
179 // init_unit_test_suite
180 //
init_unit_test_suite(int argc,char * argv[])181 test_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