diff options
Diffstat (limited to 'src/boost/libs/proto/test/external_transforms.cpp')
-rw-r--r-- | src/boost/libs/proto/test/external_transforms.cpp | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/src/boost/libs/proto/test/external_transforms.cpp b/src/boost/libs/proto/test/external_transforms.cpp new file mode 100644 index 00000000..f72e9458 --- /dev/null +++ b/src/boost/libs/proto/test/external_transforms.cpp @@ -0,0 +1,185 @@ +// Copyright 2011 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 is an example of how to specify a transform externally so +// that a single grammar can be used to drive multiple differnt +// calculations. In particular, it defines a calculator grammar +// that computes the result of an expression with either checked +// or non-checked division. + +#include <iostream> +#include <boost/mpl/int.hpp> +#include <boost/mpl/next.hpp> +#include <boost/mpl/min_max.hpp> +#include <boost/fusion/container/vector.hpp> +#include <boost/fusion/container/generation/make_vector.hpp> +#include <boost/proto/proto.hpp> +#include <boost/test/unit_test.hpp> + +namespace mpl = boost::mpl; +namespace proto = boost::proto; +namespace fusion = boost::fusion; +using proto::_; + +// The argument placeholder type +template<typename I> struct placeholder : I {}; + +// Give each rule in the grammar a "name". This is so that we +// can easily dispatch on it later. +struct calc_grammar; +struct divides_rule : proto::divides<calc_grammar, calc_grammar> {}; + +// Use external transforms in calc_gramar +struct calc_grammar + : proto::or_< + proto::when< + proto::terminal<placeholder<_> > + , proto::functional::at(proto::_state, proto::_value) + > + , proto::when< + proto::terminal<proto::convertible_to<double> > + , proto::_value + > + , proto::when< + proto::plus<calc_grammar, calc_grammar> + , proto::_default<calc_grammar> + > + , proto::when< + proto::minus<calc_grammar, calc_grammar> + , proto::_default<calc_grammar> + > + , proto::when< + proto::multiplies<calc_grammar, calc_grammar> + , proto::_default<calc_grammar> + > + // Note that we don't specify how division nodes are + // handled here. Proto::external_transform is a placeholder + // for an actual transform. + , proto::when< + divides_rule + , proto::external_transform + > + > +{}; + +template<typename E> struct calc_expr; +struct calc_domain : proto::domain<proto::generator<calc_expr> > {}; + +template<typename E> +struct calc_expr + : proto::extends<E, calc_expr<E>, calc_domain> +{ + calc_expr(E const &e = E()) : calc_expr::proto_extends(e) {} +}; + +calc_expr<proto::terminal<placeholder<mpl::int_<0> > >::type> _1; +calc_expr<proto::terminal<placeholder<mpl::int_<1> > >::type> _2; + +// Use proto::external_transforms to map from named grammar rules to +// transforms. +struct non_checked_division + : proto::external_transforms< + proto::when< divides_rule, proto::_default<calc_grammar> > + > +{}; + +struct division_by_zero : std::exception {}; + +struct do_checked_divide + : proto::callable +{ + typedef int result_type; + int operator()(int left, int right) const + { + if (right == 0) throw division_by_zero(); + return left / right; + } +}; + +// Use proto::external_transforms again, this time to map the divides_rule +// to a transforms that performs checked division. +struct checked_division + : proto::external_transforms< + proto::when< + divides_rule + , do_checked_divide(calc_grammar(proto::_left), calc_grammar(proto::_right)) + > + > +{}; + +BOOST_PROTO_DEFINE_ENV_VAR(mydata_tag, mydata); + +void test_external_transforms() +{ + non_checked_division non_checked; + int result1 = calc_grammar()(_1 / _2, fusion::make_vector(6, 2), non_checked); + BOOST_CHECK_EQUAL(result1, 3); + + // check that additional data slots are ignored + int result2 = calc_grammar()(_1 / _2, fusion::make_vector(8, 2), (non_checked, mydata = "foo")); + BOOST_CHECK_EQUAL(result2, 4); + + // check that we can use the dedicated slot for this purpose + int result3 = calc_grammar()(_1 / _2, fusion::make_vector(8, 2), (42, proto::transforms = non_checked, mydata = "foo")); + BOOST_CHECK_EQUAL(result2, 4); + + checked_division checked; + try + { + // This should throw + int result3 = calc_grammar()(_1 / _2, fusion::make_vector(6, 0), checked); + BOOST_CHECK(!"Didn't throw an exception"); // shouldn't get here! + } + catch(division_by_zero) + { + ; // OK + } + catch(...) + { + BOOST_CHECK(!"Unexpected exception"); // shouldn't get here! + } + + try + { + // This should throw + int result4 = calc_grammar()(_1 / _2, fusion::make_vector(6, 0), (checked, mydata = test_external_transforms)); + BOOST_CHECK(!"Didn't throw an exception"); // shouldn't get here! + } + catch(division_by_zero) + { + ; // OK + } + catch(...) + { + BOOST_CHECK(!"Unexpected exception"); // shouldn't get here! + } + + try + { + // This should throw + int result5 = calc_grammar()(_1 / _2, fusion::make_vector(6, 0), (42, proto::transforms = checked, mydata = test_external_transforms)); + BOOST_CHECK(!"Didn't throw an exception"); // shouldn't get here! + } + catch(division_by_zero) + { + ; // OK + } + catch(...) + { + BOOST_CHECK(!"Unexpected exception"); // shouldn't get here! + } +} + +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 for external transforms"); + + test->add(BOOST_TEST_CASE(&test_external_transforms)); + + return test; +} |