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 | { |
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 |
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. |
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 |
137 | lambda<proto::terminal<placeholder<mpl::int_<0> > >::type> const _1 = {.proto_expr_: {}}; |
138 | lambda<proto::terminal<placeholder<mpl::int_<1> > >::type> const _2 = {.proto_expr_: {}}; |
139 | lambda<proto::terminal<placeholder<mpl::int_<3> > >::type> const _3 = {.proto_expr_: {}}; |
140 | |
141 | template<typename 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> |
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 | |
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(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 | |
177 | using namespace unit_test; |
178 | /////////////////////////////////////////////////////////////////////////////// |
179 | // init_unit_test_suite |
180 | // |
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 | |