499 lines
16 KiB
C++
Executable File
499 lines
16 KiB
C++
Executable File
///////////////////////////////////////////////////////////////////////////////
|
|
// examples.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 <iostream>
|
|
#include <boost/config.hpp>
|
|
#include <boost/mpl/min_max.hpp>
|
|
#include <boost/proto/core.hpp>
|
|
#include <boost/proto/transform.hpp>
|
|
#include <boost/proto/functional/fusion.hpp>
|
|
#include <boost/utility/result_of.hpp>
|
|
#include <boost/fusion/include/cons.hpp>
|
|
#include <boost/fusion/include/tuple.hpp>
|
|
#include <boost/fusion/include/pop_front.hpp>
|
|
#include <boost/test/unit_test.hpp>
|
|
|
|
namespace mpl = boost::mpl;
|
|
namespace proto = boost::proto;
|
|
namespace fusion = boost::fusion;
|
|
using proto::_;
|
|
|
|
template<int I>
|
|
struct placeholder
|
|
{};
|
|
|
|
namespace test1
|
|
{
|
|
//[ CalcGrammar
|
|
// This is the grammar for calculator expressions,
|
|
// to which we will attach transforms for computing
|
|
// the expressions' arity.
|
|
/*<< A Calculator expression is ... >>*/
|
|
struct CalcArity
|
|
: proto::or_<
|
|
/*<< _1, or ... >>*/
|
|
proto::terminal< placeholder<0> >
|
|
/*<< _2, or ... >>*/
|
|
, proto::terminal< placeholder<1> >
|
|
/*<< some other terminal, or ... >>*/
|
|
, proto::terminal< _ >
|
|
/*<< a unary expression where the operand is a calculator expression, or ... >>*/
|
|
, proto::unary_expr< _, CalcArity >
|
|
/*<< a binary expression where the operands are calculator expressions >>*/
|
|
, proto::binary_expr< _, CalcArity, CalcArity >
|
|
>
|
|
{};
|
|
//]
|
|
}
|
|
|
|
//[ binary_arity
|
|
/*<< The `CalculatorArity` is a transform for calculating
|
|
the arity of a calculator expression. It will be define in
|
|
terms of `binary_arity`, which is defined in terms of
|
|
`CalculatorArity`; hence, the definition is recursive.>>*/
|
|
struct CalculatorArity;
|
|
|
|
// A custom transform that returns the arity of a unary
|
|
// calculator expression by finding the arity of the
|
|
// child expression.
|
|
struct unary_arity
|
|
/*<< Custom transforms should inherit from
|
|
transform<>. In some cases, (e.g., when the transform
|
|
is a template), it is also necessary to specialize
|
|
the proto::is_callable<> trait. >>*/
|
|
: proto::transform<unary_arity>
|
|
{
|
|
template<typename Expr, typename State, typename Data>
|
|
/*<< Transforms have a nested `impl<>` that is
|
|
a valid TR1 function object. >>*/
|
|
struct impl
|
|
: proto::transform_impl<Expr, State, Data>
|
|
{
|
|
/*<< Get the child. >>*/
|
|
typedef typename proto::result_of::child<Expr>::type child_expr;
|
|
|
|
/*<< Apply `CalculatorArity` to find the arity of the child. >>*/
|
|
typedef typename boost::result_of<CalculatorArity(child_expr, State, Data)>::type result_type;
|
|
|
|
/*<< The `unary_arity` transform doesn't have an interesting
|
|
runtime counterpart, so just return a default-constructed object
|
|
of the correct type. >>*/
|
|
result_type operator ()(proto::ignore, proto::ignore, proto::ignore) const
|
|
{
|
|
return result_type();
|
|
}
|
|
};
|
|
};
|
|
|
|
// A custom transform that returns the arity of a binary
|
|
// calculator expression by finding the maximum of the
|
|
// arities of the mpl::int_<2> child expressions.
|
|
struct binary_arity
|
|
/*<< All custom transforms should inherit from
|
|
transform. In some cases, (e.g., when the transform
|
|
is a template), it is also necessary to specialize
|
|
the proto::is_callable<> trait. >>*/
|
|
: proto::transform<binary_arity>
|
|
{
|
|
template<typename Expr, typename State, typename Data>
|
|
/*<< Transforms have a nested `impl<>` that is
|
|
a valid TR1 function object. >>*/
|
|
struct impl
|
|
: proto::transform_impl<Expr, State, Data>
|
|
{
|
|
/*<< Get the left and right children. >>*/
|
|
typedef typename proto::result_of::left<Expr>::type left_expr;
|
|
typedef typename proto::result_of::right<Expr>::type right_expr;
|
|
|
|
/*<< Apply `CalculatorArity` to find the arity of the left and right children. >>*/
|
|
typedef typename boost::result_of<CalculatorArity(left_expr, State, Data)>::type left_arity;
|
|
typedef typename boost::result_of<CalculatorArity(right_expr, State, Data)>::type right_arity;
|
|
|
|
/*<< The return type is the maximum of the children's arities. >>*/
|
|
typedef typename mpl::max<left_arity, right_arity>::type result_type;
|
|
|
|
/*<< The `unary_arity` transform doesn't have an interesting
|
|
runtime counterpart, so just return a default-constructed object
|
|
of the correct type. >>*/
|
|
result_type operator ()(proto::ignore, proto::ignore, proto::ignore) const
|
|
{
|
|
return result_type();
|
|
}
|
|
};
|
|
};
|
|
//]
|
|
|
|
proto::terminal< placeholder<0> >::type const _1 = {};
|
|
proto::terminal< placeholder<1> >::type const _2 = {};
|
|
|
|
//[ CalculatorArityGrammar
|
|
struct CalculatorArity
|
|
: proto::or_<
|
|
proto::when< proto::terminal< placeholder<0> >, mpl::int_<1>() >
|
|
, proto::when< proto::terminal< placeholder<1> >, mpl::int_<2>() >
|
|
, proto::when< proto::terminal<_>, mpl::int_<0>() >
|
|
, proto::when< proto::unary_expr<_, _>, unary_arity >
|
|
, proto::when< proto::binary_expr<_, _, _>, binary_arity >
|
|
>
|
|
{};
|
|
//]
|
|
|
|
//[ CalcArity
|
|
struct CalcArity
|
|
: proto::or_<
|
|
proto::when< proto::terminal< placeholder<0> >,
|
|
mpl::int_<1>()
|
|
>
|
|
, proto::when< proto::terminal< placeholder<1> >,
|
|
mpl::int_<2>()
|
|
>
|
|
, proto::when< proto::terminal<_>,
|
|
mpl::int_<0>()
|
|
>
|
|
, proto::when< proto::unary_expr<_, CalcArity>,
|
|
CalcArity(proto::_child)
|
|
>
|
|
, proto::when< proto::binary_expr<_, CalcArity, CalcArity>,
|
|
mpl::max<CalcArity(proto::_left),
|
|
CalcArity(proto::_right)>()
|
|
>
|
|
>
|
|
{};
|
|
//]
|
|
|
|
// BUGBUG find workaround for this
|
|
#if BOOST_WORKAROUND(BOOST_MSVC, == 1310)
|
|
#define _pop_front(x) call<proto::_pop_front(x)>
|
|
#define _value(x) call<proto::_value(x)>
|
|
#endif
|
|
|
|
//[ AsArgList
|
|
// This transform matches function invocations such as foo(1,'a',"b")
|
|
// and transforms them into Fusion cons lists of their arguments. In this
|
|
// case, the result would be cons(1, cons('a', cons("b", nil()))).
|
|
struct ArgsAsList
|
|
: proto::when<
|
|
proto::function<proto::terminal<_>, proto::vararg<proto::terminal<_> > >
|
|
/*<< Use a `fold<>` transform to iterate over the children of this
|
|
node in forward order, building a fusion list from front to back. >>*/
|
|
, proto::fold<
|
|
/*<< The first child expression of a `function<>` node is the
|
|
function being invoked. We don't want that in our list, so use
|
|
`pop_front()` to remove it. >>*/
|
|
proto::_pop_front(_)
|
|
/*<< `nil` is the initial state used by the `fold<>` transform. >>*/
|
|
, fusion::nil()
|
|
/*<< Put the rest of the function arguments in a fusion cons
|
|
list. >>*/
|
|
, proto::functional::push_back(proto::_state, proto::_value)
|
|
>
|
|
>
|
|
{};
|
|
//]
|
|
|
|
//[ FoldTreeToList
|
|
// This transform matches expressions of the form (_1=1,'a',"b")
|
|
// (note the use of the comma operator) and transforms it into a
|
|
// Fusion cons list of their arguments. In this case, the result
|
|
// would be cons(1, cons('a', cons("b", nil()))).
|
|
struct FoldTreeToList
|
|
: proto::or_<
|
|
// This grammar describes what counts as the terminals in expressions
|
|
// of the form (_1=1,'a',"b"), which will be flattened using
|
|
// reverse_fold_tree<> below.
|
|
proto::when< proto::assign<_, proto::terminal<_> >
|
|
, proto::_value(proto::_right)
|
|
>
|
|
, proto::when< proto::terminal<_>
|
|
, proto::_value
|
|
>
|
|
, proto::when<
|
|
proto::comma<FoldTreeToList, FoldTreeToList>
|
|
/*<< Fold all terminals that are separated by commas into a Fusion cons list. >>*/
|
|
, proto::reverse_fold_tree<
|
|
_
|
|
, fusion::nil()
|
|
, fusion::cons<FoldTreeToList, proto::_state>(FoldTreeToList, proto::_state)
|
|
>
|
|
>
|
|
>
|
|
{};
|
|
//]
|
|
|
|
//[ Promote
|
|
// This transform finds all float terminals in an expression and promotes
|
|
// them to doubles.
|
|
struct Promote
|
|
: proto::or_<
|
|
/*<< Match a `terminal<float>`, then construct a
|
|
`terminal<double>::type` with the `float`. >>*/
|
|
proto::when<proto::terminal<float>, proto::terminal<double>::type(proto::_value) >
|
|
, proto::when<proto::terminal<_> >
|
|
/*<< `nary_expr<>` has a pass-through transform which
|
|
will transform each child sub-expression using the
|
|
`Promote` transform. >>*/
|
|
, proto::when<proto::nary_expr<_, proto::vararg<Promote> > >
|
|
>
|
|
{};
|
|
//]
|
|
|
|
//[ LazyMakePair
|
|
struct make_pair_tag {};
|
|
proto::terminal<make_pair_tag>::type const make_pair_ = {{}};
|
|
|
|
// This transform matches lazy function invocations like
|
|
// `make_pair_(1, 3.14)` and actually builds a `std::pair<>`
|
|
// from the arguments.
|
|
struct MakePair
|
|
: proto::when<
|
|
/*<< Match expressions like `make_pair_(1, 3.14)` >>*/
|
|
proto::function<
|
|
proto::terminal<make_pair_tag>
|
|
, proto::terminal<_>
|
|
, proto::terminal<_>
|
|
>
|
|
/*<< Return `std::pair<F,S>(f,s)` where `f` and `s` are the
|
|
first and second arguments to the lazy `make_pair_()` function.
|
|
(This uses `proto::make<>` under the covers to evaluate the
|
|
transform.)>>*/
|
|
, std::pair<
|
|
proto::_value(proto::_child1)
|
|
, proto::_value(proto::_child2)
|
|
>(
|
|
proto::_value(proto::_child1)
|
|
, proto::_value(proto::_child2)
|
|
)
|
|
>
|
|
{};
|
|
//]
|
|
|
|
namespace lazy_make_pair2
|
|
{
|
|
//[ LazyMakePair2
|
|
struct make_pair_tag {};
|
|
proto::terminal<make_pair_tag>::type const make_pair_ = {{}};
|
|
|
|
// Like std::make_pair(), only as a function object.
|
|
/*<<Inheriting from `proto::callable` lets Proto know
|
|
that this is a callable transform, so we can use it
|
|
without having to wrap it in `proto::call<>`.>>*/
|
|
struct make_pair : proto::callable
|
|
{
|
|
template<typename Sig> struct result;
|
|
|
|
template<typename This, typename First, typename Second>
|
|
struct result<This(First, Second)>
|
|
{
|
|
typedef
|
|
std::pair<
|
|
BOOST_PROTO_UNCVREF(First)
|
|
, BOOST_PROTO_UNCVREF(Second)
|
|
>
|
|
type;
|
|
};
|
|
|
|
template<typename First, typename Second>
|
|
std::pair<First, Second>
|
|
operator()(First const &first, Second const &second) const
|
|
{
|
|
return std::make_pair(first, second);
|
|
}
|
|
};
|
|
|
|
// This transform matches lazy function invocations like
|
|
// `make_pair_(1, 3.14)` and actually builds a `std::pair<>`
|
|
// from the arguments.
|
|
struct MakePair
|
|
: proto::when<
|
|
/*<< Match expressions like `make_pair_(1, 3.14)` >>*/
|
|
proto::function<
|
|
proto::terminal<make_pair_tag>
|
|
, proto::terminal<_>
|
|
, proto::terminal<_>
|
|
>
|
|
/*<< Return `make_pair()(f,s)` where `f` and `s` are the
|
|
first and second arguments to the lazy `make_pair_()` function.
|
|
(This uses `proto::call<>` under the covers to evaluate the
|
|
transform.)>>*/
|
|
, make_pair(
|
|
proto::_value(proto::_child1)
|
|
, proto::_value(proto::_child2)
|
|
)
|
|
>
|
|
{};
|
|
//]
|
|
}
|
|
|
|
|
|
//[ NegateInt
|
|
struct NegateInt
|
|
: proto::when<proto::terminal<int>, proto::negate<_>(_)>
|
|
{};
|
|
//]
|
|
|
|
#ifndef BOOST_MSVC
|
|
//[ SquareAndPromoteInt
|
|
struct SquareAndPromoteInt
|
|
: proto::when<
|
|
proto::terminal<int>
|
|
, proto::_make_multiplies(
|
|
proto::terminal<long>::type(proto::_value)
|
|
, proto::terminal<long>::type(proto::_value)
|
|
)
|
|
>
|
|
{};
|
|
//]
|
|
#endif
|
|
|
|
namespace lambda_transform
|
|
{
|
|
//[LambdaTransform
|
|
template<typename N>
|
|
struct placeholder : N {};
|
|
|
|
// A function object that calls fusion::at()
|
|
struct at : proto::callable
|
|
{
|
|
template<typename Sig>
|
|
struct result;
|
|
|
|
template<typename This, typename Cont, typename Index>
|
|
struct result<This(Cont, Index)>
|
|
: fusion::result_of::at<
|
|
typename boost::remove_reference<Cont>::type
|
|
, typename boost::remove_reference<Index>::type
|
|
>
|
|
{};
|
|
|
|
template<typename Cont, typename Index>
|
|
typename fusion::result_of::at<Cont, Index>::type
|
|
operator ()(Cont &cont, Index const &) const
|
|
{
|
|
return fusion::at<Index>(cont);
|
|
}
|
|
};
|
|
|
|
// A transform that evaluates a lambda expression.
|
|
struct LambdaEval
|
|
: proto::or_<
|
|
/*<<When you match a placeholder ...>>*/
|
|
proto::when<
|
|
proto::terminal<placeholder<_> >
|
|
/*<<... call at() with the data parameter, which
|
|
is a tuple, and the placeholder, which is an MPL
|
|
Integral Constant.>>*/
|
|
, at(proto::_data, proto::_value)
|
|
>
|
|
/*<<Otherwise, use the _default<> transform, which
|
|
gives the operators their usual C++ meanings.>>*/
|
|
, proto::otherwise< proto::_default<LambdaEval> >
|
|
>
|
|
{};
|
|
|
|
// Define the lambda placeholders
|
|
proto::terminal<placeholder<mpl::int_<0> > >::type const _1 = {};
|
|
proto::terminal<placeholder<mpl::int_<1> > >::type const _2 = {};
|
|
|
|
void test_lambda()
|
|
{
|
|
// a tuple that contains the values
|
|
// of _1 and _2
|
|
fusion::tuple<int, int> tup(2,3);
|
|
|
|
// Use LambdaEval to evaluate a lambda expression
|
|
int j = LambdaEval()( _2 - _1, 0, tup );
|
|
BOOST_CHECK_EQUAL(j, 1);
|
|
|
|
// You can mutate leaves in an expression tree
|
|
proto::literal<int> k(42);
|
|
int &l = LambdaEval()( k += 4, 0, tup );
|
|
BOOST_CHECK_EQUAL(k.get(), 46);
|
|
BOOST_CHECK_EQUAL(&l, &k.get());
|
|
|
|
// You can mutate the values in the tuple, too.
|
|
LambdaEval()( _1 += 4, 0, tup );
|
|
BOOST_CHECK_EQUAL(6, fusion::at_c<0>(tup));
|
|
}
|
|
//]
|
|
}
|
|
|
|
void test_examples()
|
|
{
|
|
//[ CalculatorArityTest
|
|
int i = 0; // not used, dummy state and data parameter
|
|
|
|
std::cout << CalculatorArity()( proto::lit(100) * 200, i, i) << '\n';
|
|
std::cout << CalculatorArity()( (_1 - _1) / _1 * 100, i, i) << '\n';
|
|
std::cout << CalculatorArity()( (_2 - _1) / _2 * 100, i, i) << '\n';
|
|
//]
|
|
|
|
BOOST_CHECK_EQUAL(0, CalculatorArity()( proto::lit(100) * 200, i, i));
|
|
BOOST_CHECK_EQUAL(1, CalculatorArity()( (_1 - _1) / _1 * 100, i, i));
|
|
BOOST_CHECK_EQUAL(2, CalculatorArity()( (_2 - _1) / _2 * 100, i, i));
|
|
|
|
BOOST_CHECK_EQUAL(0, CalcArity()( proto::lit(100) * 200, i, i));
|
|
BOOST_CHECK_EQUAL(1, CalcArity()( (_1 - _1) / _1 * 100, i, i));
|
|
BOOST_CHECK_EQUAL(2, CalcArity()( (_2 - _1) / _2 * 100, i, i));
|
|
|
|
using boost::fusion::cons;
|
|
using boost::fusion::nil;
|
|
cons<int, cons<char, cons<std::string> > > args(ArgsAsList()( _1(1, 'a', std::string("b")), i, i ));
|
|
BOOST_CHECK_EQUAL(args.car, 1);
|
|
BOOST_CHECK_EQUAL(args.cdr.car, 'a');
|
|
BOOST_CHECK_EQUAL(args.cdr.cdr.car, std::string("b"));
|
|
|
|
cons<int, cons<char, cons<std::string> > > lst(FoldTreeToList()( (_1 = 1, 'a', std::string("b")), i, i ));
|
|
BOOST_CHECK_EQUAL(lst.car, 1);
|
|
BOOST_CHECK_EQUAL(lst.cdr.car, 'a');
|
|
BOOST_CHECK_EQUAL(lst.cdr.cdr.car, std::string("b"));
|
|
|
|
proto::plus<
|
|
proto::terminal<double>::type
|
|
, proto::terminal<double>::type
|
|
>::type p = Promote()( proto::lit(1.f) + 2.f, i, i );
|
|
|
|
//[ LazyMakePairTest
|
|
int j = 0; // not used, dummy state and data parameter
|
|
|
|
std::pair<int, double> p2 = MakePair()( make_pair_(1, 3.14), j, j );
|
|
|
|
std::cout << p2.first << std::endl;
|
|
std::cout << p2.second << std::endl;
|
|
//]
|
|
|
|
BOOST_CHECK_EQUAL(p2.first, 1);
|
|
BOOST_CHECK_EQUAL(p2.second, 3.14);
|
|
|
|
std::pair<int, double> p3 = lazy_make_pair2::MakePair()( lazy_make_pair2::make_pair_(1, 3.14), j, j );
|
|
|
|
std::cout << p3.first << std::endl;
|
|
std::cout << p3.second << std::endl;
|
|
|
|
BOOST_CHECK_EQUAL(p3.first, 1);
|
|
BOOST_CHECK_EQUAL(p3.second, 3.14);
|
|
|
|
NegateInt()(proto::lit(1), i, i);
|
|
#ifndef BOOST_MSVC
|
|
SquareAndPromoteInt()(proto::lit(1), i, i);
|
|
#endif
|
|
|
|
lambda_transform::test_lambda();
|
|
}
|
|
|
|
using namespace boost::unit_test;
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// init_unit_test_suite
|
|
//
|
|
test_suite* init_unit_test_suite( int argc, char* argv[] )
|
|
{
|
|
test_suite *test = BOOST_TEST_SUITE("test examples from the documentation");
|
|
|
|
test->add(BOOST_TEST_CASE(&test_examples));
|
|
|
|
return test;
|
|
}
|