f306b88004
[SVN r58336]
189 lines
5.9 KiB
C++
189 lines
5.9 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// 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 <sstream>
|
|
#include <boost/mpl/int.hpp>
|
|
#include <boost/mpl/min_max.hpp>
|
|
#include <boost/mpl/eval_if.hpp>
|
|
#include <boost/mpl/identity.hpp>
|
|
#include <boost/mpl/next_prior.hpp>
|
|
#include <boost/fusion/tuple.hpp>
|
|
#include <boost/typeof/typeof.hpp>
|
|
#include <boost/typeof/std/sstream.hpp>
|
|
#include <boost/typeof/std/ostream.hpp>
|
|
#include <boost/typeof/std/iostream.hpp>
|
|
#include <boost/type_traits/add_const.hpp>
|
|
#include <boost/type_traits/add_reference.hpp>
|
|
#include <boost/proto/core.hpp>
|
|
#include <boost/proto/context.hpp>
|
|
#include <boost/proto/transform.hpp>
|
|
#include <boost/test/unit_test.hpp>
|
|
#include <boost/test/floating_point_comparison.hpp>
|
|
|
|
using namespace boost;
|
|
|
|
// Forward declaration of the lambda expression wrapper
|
|
template<typename T>
|
|
struct lambda;
|
|
|
|
struct lambda_domain
|
|
: proto::domain<proto::pod_generator<lambda> >
|
|
{};
|
|
|
|
template<typename I>
|
|
struct placeholder
|
|
{
|
|
typedef I arity;
|
|
};
|
|
|
|
template<typename T>
|
|
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<placeholder_arity<_value> >() >
|
|
, when< terminal<_>, mpl::int_<0>() >
|
|
, when< nary_expr<_, vararg<_> >, fold<_, mpl::int_<0>(), mpl::max<Lambda,_state>()> >
|
|
>
|
|
{};
|
|
}
|
|
|
|
// simple wrapper for calculating a lambda expression's arity.
|
|
template<typename Expr>
|
|
struct lambda_arity
|
|
: boost::result_of<grammar::Lambda(Expr, mpl::void_, mpl::void_)>
|
|
{};
|
|
|
|
// The lambda context is the same as the default context
|
|
// with the addition of special handling for lambda placeholders
|
|
template<typename Tuple>
|
|
struct lambda_context
|
|
: proto::callable_context<lambda_context<Tuple> const>
|
|
{
|
|
lambda_context(Tuple const &args)
|
|
: args_(args)
|
|
{}
|
|
|
|
template<typename Sig>
|
|
struct result;
|
|
|
|
template<typename This, typename I>
|
|
struct result<This(proto::tag::terminal, placeholder<I> const &)>
|
|
: fusion::result_of::at<Tuple, I>
|
|
{};
|
|
|
|
template<typename I>
|
|
typename fusion::result_of::at<Tuple, I>::type
|
|
operator ()(proto::tag::terminal, placeholder<I> const &) const
|
|
{
|
|
return fusion::at<I>(this->args_);
|
|
}
|
|
|
|
Tuple args_;
|
|
};
|
|
|
|
// The lambda<> expression wrapper makes expressions polymorphic
|
|
// function objects
|
|
template<typename T>
|
|
struct lambda
|
|
{
|
|
BOOST_PROTO_BASIC_EXTENDS(T, lambda<T>, 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<T>::type
|
|
, mpl::identity<void>
|
|
, proto::result_of::eval<T const, lambda_context<fusion::tuple<> > >
|
|
>::type nullary_type;
|
|
|
|
// Define our operator () that evaluates the lambda expression.
|
|
nullary_type operator ()() const
|
|
{
|
|
fusion::tuple<> args;
|
|
lambda_context<fusion::tuple<> > ctx(args);
|
|
return proto::eval(*this, ctx);
|
|
}
|
|
|
|
#define M0(N, typename_A, A_const_ref, A_const_ref_a, ref_a) \
|
|
template<typename_A(N)> \
|
|
typename proto::result_of::eval<T const, lambda_context<fusion::tuple<A_const_ref(N)> > >::type \
|
|
operator ()(A_const_ref_a(N)) const \
|
|
{ \
|
|
fusion::tuple<A_const_ref(N)> args(ref_a(N)); \
|
|
lambda_context<fusion::tuple<A_const_ref(N)> > ctx(args); \
|
|
return proto::eval(*this, ctx); \
|
|
} \
|
|
/**/
|
|
BOOST_PROTO_REPEAT_FROM_TO(1, 4, M0)
|
|
#undef M0
|
|
};
|
|
|
|
// Define some lambda placeholders
|
|
lambda<proto::terminal<placeholder<mpl::int_<0> > >::type> const _1 = {{}};
|
|
lambda<proto::terminal<placeholder<mpl::int_<1> > >::type> const _2 = {{}};
|
|
lambda<proto::terminal<placeholder<mpl::int_<3> > >::type> const _3 = {{}};
|
|
|
|
template<typename T>
|
|
lambda<typename proto::terminal<T>::type> const val(T const &t)
|
|
{
|
|
lambda<typename proto::terminal<T>::type> that = {{t}};
|
|
return that;
|
|
}
|
|
|
|
template<typename T>
|
|
lambda<typename proto::terminal<T &>::type> const var(T &t)
|
|
{
|
|
lambda<typename proto::terminal<T &>::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;
|
|
}
|