diff options
Diffstat (limited to 'src/boost/libs/proto/example/virtual_member.cpp')
-rw-r--r-- | src/boost/libs/proto/example/virtual_member.cpp | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/src/boost/libs/proto/example/virtual_member.cpp b/src/boost/libs/proto/example/virtual_member.cpp new file mode 100644 index 00000000..46183bae --- /dev/null +++ b/src/boost/libs/proto/example/virtual_member.cpp @@ -0,0 +1,306 @@ +//[ VirtualMember +// 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 demonstrates how to use BOOST_PROTO_EXTENDS_MEMBERS() +// to add "virtual" data members to expressions within a domain. For +// instance, with Phoenix you can create a lambda expression such as +// +// if_(_1 > 0)[ std::cout << _2 ].else_[ std::cout << _3 ] +// +// In the above expression, "else_" is a so-called virtual data member +// of the expression "if_(_1 > 0)[ std::cout << _2 ]". This example +// shows how to implement the ".else_" syntax with Proto. +// +// ****WARNING****WARNING****WARNING****WARNING****WARNING****WARNING**** +// * The virtual data member feature is experimental and can change at * +// * any time. Use it at your own risk. * +// ********************************************************************** + +#if defined(_MSC_VER) && _MSC_VER == 1310 +#error "Sorry, this example doesn\'t work with MSVC 7.1" +#endif + +#include <iostream> +#include <boost/config.hpp> +#include <boost/detail/workaround.hpp> +#include <boost/mpl/eval_if.hpp> +#include <boost/mpl/min_max.hpp> +#include <boost/mpl/next_prior.hpp> +#include <boost/fusion/include/at.hpp> +#include <boost/fusion/include/vector.hpp> +#include <boost/typeof/std/ostream.hpp> +#include <boost/proto/proto.hpp> + +namespace mpl = boost::mpl; +namespace proto = boost::proto; +namespace fusion = boost::fusion; +using proto::_; + +namespace mini_lambda +{ + // A callable PolymorphicFunctionObject that wraps + // fusion::at() + struct at : proto::callable + { + template<class Sig> + struct result; + + template<class This, class Vector, class N> + struct result<This(Vector, N)> + : fusion::result_of::at< + typename boost::remove_reference<Vector>::type + , typename boost::remove_reference<N>::type + > + {}; + + template<class Vector, class N> + typename fusion::result_of::at<Vector const, N>::type + operator()(Vector const &vector, N) const + { + return fusion::at<N>(vector); + } + }; + + // An MPL IntegralConstant + template<class N> + struct placeholder + { + typedef N type; + typedef typename N::tag tag; + typedef typename N::next next; + typedef typename N::prior prior; + typedef typename N::value_type value_type; + static const value_type value = N::value; + }; + + // Some keyword types for our lambda EDSL + namespace keyword + { + struct if_ {}; + struct else_ {}; + struct do_ {}; + struct while_ {}; + struct try_ {}; + struct catch_ {}; + } + + // Forward declaration for the mini-lambda grammar + struct eval_if_else; + + // Forward declaration for the mini-lambda expression wrapper + template<class E> + struct expression; + + // The grammar for mini-lambda expressions with transforms for + // evaluating the lambda expression. + struct grammar + : proto::or_< + // When evaluating a placeholder, use the placeholder + // to index into the "data" parameter, which is a fusion + // vector containing the arguments to the lambda expression. + proto::when< + proto::terminal<placeholder<_> > + , at(proto::_data, proto::_value) + > + // When evaluating if/then/else expressions of the form + // "if_( E0 )[ E1 ].else_[ E2 ]", pass E0, E1 and E2 to + // eval_if_else along with the "data" parameter. Note the + // use of proto::member<> to match binary expressions like + // "X.Y" where "Y" is a virtual data member. + , proto::when< + proto::subscript< + proto::member< + proto::subscript< + proto::function< + proto::terminal<keyword::if_> + , grammar + > + , grammar + > + , proto::terminal<keyword::else_> + > + , grammar + > + , eval_if_else( + proto::_right(proto::_left(proto::_left(proto::_left))) + , proto::_right(proto::_left(proto::_left)) + , proto::_right + , proto::_data + ) + > + , proto::otherwise< + proto::_default<grammar> + > + > + {}; + + // A callable PolymorphicFunctionObject that evaluates + // if/then/else expressions. + struct eval_if_else : proto::callable + { + typedef void result_type; + + template<typename If, typename Then, typename Else, typename Args> + void operator()(If const &if_, Then const &then_, Else const &else_, Args const &args) const + { + if(grammar()(if_, 0, args)) + { + grammar()(then_, 0, args); + } + else + { + grammar()(else_, 0, args); + } + } + }; + + // Define the mini-lambda domain, in which all expressions are + // wrapped in mini_lambda::expression. + struct domain + : proto::domain<proto::pod_generator<expression> > + {}; + + // A simple transform for computing the arity of + // a lambda expression. + struct arity_of + : proto::or_< + proto::when< + proto::terminal< placeholder<_> > + , mpl::next<proto::_value>() + > + , proto::when< + proto::terminal<_> + , mpl::int_<0>() + > + , proto::otherwise< + proto::fold< + _ + , mpl::int_<0>() + , mpl::max<arity_of, proto::_state>() + > + > + > + {}; + + // Here is the mini-lambda expression wrapper. It serves two purposes: + // 1) To define operator() overloads that evaluate the lambda expression, and + // 2) To define virtual data members like "else_" so that we can write + // expressions like "if_(X)[Y].else_[Z]". + template<class E> + struct expression + { + BOOST_PROTO_BASIC_EXTENDS(E, expression<E>, domain) + BOOST_PROTO_EXTENDS_ASSIGN() + BOOST_PROTO_EXTENDS_SUBSCRIPT() + + // Use BOOST_PROTO_EXTENDS_MEMBERS() to define "virtual" + // data members that all expressions in the mini-lambda + // domain will have. They can be used to create expressions + // like "if_(x)[y].else_[z]" and "do_[y].while_(z)". + BOOST_PROTO_EXTENDS_MEMBERS( + ((keyword::else_, else_)) + ((keyword::while_, while_)) + ((keyword::catch_, catch_)) + ) + + // Calculate the arity of this lambda expression + static int const arity = boost::result_of<arity_of(E)>::type::value; + + // Define overloads of operator() that evaluate the lambda + // expression for up to 3 arguments. + + // Don't try to compute the return type of the lambda if + // it isn't nullary. + typename mpl::eval_if_c< + 0 != arity + , mpl::identity<void> + , boost::result_of<grammar( + E const & + , int const & + , fusion::vector<> & + )> + >::type + operator()() const + { + BOOST_MPL_ASSERT_RELATION(arity, ==, 0); + fusion::vector<> args; + return grammar()(proto_base(), 0, args); + } + + #define BOOST_PROTO_LOCAL_MACRO( \ + N, typename_A, A_const_ref, A_const_ref_a, a \ + ) \ + template<typename_A(N)> \ + typename boost::result_of<grammar( \ + E const & \ + , int const & \ + , fusion::vector<A_const_ref(N)> & \ + )>::type \ + operator ()(A_const_ref_a(N)) const \ + { \ + BOOST_MPL_ASSERT_RELATION(arity, <=, N); \ + fusion::vector<A_const_ref(N)> args(a(N)); \ + return grammar()(proto_base(), 0, args); \ + } + // Repeats BOOST_PROTO_LOCAL_MACRO macro for N=1 to 3 + // inclusive (because there are only 3 placeholders) + #define BOOST_PROTO_LOCAL_a BOOST_PROTO_a + #define BOOST_PROTO_LOCAL_LIMITS (1, 3) + #include BOOST_PROTO_LOCAL_ITERATE() + }; + + namespace placeholders + { + typedef placeholder<mpl::int_<0> > _1_t; + typedef placeholder<mpl::int_<1> > _2_t; + typedef placeholder<mpl::int_<2> > _3_t; + + // Define some placeholders + expression<proto::terminal<_1_t>::type> const _1 = {{{}}}; + expression<proto::terminal<_2_t>::type> const _2 = {{{}}}; + expression<proto::terminal<_3_t>::type> const _3 = {{{}}}; + + // Define the if_() statement + template<typename E> + typename proto::result_of::make_expr<proto::tag::function, domain + , keyword::if_ + , E const & + >::type const + if_(E const &e) + { + return proto::make_expr<proto::tag::function, domain>( + keyword::if_() + , boost::ref(e) + ); + } + } + + using placeholders::if_; +} + +int main() +{ + using namespace mini_lambda::placeholders; + + // OK, we can create if/then/else lambda expressions + // and evaluate them. + if_(_1 > 0) + [ + std::cout << _2 << '\n' + ] + .else_ + [ + std::cout << _3 << '\n' + ] + (-42, "positive", "non-positive"); + + // Even though all expressions in the mini-lambda + // domain have members named else_, while_, and catch_, + // they all occupy the same byte in the expression. + BOOST_MPL_ASSERT_RELATION(sizeof(_1), ==, 2); + + return 0; +} +//] |