Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r49910 - trunk/libs/proto/example
From: eric_at_[hidden]
Date: 2008-11-24 02:17:41


Author: eric_niebler
Date: 2008-11-24 02:17:40 EST (Mon, 24 Nov 2008)
New Revision: 49910
URL: http://svn.boost.org/trac/boost/changeset/49910

Log:
add an example of virtual data members
Added:
   trunk/libs/proto/example/virtual_member.cpp (contents, props changed)
Text files modified:
   trunk/libs/proto/example/Jamfile.v2 | 4 ++++
   1 files changed, 4 insertions(+), 0 deletions(-)

Modified: trunk/libs/proto/example/Jamfile.v2
==============================================================================
--- trunk/libs/proto/example/Jamfile.v2 (original)
+++ trunk/libs/proto/example/Jamfile.v2 2008-11-24 02:17:40 EST (Mon, 24 Nov 2008)
@@ -68,3 +68,7 @@
         mini_lambda.cpp
     ;
 
+exe virtual_member
+ :
+ virtual_member.cpp
+ ;

Added: trunk/libs/proto/example/virtual_member.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/proto/example/virtual_member.cpp 2008-11-24 02:17:40 EST (Mon, 24 Nov 2008)
@@ -0,0 +1,318 @@
+//[ 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;
+ BOOST_STATIC_CONSTANT(value_type, value = N::value);
+ };
+
+ // Some keyword types for our lambda DSEL
+ namespace keyword
+ {
+ struct if_ {};
+ struct else_ {};
+ struct do_ {};
+ struct while_ {};
+ struct try_ {};
+ struct catch_ {};
+ }
+
+ // Forward declaration for the mini-lambda grammar
+ struct 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);
+ }
+ }
+ };
+
+ // 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>
+ >
+ >
+ {};
+
+ // 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::when<
+ proto::nary_expr<_, proto::vararg<_> >
+ , 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_))
+ )
+
+ BOOST_STATIC_CONSTANT(
+ int, 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::vector0 &)>
+ >::type
+ operator()() const
+ {
+ BOOST_MPL_ASSERT_RELATION(arity, ==, 0);
+ fusion::vector0 args;
+ return grammar()(proto_base(), 0, args);
+ }
+
+ template<typename A0>
+ typename boost::result_of<grammar(
+ E const &
+ , int const &
+ , fusion::vector1<A0 const &> &
+ )>::type
+ operator ()(A0 const &a0) const
+ {
+ BOOST_MPL_ASSERT_RELATION(arity, <=, 1);
+ fusion::vector1<A0 const &> args(a0);
+ return grammar()(proto_base(), 0, args);
+ }
+
+ template<typename A0, typename A1>
+ typename boost::result_of<grammar(
+ E const &
+ , int const &
+ , fusion::vector2<A0 const &, A1 const &> &
+ )>::type
+ operator ()(A0 const &a0, A1 const &a1) const
+ {
+ BOOST_MPL_ASSERT_RELATION(arity, <=, 2);
+ fusion::vector2<A0 const &, A1 const &> args(a0, a1);
+ return grammar()(proto_base(), 0, args);
+ }
+
+ template<typename A0, typename A1, typename A2>
+ typename boost::result_of<grammar(
+ E const &
+ , int const &
+ , fusion::vector3<A0 const &, A1 const &, A2 const &> &
+ )>::type
+ operator ()(A0 const &a0, A1 const &a1, A2 const &a2) const
+ {
+ BOOST_MPL_ASSERT_RELATION(arity, <=, 3);
+ fusion::vector3<A0 const &, A1 const &, A2 const &> args(a0, a1, a2);
+ return grammar()(proto_base(), 0, args);
+ }
+ };
+
+ 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;
+}
+//]


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk