On 11/18/2010 3:31 PM, Eric Niebler wrote:
> On 11/18/2010 1:45 PM, Thomas Heller wrote:
>> Eric Niebler <e...@...> writes:
>>> It's REALLY hard. The let context needs to be bundled with the Expr,
>>> State, or Data parameters somehow, but in a way that's transparent. I
>>> don't actually know if it's possible.
>>
>> Very hard ... yeah. I am thinking that we can maybe save these variables in 
>> the 
>> transform?
> 
> I'm thinking we just stuff it into the Data parameter. We have a
> let_scope template that is effectively a pair containing:
> 
> 1. The user's original Data, and
> 2. A Fusion map from local variables (_a) to values.
> 
> The let transform evaluates the bindings and stores the result in the
> let_scope's Fusion map alongside the user's Data. We pass the let_scope
> as the new Data parameter. _a is itself a transform that looks up the
> value in Data's Fusion map. The proto::_data transform is changed to be
> aware of let_scope and return only the original user's Data. This can
> work. We also need to be sure not to break the new
> proto::external_transform.
> 
> The problems with this approach as I see it:
> 
> 1. It's not completely transparent. Custom primitive transforms will see
> that the Data parameter has been monkeyed with.
> 
> 2. Local variables like _a are not lexically scoped. They are, in fact,
> dynamically scoped. That is, you can access _a outside of a let<>
> clause, as long as you've been called from within a let clause.
> 
> Might be worth it. But as there's no pressing need, I'm content to let
> this simmer. Maybe we can think of something better.

I played with the let transform idea over the weekend. It *may* be
possible to accomplish without the two problems I described above. See
the attached let transform (needs latest Proto trunk). I'm also
attaching the Renumber example, reworked to use let.

This code is NOT ready for prime time. I'm not convinced it behaves
sensibly in all cases. I'm only posting it as a curiosity. You're insane
if you use this in production code. Etc, etc.

-- 
Eric Niebler
BoostPro Computing
http://www.boostpro.com
#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/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/mpl/if.hpp>
    #include <boost/mpl/eval_if.hpp>
    //#include <boost/mpl/print.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
    {
        // Fwd declarations to be moved to proto_fwd.hpp
        template<
            BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT(BOOST_PROTO_MAX_ARITY, typename 
Local, void)
          , typename Transform = void
        >
        struct let;

        template<typename Tag>
        struct local;

        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
            {
                typedef LocalMap local_map_type;
                typedef Data data_type;

                let_scope(LocalMap l, Data d)
                  : locals(l)
                  , data(d)
                {}

                LocalMap locals;
                Data data;

            private:
                let_scope &operator=(let_scope const &);
            };

            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 Data result_type;

                    Data operator()(
                        typename impl::expr_param
                      , typename impl::state_param
                      , typename impl::data_param d
                    ) const
                    {
                        return d.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
                  , 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
                  , 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
                  , 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;
            };

#if 0
            // 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)>
#endif

            template<typename T>
            struct rewrite_transform
            {
                typedef proto::call<T(_, _state, _get_data)> type;
            };

        #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 fusion::result_of::at_key<LocalMap, local<Tag> 
>::type result_type;

                result_type operator()(
                    typename impl::expr_param
                  , typename impl::state_param
                  , typename impl::data_param d
                ) const
                {
                    return fusion::at_key<local<Tag> >(d.locals);
                }
            };
        };

        // 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;

        /// \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_PARAMS(BOOST_PROTO_MAX_ARITY, Local)
                      , Transform
                    >
                init_local_map;
                typedef
                    typename detail::rewrite_transform<typename 
init_local_map::transform_type>::type
                transform_type;
                typedef typename init_local_map::local_map_type local_map_type;
                //typedef typename mpl::print<transform_type>::type p1;
                typedef detail::let_scope<local_map_type, Data> data_type;
                typedef typename transform_type::template impl<Expr, State, 
data_type &>::result_type result_type;

                result_type operator()(
                    typename impl::expr_param e
                  , typename impl::state_param s
                  , typename impl::data_param d
                ) const
                {
                    data_type data(init_local_map::call(e, s, d), d);
                    return typename transform_type::template impl<Expr, State, 
data_type &>()(e, s, data);
                }
            };
        };

        /// INTERNAL ONLY
        ///
        template<typename Local0, typename Transform>
        struct is_callable<let<Local0, 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>
        {
            typedef Transform 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
//#include "stdafx.h"
#include <boost/proto/proto.hpp>
#include <boost/proto/transform/let.hpp>

#include <cassert>
#include <utility>
#include <iostream>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/include/push_front.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/next.hpp>

namespace mpl = boost::mpl;
namespace proto = boost::proto;
namespace fusion = boost::fusion;
using proto::_;
using proto::_a;

struct term
{
    friend std::ostream &operator<<(std::ostream &sout, term const &)
    {
        return sout << "term";
    }
};

template<typename I>
struct newterm
{
    friend std::ostream &operator<<(std::ostream &sout, newterm<I> const &)
    {
        return sout << "newterm<" << I::value << ">";
    }
};

proto::terminal<term>::type const a = {{}};
proto::terminal<term>::type const b = {{}};
proto::terminal<term>::type const c = {{}};

using proto::functional::make_pair;
using proto::functional::first;
using proto::functional::second;
using proto::functional::push_back;
using proto::functional::unpack_expr;

#if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1600))
#define _make_terminal(x) call<proto::_make_terminal(x) >
#define Renumber(x,y) proto::call<Renumber(x,y) >
#define push_back(x,y) proto::call<push_back(x,y) >
#define make_pair(x,y) proto::call<make_pair(x,y) >
#define unpack_expr(x,y) proto::call<unpack_expr(x,y) >
#define first(x) proto::call<first(x) >
#define second(x) proto::call<second(x) >
#endif

struct Renumber;

struct RenumberFun
  : proto::fold<
        _
      , make_pair(fusion::vector0<>(), proto::_state)
      , proto::let<
            _a(Renumber(_, second(proto::_state)))
          , make_pair(
                push_back(first(proto::_state), first(_a))
              , second(_a)
            )
        >
    >
{};

struct Renumber
  : proto::or_<
        proto::when<
            proto::terminal<term>
          , make_pair(
                proto::_make_terminal(newterm<proto::_state>())
              , mpl::next<proto::_state>()
            )
        >
      , proto::when<
            proto::terminal<_>
          , make_pair(proto::_byval(_), proto::_state)
        >
      , proto::otherwise<
            proto::let<
                _a(RenumberFun)
              , make_pair(
                    proto::lazy<unpack_expr<proto::tag_of<_> >(first(_a))>
                  , second(_a)
                )
            >
        >
    >
{};

#if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1600))
#undef _make_terminal
#undef Renumber
#undef push_back
#undef make_pair
#undef unpack_expr
#undef first
#undef second
#endif

int main()
{
    proto::display_expr(((a * a) + 1) + (b - c));
    proto::display_expr(Renumber()(((a * a) + 1) + (b - c), 
mpl::int_<42>()).first);
}
_______________________________________________
proto mailing list
proto@lists.boost.org
http://lists.boost.org/mailman/listinfo.cgi/proto

Reply via email to