/////////////////////////////////////////////////////////////////////////////// // lambda.hpp // // Copyright 2008 Eric Niebler. Distributed under the Boost // Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace boost; // Forward declaration of the lambda expression wrapper template struct lambda; struct lambda_domain : proto::domain > {}; template struct placeholder { typedef I arity; }; template struct placeholder_arity { typedef typename T::arity type; }; namespace grammar { using namespace proto; // The lambda grammar, with the transforms for calculating the max arity struct Lambda : or_< when< terminal< placeholder<_> >, mpl::next >() > , when< terminal<_>, mpl::int_<0>() > , when< nary_expr<_, vararg<_> >, fold<_, mpl::int_<0>(), mpl::max()> > > {}; } // simple wrapper for calculating a lambda expression's arity. template struct lambda_arity : boost::result_of {}; // The lambda context is the same as the default context // with the addition of special handling for lambda placeholders template struct lambda_context : proto::callable_context const> { lambda_context(Tuple const &args) : args_(args) {} template struct result; template struct result const &)> : fusion::result_of::at {}; template typename fusion::result_of::at::type operator ()(proto::tag::terminal, placeholder const &) const { return fusion::at(this->args_); } Tuple args_; }; // The lambda<> expression wrapper makes expressions polymorphic // function objects template struct lambda { BOOST_PROTO_BASIC_EXTENDS(T, lambda, lambda_domain) BOOST_PROTO_EXTENDS_ASSIGN() BOOST_PROTO_EXTENDS_SUBSCRIPT() // Careful not to evaluate the return type of the nullary function // unless we have a nullary lambda! typedef typename mpl::eval_if< typename lambda_arity::type , mpl::identity , proto::result_of::eval > > >::type nullary_type; // Define our operator () that evaluates the lambda expression. nullary_type operator ()() const { fusion::tuple<> args; lambda_context > ctx(args); return proto::eval(*this, ctx); } #define M0(N, typename_A, A_const_ref, A_const_ref_a, ref_a) \ template \ typename proto::result_of::eval > >::type \ operator ()(A_const_ref_a(N)) const \ { \ fusion::tuple args(ref_a(N)); \ lambda_context > ctx(args); \ return proto::eval(*this, ctx); \ } \ /**/ BOOST_PROTO_REPEAT_FROM_TO(1, 4, M0) #undef M0 }; // Define some lambda placeholders lambda > >::type> const _1 = {{}}; lambda > >::type> const _2 = {{}}; lambda > >::type> const _3 = {{}}; template lambda::type> const val(T const &t) { lambda::type> that = {{t}}; return that; } template lambda::type> const var(T &t) { lambda::type> that = {{t}}; return that; } void test_lambda() { BOOST_CHECK_EQUAL(11, ( (_1 + 2) / 4 )(42)); BOOST_CHECK_EQUAL(-11, ( (-(_1 + 2)) / 4 )(42)); BOOST_CHECK_CLOSE(2.58, ( (4 - _2) * 3 )(42, 3.14), 0.1); // check non-const ref terminals std::stringstream sout; (sout << _1 << " -- " << _2)(42, "Life, the Universe and Everything!"); BOOST_CHECK_EQUAL("42 -- Life, the Universe and Everything!", sout.str()); // check nullary lambdas BOOST_CHECK_EQUAL(3, (val(1) + val(2))()); // check array indexing for kicks int integers[5] = {0}; (var(integers)[2] = 2)(); (var(integers)[_1] = _1)(3); BOOST_CHECK_EQUAL(2, integers[2]); BOOST_CHECK_EQUAL(3, integers[3]); } using namespace unit_test; /////////////////////////////////////////////////////////////////////////////// // init_unit_test_suite // test_suite* init_unit_test_suite( int argc, char* argv[] ) { test_suite *test = BOOST_TEST_SUITE("test expression template domains"); test->add(BOOST_TEST_CASE(&test_lambda)); return test; }