/////////////////////////////////////////////////////////////////////////////// // 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 #include #include #include #include #include #include #include #include #include #include namespace mpl = boost::mpl; namespace proto = boost::proto; namespace fusion = boost::fusion; using proto::_; template 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 { template /*<< Transforms have a nested `impl<>` that is a valid TR1 function object. >>*/ struct impl : proto::transform_impl { /*<< Get the child. >>*/ typedef typename proto::result_of::child::type child_expr; /*<< Apply `CalculatorArity` to find the arity of the child. >>*/ typedef typename boost::result_of::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 { template /*<< Transforms have a nested `impl<>` that is a valid TR1 function object. >>*/ struct impl : proto::transform_impl { /*<< Get the left and right children. >>*/ typedef typename proto::result_of::left::type left_expr; typedef typename proto::result_of::right::type right_expr; /*<< Apply `CalculatorArity` to find the arity of the left and right children. >>*/ typedef typename boost::result_of::type left_arity; typedef typename boost::result_of::type right_arity; /*<< The return type is the maximum of the children's arities. >>*/ typedef typename mpl::max::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() > > {}; //] // BUGBUG find workaround for this #if BOOST_WORKAROUND(BOOST_MSVC, == 1310) #define _pop_front(x) call #define _value(x) call #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::vararg > > /*<< 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 /*<< Fold all terminals that are separated by commas into a Fusion cons list. >>*/ , proto::reverse_fold_tree< _ , fusion::nil() , fusion::cons(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`, then construct a `terminal::type` with the `float`. >>*/ proto::when, proto::terminal::type(proto::_value) > , proto::when > /*<< `nary_expr<>` has a pass-through transform which will transform each child sub-expression using the `Promote` transform. >>*/ , proto::when > > > {}; //] //[ LazyMakePair struct make_pair_tag {}; proto::terminal::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 , proto::terminal<_> , proto::terminal<_> > /*<< Return `std::pair(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::type const make_pair_ = {{}}; // Like std::make_pair(), only as a function object. /*<`.>>*/ struct make_pair : proto::callable { template struct result; template struct result { typedef std::pair< BOOST_PROTO_UNCVREF(First) , BOOST_PROTO_UNCVREF(Second) > type; }; template std::pair 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 , 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::negate<_>(_)> {}; //] #ifndef BOOST_MSVC //[ SquareAndPromoteInt struct SquareAndPromoteInt : proto::when< proto::terminal , proto::_make_multiplies( proto::terminal::type(proto::_value) , proto::terminal::type(proto::_value) ) > {}; //] #endif namespace lambda_transform { //[LambdaTransform template struct placeholder : N {}; // A function object that calls fusion::at() struct at : proto::callable { template struct result; template struct result : fusion::result_of::at< typename boost::remove_reference::type , typename boost::remove_reference::type > {}; template typename fusion::result_of::at::type operator ()(Cont &cont, Index const &) const { return fusion::at(cont); } }; // A transform that evaluates a lambda expression. struct LambdaEval : proto::or_< /*<>*/ proto::when< proto::terminal > /*<<... call at() with the data parameter, which is a tuple, and the placeholder, which is an MPL Integral Constant.>>*/ , at(proto::_data, proto::_value) > /*< transform, which gives the operators their usual C++ meanings.>>*/ , proto::otherwise< proto::_default > > {}; // Define the lambda placeholders proto::terminal > >::type const _1 = {}; proto::terminal > >::type const _2 = {}; void test_lambda() { // a tuple that contains the values // of _1 and _2 fusion::tuple 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 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 > > 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 > > 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::type , proto::terminal::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 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 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; }