#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 #include #include #include #include #include #include #include #include #include #include #include #include #include //#include #include #include #include #include #include #include #include #include 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 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 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 struct init_local_map; // A transnform that fetches the original data parameter out of a let_scope struct _get_data : transform<_get_data> { template struct impl; template struct impl &> : transform_impl &> { typedef Data result_type; Data operator()( typename impl::expr_param , typename impl::state_param , typename impl::data_param d ) const { return d.data; } }; }; template struct rewrite_transform; template 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::value) > struct rewrite_object : mpl::eval_if_c< is_transform::value , rewrite_transform , mpl::identity > {}; // rewrite_makeable if Object is not a function type (or pointer to function type) template struct rewrite_makeable { typedef proto::make::type> type; }; // rewrite_callable if T is a PrimitiveTransform or a function of arity >2 template struct rewrite_callable { typedef proto::call type; }; template struct rewrite_callable : rewrite_callable {}; template struct rewrite_callable : mpl::if_c< is_transform::value , proto::call , proto::call > {}; template struct rewrite_callable : mpl::if_c< is_transform::value , proto::call , proto::call > {}; template struct rewrite_callable : mpl::if_c< is_transform::value , proto::call , proto::call > {}; // rewrite_lazyable if Object is not a function type (or pointer to function type) template struct rewrite_lazyable { typedef proto::lazy::type> type; }; #if 0 // Note: make<> is a transform, but we don't want to monkey with the data parameter yet let<_a, make > > => make > let<_a, make(_data)> > => make(call<_data(_, _state, _get_data))> > // Here, we do want to monkey with the data parameter, so just checking if T is a // transform is insufficient. let<_a, MyTransform > => call(_, _state, _get_data)> #endif template struct rewrite_transform { typedef proto::call 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 \ struct X ARITY(1)> \ : rewrite_callable::type> \ {}; \ \ template \ struct X ARITY(1)> \ : rewrite_makeable::type> \ {}; \ \ template \ struct X ARITY(1)> \ : rewrite_lazyable::type> \ {}; \ \ template \ struct X ARITY(1)> \ { \ typedef local 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, )) #include BOOST_PP_ITERATE() } template struct local : transform > { // TODO put something here to give a sensible compile error // when a local variable is used out of a let scope. template struct impl; template struct impl &> : transform_impl &> { typedef typename fusion::result_of::at_key >::type result_type; result_type operator()( typename impl::expr_param , typename impl::state_param , typename impl::data_param d ) const { return fusion::at_key >(d.locals); } }; }; // Define some placeholders to be used to define local variables typedef local _a; typedef local _b; typedef local _c; typedef local _d; typedef local _e; typedef local _f; typedef local _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 > { template struct impl : transform_impl { 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::type transform_type; typedef typename init_local_map::local_map_type local_map_type; //typedef typename mpl::print::type p1; typedef detail::let_scope data_type; typedef typename transform_type::template impl::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()(e, s, data); } }; }; /// INTERNAL ONLY /// template struct is_callable > : mpl::true_ {}; /// INTERNAL ONLY /// template struct is_callable > : mpl::true_ {}; }} #endif #else #define N BOOST_PP_ITERATION() // rewrite_args template struct rewrite_args { typedef R type(BOOST_PP_ENUM_BINARY_PARAMS(N, typename rewrite_transform::type BOOST_PP_INTERCEPT)); }; template struct rewrite_args : rewrite_args {}; #if N > 0 // rewrite_object template< template class T BOOST_PP_ENUM_TRAILING_PARAMS(N, typename A) > struct rewrite_object BOOST_MPL_AUX_LAMBDA_ARITY_PARAM(N)> { typedef T::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 \ 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 struct init_local_map : transform_impl { typedef Transform transform_type; BOOST_PP_REPEAT(N, M1, ~) typedef fusion::map 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 struct rewrite_makeable { typedef proto::make::type(BOOST_PP_ENUM_PARAMS(N, A))> type; }; template struct rewrite_makeable : rewrite_makeable {}; // rewrite_lazyable template struct rewrite_lazyable { typedef proto::lazy::type(BOOST_PP_ENUM_PARAMS(N, A))> type; }; // rewrite_lazyable template struct rewrite_lazyable : rewrite_lazyable {}; // rewrite_transform template struct rewrite_transform { typedef typename rewrite_args::type fun_type; typedef typename mpl::eval_if_c< is_callable::value , rewrite_callable , rewrite_makeable >::type type; }; template struct rewrite_transform : rewrite_transform {}; #undef N #endif