//[ Lambda /////////////////////////////////////////////////////////////////////////////// // 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) // // This example builds a simple but functional lambda library using Proto. #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mpl = boost::mpl; namespace proto = boost::proto; namespace fusion = boost::fusion; using proto::_; // 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; }; // The lambda grammar, with the transforms for calculating the max arity struct lambda_arity : proto::or_< proto::when< proto::terminal< placeholder<_> > , mpl::next >() > , proto::when< proto::terminal<_> , mpl::int_<0>() > , proto::when< proto::nary_expr<_, proto::vararg<_> > , proto::fold<_, mpl::int_<0>(), mpl::max()> > > {}; // 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() // Calculate the arity of this lambda expression static int const arity = boost::result_of::type::value; template struct result; // Define nested result<> specializations to calculate the return // type of this lambda expression. But be careful not to evaluate // the return type of the nullary function unless we have a nullary // lambda! template struct result : mpl::eval_if_c< 0 == arity , proto::result_of::eval > > , mpl::identity > {}; template struct result : proto::result_of::eval > > {}; template struct result : proto::result_of::eval > > {}; // Define our operator () that evaluates the lambda expression. typename result::type operator ()() const { fusion::tuple<> args; lambda_context > ctx(args); return proto::eval(*this, ctx); } template typename result::type operator ()(A0 const &a0) const { fusion::tuple args(a0); lambda_context > ctx(args); return proto::eval(*this, ctx); } template typename result::type operator ()(A0 const &a0, A1 const &a1) const { fusion::tuple args(a0, a1); lambda_context > ctx(args); return proto::eval(*this, ctx); } }; // Define some lambda placeholders lambda > >::type> const _1 = {{}}; lambda > >::type> const _2 = {{}}; 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; } template struct construct_helper { typedef T result_type; // for TR1 result_of T operator()() const { return T(); } // Generate BOOST_PROTO_MAX_ARITY overloads of the // following function call operator. #define BOOST_PROTO_LOCAL_MACRO(N, typename_A, A_const_ref, A_const_ref_a, a)\ template \ T operator()(A_const_ref_a(N)) const \ { return T(a(N)); } #define BOOST_PROTO_LOCAL_a BOOST_PROTO_a #include BOOST_PROTO_LOCAL_ITERATE() }; // Generate BOOST_PROTO_MAX_ARITY-1 overloads of the // following construct() function template. #define M0(N, typename_A, A_const_ref, A_const_ref_a, ref_a) \ template \ typename proto::result_of::make_expr< \ proto::tag::function \ , lambda_domain \ , construct_helper \ , A_const_ref(N) \ >::type const \ construct(A_const_ref_a(N)) \ { \ return proto::make_expr< \ proto::tag::function \ , lambda_domain \ >( \ construct_helper() \ , ref_a(N) \ ); \ } BOOST_PROTO_REPEAT_FROM_TO(1, BOOST_PROTO_MAX_ARITY, M0) #undef M0 struct S { S() {} S(int i, char c) { std::cout << "S(" << i << "," << c << ")\n"; } }; int main() { // Create some lambda objects and immediately // invoke them by applying their operator(): int i = ( (_1 + 2) / 4 )(42); std::cout << i << std::endl; // prints 11 int j = ( (-(_1 + 2)) / 4 )(42); std::cout << j << std::endl; // prints -11 double d = ( (4 - _2) * 3 )(42, 3.14); std::cout << d << std::endl; // prints 2.58 // check non-const ref terminals (std::cout << _1 << " -- " << _2 << '\n')(42, "Life, the Universe and Everything!"); // prints "42 -- Life, the Universe and Everything!" // "Nullary" lambdas work too int k = (val(1) + val(2))(); std::cout << k << std::endl; // prints 3 // check array indexing for kicks int integers[5] = {0}; (var(integers)[2] = 2)(); (var(integers)[_1] = _1)(3); std::cout << integers[2] << std::endl; // prints 2 std::cout << integers[3] << std::endl; // prints 3 // Now use a lambda with an STL algorithm! int rgi[4] = {1,2,3,4}; char rgc[4] = {'a','b','c','d'}; S rgs[4]; std::transform(rgi, rgi+4, rgc, rgs, construct(_1, _2)); return 0; } //]