Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r68544 - in trunk: boost/proto boost/proto/transform libs/proto/doc libs/proto/doc/reference libs/proto/doc/reference/transform libs/proto/test
From: eric_at_[hidden]
Date: 2011-01-29 07:38:42


Author: eric_niebler
Date: 2011-01-29 07:38:37 EST (Sat, 29 Jan 2011)
New Revision: 68544
URL: http://svn.boost.org/trac/boost/changeset/68544

Log:
add proto::let and proto::local
Added:
   trunk/boost/proto/transform/let.hpp (contents, props changed)
   trunk/libs/proto/doc/reference/transform.xml (contents, props changed)
   trunk/libs/proto/doc/reference/transform/let.xml (contents, props changed)
   trunk/libs/proto/test/let.cpp (contents, props changed)
Text files modified:
   trunk/boost/proto/proto_fwd.hpp | 18 ++++++++++++++++++
   trunk/boost/proto/transform.hpp | 1 +
   trunk/libs/proto/doc/reference.xml | 11 +++++++++++
   trunk/libs/proto/test/Jamfile.v2 | 1 +
   4 files changed, 31 insertions(+), 0 deletions(-)

Modified: trunk/boost/proto/proto_fwd.hpp
==============================================================================
--- trunk/boost/proto/proto_fwd.hpp (original)
+++ trunk/boost/proto/proto_fwd.hpp 2011-01-29 07:38:37 EST (Sat, 29 Jan 2011)
@@ -696,6 +696,12 @@
     template<typename Fun>
     struct lazy;
 
+ template<
+ BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT(BOOST_PROTO_MAX_ARITY, typename Local, void)
+ , typename Transform = void
+ >
+ struct let;
+
     template<typename Sequence, typename State, typename Fun>
     struct fold;
 
@@ -754,6 +760,18 @@
 
     namespace exops = exprns_;
 
+ template<typename Tag>
+ struct local;
+
+ // Define some placeholders to be used to define local variables
+ typedef local<struct _a_tag> _a;
+ typedef local<struct _b_tag> _b;
+ typedef local<struct _c_tag> _c;
+ typedef local<struct _d_tag> _d;
+ typedef local<struct _e_tag> _e;
+ typedef local<struct _f_tag> _f;
+ typedef local<struct _g_tag> _g;
+
 }} // namespace boost::proto
 
 #endif

Modified: trunk/boost/proto/transform.hpp
==============================================================================
--- trunk/boost/proto/transform.hpp (original)
+++ trunk/boost/proto/transform.hpp 2011-01-29 07:38:37 EST (Sat, 29 Jan 2011)
@@ -15,6 +15,7 @@
 #include <boost/proto/transform/fold.hpp>
 #include <boost/proto/transform/fold_tree.hpp>
 #include <boost/proto/transform/lazy.hpp>
+#include <boost/proto/transform/let.hpp>
 #include <boost/proto/transform/make.hpp>
 #include <boost/proto/transform/pass_through.hpp>
 #include <boost/proto/transform/when.hpp>

Added: trunk/boost/proto/transform/let.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/proto/transform/let.hpp 2011-01-29 07:38:37 EST (Sat, 29 Jan 2011)
@@ -0,0 +1,515 @@
+#ifndef BOOST_PP_IS_ITERATING
+ ///////////////////////////////////////////////////////////////////////////////
+ /// \file let.hpp
+ /// Contains definition of the let transform.
+ //
+ // Copyright 2010 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)
+
+ #ifndef BOOST_PROTO_TRANSFORM_LET_HPP_EAN_12_04_2010
+ #define BOOST_PROTO_TRANSFORM_LET_HPP_EAN_12_04_2010
+
+ #include <boost/preprocessor/cat.hpp>
+ #include <boost/preprocessor/facilities/intercept.hpp>
+ #include <boost/preprocessor/arithmetic/inc.hpp>
+ #include <boost/preprocessor/repetition/repeat.hpp>
+ #include <boost/preprocessor/repetition/enum.hpp>
+ #include <boost/preprocessor/repetition/enum_trailing.hpp>
+ #include <boost/preprocessor/repetition/enum_params.hpp>
+ #include <boost/preprocessor/repetition/enum_binary_params.hpp>
+ #include <boost/preprocessor/repetition/enum_trailing_params.hpp>
+ #include <boost/preprocessor/repetition/enum_params_with_a_default.hpp>
+ #include <boost/preprocessor/repetition/enum_trailing_binary_params.hpp>
+ #include <boost/preprocessor/facilities/intercept.hpp>
+ #include <boost/preprocessor/iteration/iterate.hpp>
+ #include <boost/utility/enable_if.hpp>
+ #include <boost/mpl/if.hpp>
+ #include <boost/mpl/eval_if.hpp>
+ #include <boost/mpl/identity.hpp>
+ #include <boost/mpl/aux_/template_arity.hpp>
+ #include <boost/mpl/aux_/lambda_arity_param.hpp>
+ #include <boost/fusion/include/map.hpp>
+ #include <boost/fusion/include/at_key.hpp>
+ #include <boost/proto/proto_fwd.hpp>
+ #include <boost/proto/traits.hpp>
+ #include <boost/proto/transform/impl.hpp>
+
+ namespace boost { namespace proto
+ {
+ template<typename T>
+ struct is_let
+ : mpl::false_
+ {};
+
+ template<BOOST_PP_ENUM_PARAMS(BOOST_PP_INC(BOOST_PROTO_MAX_ARITY), typename T)>
+ struct is_let<let<BOOST_PP_ENUM_PARAMS(BOOST_PP_INC(BOOST_PROTO_MAX_ARITY), T)> >
+ : mpl::true_
+ {};
+
+ namespace detail
+ {
+ // A structure that holds both a map of local variables as
+ // well as the original Data parameter passed to the let transform
+ template<typename LocalMap, typename Data>
+ struct let_scope
+ {
+ LocalMap locals;
+ Data data;
+
+ typedef Data data_type;
+
+ data_type get_data()
+ {
+ return data;
+ }
+
+ template<typename Key>
+ struct at_key
+ : fusion::result_of::at_key<LocalMap, Key>
+ {};
+
+ template<typename Key>
+ typename fusion::result_of::at_key<LocalMap, Key>::type
+ get_local()
+ {
+ return fusion::at_key<Key>(locals);
+ }
+ };
+
+ // Specialization to handle an outer let scope that holds a
+ // reference to an inner let scope.
+ template<typename LocalMap, typename OtherMap, typename Data>
+ struct let_scope<LocalMap, let_scope<OtherMap, Data> &>
+ {
+ LocalMap locals;
+ let_scope<OtherMap, Data> &data;
+
+ typedef typename let_scope<OtherMap, Data>::data_type data_type;
+
+ data_type get_data()
+ {
+ return data.get_data();
+ }
+
+ template<typename Key>
+ struct at_key
+ : mpl::eval_if_c<
+ fusion::result_of::has_key<LocalMap, Key>::value
+ , fusion::result_of::at_key<LocalMap, Key>
+ , typename let_scope<OtherMap, Data>::template at_key<Key>
+ >
+ {};
+
+ template<typename Key>
+ typename at_key<Key>::type get_local()
+ {
+ return this->get_local_impl<Key>(fusion::result_of::has_key<LocalMap, Key>());
+ }
+
+ private:
+ template<typename Key>
+ typename at_key<Key>::type get_local_impl(mpl::true_)
+ {
+ return fusion::at_key<Key>(locals);
+ }
+
+ template<typename Key>
+ typename at_key<Key>::type get_local_impl(mpl::false_)
+ {
+ return data.get_local<Key>();
+ }
+ };
+
+ template<typename Expr, typename State, typename Data
+ BOOST_PP_ENUM_TRAILING_BINARY_PARAMS(BOOST_PROTO_MAX_ARITY, typename Local, = void BOOST_PP_INTERCEPT)
+ , typename Transform = void
+ >
+ struct init_local_map;
+
+ // A transnform that fetches the original data parameter out of a let_scope
+ struct _get_data : transform<_get_data>
+ {
+ template<typename Expr, typename State, typename Data>
+ struct impl;
+
+ template<typename Expr, typename State, typename LocalMap, typename Data>
+ struct impl<Expr, State, let_scope<LocalMap, Data> &>
+ : transform_impl<Expr, State, let_scope<LocalMap, Data> &>
+ {
+ typedef typename let_scope<LocalMap, Data>::data_type result_type;
+
+ result_type operator()(
+ typename impl::expr_param
+ , typename impl::state_param
+ , typename impl::data_param d
+ ) const
+ {
+ return d.get_data();
+ }
+ };
+ };
+
+ template<typename T>
+ struct rewrite_transform;
+
+ template<typename T>
+ struct rewrite_args
+ {
+ typedef T type;
+ };
+
+ // rewrite_object if T is not a template....
+ template<
+ typename T
+ BOOST_MPL_AUX_LAMBDA_ARITY_PARAM(long Arity = mpl::aux::template_arity<T>::value)
+ >
+ struct rewrite_object
+ : mpl::eval_if_c<
+ is_transform<T>::value
+ , rewrite_transform<T>
+ , mpl::identity<T>
+ >
+ {};
+
+ // rewrite_makeable if Object is not a function type (or pointer to function type)
+ template<typename Object>
+ struct rewrite_makeable
+ {
+ typedef proto::make<typename rewrite_object<Object>::type> type;
+ };
+
+ // rewrite_callable if T is a PrimitiveTransform or a function of arity >2
+ template<typename T>
+ struct rewrite_callable
+ {
+ typedef proto::call<T> type;
+ };
+
+ template<typename Sig>
+ struct rewrite_callable<Sig *>
+ : rewrite_callable<Sig>
+ {};
+
+ template<typename R>
+ struct rewrite_callable<R()>
+ : mpl::if_c<
+ is_transform<R>::value && !is_let<R>::value
+ , proto::call<R(_, _state, _get_data)>
+ , proto::call<R()>
+ >
+ {};
+
+ template<typename R, typename A0>
+ struct rewrite_callable<R(A0)>
+ : mpl::if_c<
+ is_transform<R>::value && !is_let<R>::value
+ , proto::call<R(A0, _state, _get_data)>
+ , proto::call<R(A0)>
+ >
+ {};
+
+ template<typename R, typename A0, typename A1>
+ struct rewrite_callable<R(A0, A1)>
+ : mpl::if_c<
+ is_transform<R>::value && !is_let<R>::value
+ , proto::call<R(A0, A1, _get_data)>
+ , proto::call<R(A0, A1)>
+ >
+ {};
+
+ // rewrite_lazyable if Object is not a function type (or pointer to function type)
+ template<typename Object>
+ struct rewrite_lazyable
+ {
+ typedef proto::lazy<typename rewrite_object<Object>::type> type;
+ };
+
+ // Why all the complexity and special handling for certain Proto transforms?
+ // Note: make<> is a transform, but we don't want to monkey with the data parameter yet
+ //
+ // let<_a, make<foo<_a> > > => make<foo<_a> >
+ // let<_a, make<foo<_a>(_data)> > => make<foo<_a>(call<_data(_, _state, _get_data))> >
+ //
+ // Here, we do want to monkey with the data parameter, so just checking if T<X> is a
+ // transform is insufficient.
+ //
+ // let<_a, MyTransform<X> > => call<MyTransform<X>(_, _state, _get_data)>
+
+ template<typename T>
+ struct rewrite_transform
+ : mpl::if_c<
+ is_let<T>::value
+ , T
+ , proto::call<T(_, _state, _get_data)>
+ >
+ {};
+
+ #define BOOST_PROTO_IGNORE_ARITY(I)
+
+ // Some Proto transforms need to be handled specially by the let rewritter rules
+ #define BOOST_PROTO_HANDLE_SPECIAL_TRANSFORMS(X, ARITY) \
+ template<typename T> \
+ struct X<proto::call<T> ARITY(1)> \
+ : rewrite_callable<typename rewrite_args<T>::type> \
+ {}; \
+ \
+ template<typename T> \
+ struct X<proto::make<T> ARITY(1)> \
+ : rewrite_makeable<typename rewrite_args<T>::type> \
+ {}; \
+ \
+ template<typename T> \
+ struct X<proto::lazy<T> ARITY(1)> \
+ : rewrite_lazyable<typename rewrite_args<T>::type> \
+ {}; \
+ \
+ template<typename Tag> \
+ struct X<local<Tag> ARITY(1)> \
+ { \
+ typedef local<Tag> type; \
+ }; \
+ /**/
+
+ // These must be kept in sync, hence the macros.
+ BOOST_PROTO_HANDLE_SPECIAL_TRANSFORMS(rewrite_transform, BOOST_PROTO_IGNORE_ARITY)
+ BOOST_PROTO_HANDLE_SPECIAL_TRANSFORMS(rewrite_object, BOOST_MPL_AUX_LAMBDA_ARITY_PARAM)
+
+ #undef BOOST_PROTO_IGNORE_ARITY
+ #undef BOOST_PROTO_HANDLE_SPECIAL_TRANSFORMS
+
+ #define BOOST_PP_ITERATION_PARAMS_1 (3, (0, BOOST_PROTO_MAX_ARITY, <boost/proto/transform/let.hpp>))
+ #include BOOST_PP_ITERATE()
+ }
+
+ template<typename Tag>
+ struct local : transform<local<Tag> >
+ {
+ // TODO put something here to give a sensible compile error
+ // when a local variable is used out of a let scope.
+ template<typename Expr, typename State, typename Data>
+ struct impl;
+
+ template<typename Expr, typename State, typename LocalMap, typename Data>
+ struct impl<Expr, State, detail::let_scope<LocalMap, Data> &>
+ : transform_impl<Expr, State, detail::let_scope<LocalMap, Data> &>
+ {
+ typedef
+ typename detail::let_scope<LocalMap, Data>::template at_key<local<Tag> >::type
+ result_type;
+
+ result_type operator()(
+ typename impl::expr_param
+ , typename impl::state_param
+ , typename impl::data_param d
+ ) const
+ {
+ return d.template get_local<local<Tag> >();
+ }
+ };
+ };
+
+ /// \brief A PrimitiveTransform that allows the result of one
+ /// computation to be saved off before invoking another transform
+ ///
+ /// Example:
+ ///
+ /// \code
+ /// struct RenumberFun
+ /// : proto::fold<
+ /// _
+ /// , make_pair(fusion::vector0<>(), proto::_state)
+ /// , let<
+ /// _a(Renumber(_, second(proto::_state)))
+ /// , make_pair(
+ /// push_back(first(proto::_state), first(_a))
+ /// , second(_a)
+ /// )
+ /// >
+ /// >
+ /// {};
+ /// \endcode
+ template<
+ BOOST_PP_ENUM_PARAMS(BOOST_PROTO_MAX_ARITY, typename Local)
+ , typename Transform
+ >
+ struct let
+ : transform<let<BOOST_PP_ENUM_PARAMS(BOOST_PROTO_MAX_ARITY, Local), Transform> >
+ {
+ template<typename Expr, typename State, typename Data>
+ struct impl : transform_impl<Expr, State, Data>
+ {
+ typedef
+ detail::init_local_map<
+ Expr, State, Data
+ BOOST_PP_ENUM_TRAILING_PARAMS(BOOST_PROTO_MAX_ARITY, Local)
+ , Transform
+ >
+ init_local_map;
+ typedef typename init_local_map::transform_type transform_type;
+ typedef typename init_local_map::local_map_type local_map_type;
+ typedef detail::let_scope<local_map_type, Data> let_scope;
+ typedef typename transform_type::template impl<Expr, State, let_scope &>::result_type result_type;
+
+ result_type operator()(
+ typename impl::expr_param e
+ , typename impl::state_param s
+ , typename impl::data_param d
+ ) const
+ {
+ let_scope scope = {init_local_map::call(e, s, d), d};
+ return typename transform_type::template impl<Expr, State, let_scope &>()(e, s, scope);
+ }
+ };
+ };
+
+ /// INTERNAL ONLY
+ ///
+ template<BOOST_PP_ENUM_PARAMS(BOOST_PROTO_MAX_ARITY, typename Local), typename Transform>
+ struct is_callable<let<BOOST_PP_ENUM_PARAMS(BOOST_PROTO_MAX_ARITY, Local), Transform> >
+ : mpl::true_
+ {};
+
+ /// INTERNAL ONLY
+ ///
+ template<typename Tag>
+ struct is_callable<local<Tag> >
+ : mpl::true_
+ {};
+
+ }}
+
+ #endif
+
+#else
+
+ #define N BOOST_PP_ITERATION()
+
+ // rewrite_args
+ template<typename R BOOST_PP_ENUM_TRAILING_PARAMS(N, typename A)>
+ struct rewrite_args<R(BOOST_PP_ENUM_PARAMS(N, A))>
+ {
+ typedef R type(BOOST_PP_ENUM_BINARY_PARAMS(N, typename rewrite_transform<A, >::type BOOST_PP_INTERCEPT));
+ };
+
+ template<typename R BOOST_PP_ENUM_TRAILING_PARAMS(N, typename A)>
+ struct rewrite_args<R(*)(BOOST_PP_ENUM_PARAMS(N, A))>
+ : rewrite_args<R(BOOST_PP_ENUM_PARAMS(N, A))>
+ {};
+
+ #if N > 0
+ // rewrite_object
+ template<
+ template<BOOST_PP_ENUM_PARAMS(N, typename BOOST_PP_INTERCEPT)> class T
+ BOOST_PP_ENUM_TRAILING_PARAMS(N, typename A)
+ >
+ struct rewrite_object<T<BOOST_PP_ENUM_PARAMS(N, A)> BOOST_MPL_AUX_LAMBDA_ARITY_PARAM(N)>
+ {
+ typedef
+ T<BOOST_PP_ENUM_BINARY_PARAMS(N, typename rewrite_object<A, >::type BOOST_PP_INTERCEPT)>
+ type;
+ };
+
+ #define M0(Z, N, DATA) \
+ BOOST_PP_CAT(Local, N)(BOOST_PP_CAT(Init, N)) \
+ /**/
+
+ #define M1(Z, N, DATA) \
+ typedef \
+ typename when<_, BOOST_PP_CAT(Init, N)>::template impl<Expr, State, Data> \
+ BOOST_PP_CAT(fun_type, N); \
+ typedef \
+ fusion::pair< \
+ BOOST_PP_CAT(Local, N) \
+ , typename BOOST_PP_CAT(fun_type, N)::result_type \
+ > \
+ BOOST_PP_CAT(value_type, N); \
+ /**/
+
+ #define M2(Z, N, DATA) \
+ BOOST_PP_CAT(value_type, N)(BOOST_PP_CAT(fun_type, N)()(e, s, d)) \
+ /**/
+
+ template<typename Expr, typename State, typename Data
+ BOOST_PP_ENUM_TRAILING_PARAMS(N, typename Local)
+ BOOST_PP_ENUM_TRAILING_PARAMS(N, typename Init)
+ , typename Transform
+ >
+ struct init_local_map<Expr, State, Data
+ BOOST_PP_ENUM_TRAILING(N, M0, ~)
+ , Transform
+ > : transform_impl<Expr, State, Data>
+ {
+ // Rewrite the transform to make our monkeying with the data parameter
+ // transparent and to properly scope the local variables.
+ typedef typename detail::rewrite_transform<Transform>::type transform_type;
+ BOOST_PP_REPEAT(N, M1, ~)
+ typedef fusion::map<BOOST_PP_ENUM_PARAMS(N, value_type)> local_map_type;
+
+ static local_map_type call(
+ typename init_local_map::expr_param e
+ , typename init_local_map::state_param s
+ , typename init_local_map::data_param d
+ )
+ {
+ return local_map_type(BOOST_PP_ENUM(N, M2, ~));
+ }
+ };
+
+ #undef M2
+ #undef M1
+ #undef M0
+
+ #endif
+
+ // rewrite_makeable
+ template<typename R BOOST_PP_ENUM_TRAILING_PARAMS(N, typename A)>
+ struct rewrite_makeable<R(BOOST_PP_ENUM_PARAMS(N, A))>
+ {
+ typedef
+ proto::make<typename rewrite_object<R>::type(BOOST_PP_ENUM_PARAMS(N, A))>
+ type;
+ };
+
+ template<typename Object BOOST_PP_ENUM_TRAILING_PARAMS(N, typename A)>
+ struct rewrite_makeable<Object(*)(BOOST_PP_ENUM_PARAMS(N, A))>
+ : rewrite_makeable<Object(BOOST_PP_ENUM_PARAMS(N, A))>
+ {};
+
+ // rewrite_lazyable
+ template<typename R BOOST_PP_ENUM_TRAILING_PARAMS(N, typename A) >
+ struct rewrite_lazyable<R(BOOST_PP_ENUM_PARAMS(N, A))>
+ {
+ typedef
+ proto::lazy<typename rewrite_object<R>::type(BOOST_PP_ENUM_PARAMS(N, A))>
+ type;
+ };
+
+ // rewrite_lazyable
+ template<typename R BOOST_PP_ENUM_TRAILING_PARAMS(N, typename A) >
+ struct rewrite_lazyable<R(*)(BOOST_PP_ENUM_PARAMS(N, A))>
+ : rewrite_lazyable<R(BOOST_PP_ENUM_PARAMS(N, A))>
+ {};
+
+ // rewrite_transform
+ template<typename R BOOST_PP_ENUM_TRAILING_PARAMS(N, typename A) >
+ struct rewrite_transform<R(BOOST_PP_ENUM_PARAMS(N, A))>
+ {
+ typedef typename rewrite_args<R(BOOST_PP_ENUM_PARAMS(N, A))>::type fun_type;
+ typedef
+ typename mpl::eval_if_c<
+ is_callable<R>::value
+ , rewrite_callable<fun_type>
+ , rewrite_makeable<fun_type>
+ >::type
+ type;
+ };
+
+ template<typename R BOOST_PP_ENUM_TRAILING_PARAMS(N, typename A) >
+ struct rewrite_transform<R(*)(BOOST_PP_ENUM_PARAMS(N, A))>
+ : rewrite_transform<R(BOOST_PP_ENUM_PARAMS(N, A))>
+ {};
+
+ #undef N
+
+#endif

Modified: trunk/libs/proto/doc/reference.xml
==============================================================================
--- trunk/libs/proto/doc/reference.xml (original)
+++ trunk/libs/proto/doc/reference.xml 2011-01-29 07:38:37 EST (Sat, 29 Jan 2011)
@@ -467,6 +467,11 @@
       </listitem>
       <listitem>
         <computeroutput>
+ <classname alt="boost::proto::let">proto::let</classname>
+ </computeroutput>
+ </listitem>
+ <listitem>
+ <computeroutput>
           <classname alt="boost::proto::listN">proto::list1&lt;&gt;, proto::list2&lt;&gt;, ...</classname>
         </computeroutput>
       </listitem>
@@ -477,6 +482,11 @@
       </listitem>
       <listitem>
         <computeroutput>
+ <classname alt="boost::proto::local">proto::local</classname>
+ </computeroutput>
+ </listitem>
+ <listitem>
+ <computeroutput>
           <classname alt="boost::proto::logical_and">proto::logical_and</classname>
         </computeroutput>
       </listitem>
@@ -1137,6 +1147,7 @@
   <xi:include href="reference/transform/fold_tree.xml"/>
   <xi:include href="reference/transform/impl.xml"/>
   <xi:include href="reference/transform/lazy.xml"/>
+ <xi:include href="reference/transform/let.xml"/>
   <xi:include href="reference/transform/make.xml"/>
   <xi:include href="reference/transform/pass_through.xml"/>
   <xi:include href="reference/transform/when.xml"/>

Added: trunk/libs/proto/doc/reference/transform.xml
==============================================================================
--- (empty file)
+++ trunk/libs/proto/doc/reference/transform.xml 2011-01-29 07:38:37 EST (Sat, 29 Jan 2011)
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<header name="boost/proto/transform.hpp">
+ <para>Includes all the built-in transforms of Proto.</para>
+</header>

Added: trunk/libs/proto/doc/reference/transform/let.xml
==============================================================================
--- (empty file)
+++ trunk/libs/proto/doc/reference/transform/let.xml 2011-01-29 07:38:37 EST (Sat, 29 Jan 2011)
@@ -0,0 +1,178 @@
+<?xml version="1.0" encoding="utf-8"?>
+<header name="boost/proto/transform/let.hpp">
+ <para>
+ Contains definition of the
+ <computeroutput><classname alt="boost::proto::let">proto::let&lt;&gt;</classname></computeroutput>
+ transform and the
+ <computeroutput><classname alt="boost::proto::local">proto::local&lt;&gt;</classname></computeroutput>
+ placeholder.
+ </para>
+ <namespace name="boost">
+ <namespace name="proto">
+ <struct name="local">
+ <template>
+ <template-type-parameter name="T"/>
+ </template>
+ <purpose>
+ A placeholder for a local variable in a
+ <computeroutput><classname>proto::let</classname>&lt;&gt;</computeroutput>
+ transform.
+ </purpose>
+ </struct>
+
+ <struct name="let">
+ <template>
+ <template-type-parameter name="T" pack="1"/>
+ </template>
+
+ <inherit><classname>proto::transform</classname>&lt; let&lt;T...&gt; &gt;</inherit>
+
+ <purpose>A <conceptname>PrimitiveTransform</conceptname> that has the effect of saving
+ the results of transforms in local variables so they can be referred to from other
+ transforms.</purpose>
+
+ <description>
+ <para>
+ The purpose of <computeroutput>proto::let&lt;&gt;</computeroutput> is to make it
+ possible to save the results of transforms in local variables so they can be referred
+ to from other transforms.
+ </para>
+
+ <para>
+ Take as an example a transform that computes a <computeroutput>std::pair&lt;&gt;</computeroutput>
+ and saves the result into a temporary object. Then, it increments the value stored in
+ <computeroutput>second</computeroutput> and returns a new pair. It might look like this:
+ </para>
+
+ <para>
+ <programlisting>using proto::_a;
+using proto::functional::make_pair;
+using proto::functional::first;
+using proto::functional::second;
+
+struct inc_second
+ : proto::let&lt;
+ // make a pair and initialize _a with it.
+ _a(make_pair(proto::_value, proto::_state))
+ // use _a to make a new pair with the first element
+ // of _a and the incremented second element.
+ , make_pair(first(_a), mpl::next&lt;second(_a)&gt;())
+ &gt;
+{};
+
+int main()
+{
+ proto::literal&lt;char const *&gt; hello("hello");
+ std::pair&lt;char const *, mpl::int_&lt;1&gt; &gt; p =
+ inc_second()(hello, mpl::int_&lt;0&gt;());
+}</programlisting>
+ </para>
+
+ <para>
+ <computeroutput>proto::let&lt;&gt;</computeroutput> requires that the first
+ (N-1) template parameters are of the form <computeroutput><replaceable>_x</replaceable>(<conceptname>Transform</conceptname>)</computeroutput>,
+ where <computeroutput><replaceable>_x</replaceable></computeroutput> is an instance of
+ <computeroutput><classname>proto::local</classname>&lt;&gt;</computeroutput>
+ like <computeroutput>proto::_a</computeroutput>. Also, the <replaceable>N</replaceable>th
+ parameter must be a <conceptname>Transform</conceptname>.
+ </para>
+
+ <para>
+ For the full description of the behavior of the
+ <computeroutput>proto::let&lt;&gt;</computeroutput>
+ transform, see the documentation for the nested
+ <computeroutput><classname alt="proto::let::impl">proto::let::impl&lt;&gt;</classname></computeroutput>
+ class template.
+ </para>
+ </description>
+
+ <struct name="impl">
+ <template>
+ <template-type-parameter name="Expr"/>
+ <template-type-parameter name="State"/>
+ <template-type-parameter name="Data"/>
+ </template>
+ <inherit><classname>proto::transform_impl</classname>&lt; Expr, State, Data &gt;</inherit>
+ <typedef name="result_type">
+ <type><emphasis>see-below</emphasis></type>
+ <description>
+ <para>
+ <computeroutput><classname>proto::let</classname>&lt;L<subscript>0</subscript>(I<subscript>0</subscript>),...L<subscript>n</subscript>(I<subscript>n</subscript>), T&gt;::impl&lt;Expr, State, Data&gt;::result_type</computeroutput> is
+ computed as follows:
+ </para>
+ <para>
+ Let <computeroutput>T<superscript>'</superscript></computeroutput> be a new transform such that
+ every <computeroutput>L<subscript>i</subscript></computeroutput> in <computeroutput>T</computeroutput>
+ is lexically replaced with
+ <computeroutput><classname>proto::when</classname>&lt;<classname>_</classname>, I<subscript>i</subscript>&gt;</computeroutput>,
+ with the exception noted below.
+ </para>
+ <para>
+ In the case of nested <computeroutput><classname>proto::let</classname>&lt;&gt;</computeroutput> transforms,
+ lexical substitution proceeds inside-out; that is, substitution of the innermost <computeroutput>let&lt;&gt;</computeroutput>
+ is done first. This is to ensure that local variables are found in the innermost lexical scope in which they appear.
+ </para>
+ <para>
+ <computeroutput>result_type</computeroutput> is
+ <computeroutput><classname>proto::when</classname>&lt;<classname>_</classname>,
+ T<superscript>'</superscript>&gt;::impl&lt;Expr, State, Data&gt;::result_type</computeroutput>.
+ </para>
+ <para>
+ Note: local variables have no visibility outside of the transform <computeroutput>T</computeroutput>.
+ That is, one local variable cannot participate in the initialization of another in the same
+ <computeroutput>let&lt;&gt;</computeroutput> transform.
+ </para>
+ </description>
+ </typedef>
+ <method-group name="public member functions">
+ <method name="operator()" cv="const">
+ <type>result_type</type>
+ <parameter name="expr">
+ <paramtype>typename impl::expr_param</paramtype>
+ </parameter>
+ <parameter name="state">
+ <paramtype>typename impl::state_param</paramtype>
+ </parameter>
+ <parameter name="data">
+ <paramtype>typename impl::data_param</paramtype>
+ </parameter>
+ <description>
+ <para>
+ <computeroutput><classname>proto::let</classname>&lt;L<subscript>0</subscript>(I<subscript>0</subscript>),...L<subscript>n</subscript>(I<subscript>n</subscript>), T&gt;::impl&lt;Expr,State,Data&gt;::operator()</computeroutput>
+ behaves as follows:
+ </para>
+ <para>
+ For each <computeroutput>i</computeroutput> in <computeroutput>[0,n]</computeroutput>, let
+ <computeroutput>X<subscript>i</subscript></computeroutput> be a hidden temporary object initialized with
+ <computeroutput><classname>proto::when</classname>&lt;<classname>_</classname>, I<subscript>i</subscript>&gt;::impl&lt;Expr, State, Data&gt;()(expr, state, data)</computeroutput>.
+ The temporary objects <computeroutput>X<subscript>i</subscript></computeroutput> belong to the
+ lexical scope of the current <computeroutput>let&lt;&gt;</computeroutput> transform. (Nested
+ <computeroutput>let&lt;&gt;</computeroutput> transforms have their own lexical scopes with
+ their own hidden temporary objects.)
+ </para>
+ <para>
+ For each <computeroutput>i</computeroutput> in <computeroutput>[0,n]</computeroutput>,
+ define a <conceptname>PrimitiveTransform</conceptname> <computeroutput>GetLocal<subscript>i</subscript></computeroutput>
+ that returns the nearest lexically scoped hidden temporary object <computeroutput>X<subscript>i</subscript></computeroutput>.
+ </para>
+ <para>
+ Let <computeroutput>T<superscript>'</superscript></computeroutput> be a new transform such that
+ every <computeroutput>L<subscript>i</subscript></computeroutput> in <computeroutput>T</computeroutput>
+ is lexically replaced with <computeroutput>GetLocal<subscript>i</subscript></computeroutput>.
+ </para>
+ <para>
+ Note: local variables have no visibility outside of the transform <computeroutput>T</computeroutput>.
+ That is, one local variable cannot participate in the initialization of another in the same
+ <computeroutput>let&lt;&gt;</computeroutput> transform.
+ </para>
+ </description>
+ <returns><computeroutput><classname>proto::when</classname>&lt;<classname>_</classname>,
+ T<superscript>'</superscript>&gt;::impl&lt;Expr, State, Data&gt;()(expr, state, data)</computeroutput>
+ </returns>
+ </method>
+ </method-group>
+ </struct>
+ </struct>
+ </namespace>
+ </namespace>
+</header>

Modified: trunk/libs/proto/test/Jamfile.v2
==============================================================================
--- trunk/libs/proto/test/Jamfile.v2 (original)
+++ trunk/libs/proto/test/Jamfile.v2 2011-01-29 07:38:37 EST (Sat, 29 Jan 2011)
@@ -27,6 +27,7 @@
         [ run deduce_domain.cpp ]
         [ run examples.cpp ]
         [ run lambda.cpp ]
+ [ run let.cpp ]
         [ run make_expr.cpp ]
         [ run matches.cpp ]
         [ run proto_fusion.cpp : : : <toolset>gcc:<cxxflags>-ftemplate-depth-1024 ]

Added: trunk/libs/proto/test/let.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/proto/test/let.cpp 2011-01-29 07:38:37 EST (Sat, 29 Jan 2011)
@@ -0,0 +1,213 @@
+///////////////////////////////////////////////////////////////////////////////
+// let.cpp
+//
+// Copyright 2010 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 <string>
+#include <boost/proto/proto.hpp>
+#include <boost/test/unit_test.hpp>
+
+namespace mpl = boost::mpl;
+namespace proto = boost::proto;
+using proto::_;
+using proto::_a;
+using proto::_b;
+
+///////////////////////////////////////////////////////////////////////////////
+// test_let_call_once
+// verify that the transform bound to the local variable is really called
+// only once.
+struct once
+{
+ static int ctors;
+ once() { ++ctors; }
+};
+
+int once::ctors = 0;
+
+struct LetCallOnce
+ : proto::otherwise<
+ proto::let<
+ _a(once())
+ , proto::functional::make_pair(_a, _a)
+ >
+ >
+{};
+
+void test_let_call_once()
+{
+ proto::literal<int> i(0);
+ std::pair<once, once> p = LetCallOnce()(i);
+ BOOST_CHECK_EQUAL(1, once::ctors);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// test_let_object_transforms
+// verify that let works with object transforms.
+struct LetObjectTransforms
+ : proto::otherwise<
+ proto::let<
+ _a(int())
+ , std::pair<_a, _a>(_a, _a)
+ >
+ >
+{};
+
+struct LetObjectTransforms2
+ : proto::otherwise<
+ proto::let<
+ _a(int())
+ , proto::make<std::pair<_a, _a>(_a, _a)>
+ >
+ >
+{};
+
+void test_let_object_transforms()
+{
+ proto::literal<int> i(0);
+ std::pair<int, int> p = LetObjectTransforms()(i);
+ BOOST_CHECK_EQUAL(0, p.first);
+ BOOST_CHECK_EQUAL(0, p.second);
+
+ std::pair<int, int> p2 = LetObjectTransforms2()(i);
+ BOOST_CHECK_EQUAL(0, p2.first);
+ BOOST_CHECK_EQUAL(0, p2.second);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// test_let_data
+// verify that the monkeying with the data parameter is transparent.
+struct MyData : proto::_data {};
+
+struct LetData
+ : proto::otherwise<
+ proto::let<
+ _a(int())
+ , std::pair<MyData, _a>(MyData, _a)
+ >
+ >
+{};
+
+void test_let_data()
+{
+ std::string hello("hello");
+ proto::literal<int> i(0);
+ std::pair<std::string, int> p = LetData()(i, 0, hello);
+ BOOST_CHECK_EQUAL(hello, p.first);
+ BOOST_CHECK_EQUAL(0, p.second);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// test_let_scope
+// verify that the local variables are scoped properly.
+struct LetScope
+ : proto::otherwise<
+ proto::let<
+ _a(proto::_value)
+ , proto::functional::make_pair(
+ _a
+ , proto::let<
+ _a(proto::_state)
+ , proto::functional::make_pair(_a, proto::_data)
+ >
+ )
+ >
+ >
+{};
+
+void test_let_scope()
+{
+ std::string hello("hello");
+ proto::literal<short> i((short)42);
+ std::pair<short, std::pair<float, std::string> > p = LetScope()(i, 3.14f, hello);
+ BOOST_CHECK_EQUAL(42, p.first);
+ BOOST_CHECK_EQUAL(3.14f, p.second.first);
+ BOOST_CHECK_EQUAL(hello, p.second.second);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// test_let_scope2
+// verify that the local variables are scoped properly.
+struct LetScope2
+ : proto::otherwise<
+ proto::let<
+ _a(proto::_value)
+ , proto::functional::make_pair(
+ _a
+ , proto::let<
+ _b(proto::_state)
+ , proto::functional::make_pair(_a, _b)
+ >
+ )
+ >
+ >
+{};
+
+struct LetScope3
+ : proto::otherwise<
+ proto::let<
+ _a(proto::_value)
+ , proto::functional::make_pair(
+ _a
+ , proto::let<
+ _b(proto::_state)
+ , proto::call<proto::functional::make_pair(_a, _b)>
+ >
+ )
+ >
+ >
+{};
+
+struct LetScope4
+ : proto::otherwise<
+ proto::let<
+ _a(proto::_value)
+ , proto::functional::make_pair(
+ _a
+ , proto::let<
+ _b(proto::_state)
+ , proto::call<proto::functional::make_pair>(_a, _b)
+ >
+ )
+ >
+ >
+{};
+
+void test_let_scope2()
+{
+ char const * sz = "";
+ proto::literal<short> i((short)42);
+ std::pair<short, std::pair<short, float> > p2 = LetScope2()(i, 3.14f, sz);
+ BOOST_CHECK_EQUAL(42, p2.first);
+ BOOST_CHECK_EQUAL(42, p2.second.first);
+ BOOST_CHECK_EQUAL(3.14f, p2.second.second);
+
+ std::pair<short, std::pair<short, float> > p3 = LetScope3()(i, 3.14f, sz);
+ BOOST_CHECK_EQUAL(42, p3.first);
+ BOOST_CHECK_EQUAL(42, p3.second.first);
+ BOOST_CHECK_EQUAL(3.14f, p3.second.second);
+
+ std::pair<short, std::pair<short, float> > p4 = LetScope4()(i, 3.14f, sz);
+ BOOST_CHECK_EQUAL(42, p4.first);
+ BOOST_CHECK_EQUAL(42, p4.second.first);
+ BOOST_CHECK_EQUAL(3.14f, p4.second.second);
+}
+
+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 let transform");
+
+ test->add(BOOST_TEST_CASE(&test_let_call_once));
+ test->add(BOOST_TEST_CASE(&test_let_object_transforms));
+ test->add(BOOST_TEST_CASE(&test_let_data));
+ test->add(BOOST_TEST_CASE(&test_let_scope));
+ test->add(BOOST_TEST_CASE(&test_let_scope2));
+
+ return test;
+}


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