1 /////////////////////////////////////////////////////////////////////////////// 2 /// \file when.hpp 3 /// Definition of when transform. 4 // 5 // Copyright 2008 Eric Niebler. Distributed under the Boost 6 // Software License, Version 1.0. (See accompanying file 7 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 8 9 #ifndef BOOST_PROTO_TRANSFORM_WHEN_HPP_EAN_10_29_2007 10 #define BOOST_PROTO_TRANSFORM_WHEN_HPP_EAN_10_29_2007 11 12 #include <boost/preprocessor/cat.hpp> 13 #include <boost/preprocessor/repetition/enum_params.hpp> 14 #include <boost/preprocessor/repetition/enum_trailing_params.hpp> 15 #include <boost/preprocessor/iteration/iterate.hpp> 16 #include <boost/mpl/at.hpp> 17 #include <boost/mpl/if.hpp> 18 #include <boost/mpl/map.hpp> 19 #include <boost/mpl/eval_if.hpp> 20 #include <boost/proto/proto_fwd.hpp> 21 #include <boost/proto/traits.hpp> 22 #include <boost/proto/transform/call.hpp> 23 #include <boost/proto/transform/make.hpp> 24 #include <boost/proto/transform/impl.hpp> 25 #include <boost/proto/transform/env.hpp> 26 27 #if defined(_MSC_VER) 28 # pragma warning(push) 29 # pragma warning(disable : 4714) // function 'xxx' marked as __forceinline not inlined 30 #endif 31 32 namespace boost { namespace proto 33 { 34 namespace detail 35 { 36 template<typename Grammar, typename R, typename Fun> 37 struct when_impl 38 : transform<when<Grammar, Fun> > 39 { 40 typedef Grammar first; 41 typedef Fun second; 42 typedef typename Grammar::proto_grammar proto_grammar; 43 44 // Note: do not evaluate is_callable<R> in this scope. 45 // R may be an incomplete type at this point. 46 47 template<typename Expr, typename State, typename Data> 48 struct impl : transform_impl<Expr, State, Data> 49 { 50 // OK to evaluate is_callable<R> here. R should be compete by now. 51 typedef 52 typename mpl::if_c< 53 is_callable<R>::value 54 , proto::call<Fun> // "R" is a function to call 55 , proto::make<Fun> // "R" is an object to construct 56 >::type 57 which; 58 59 typedef typename which::template impl<Expr, State, Data>::result_type result_type; 60 61 /// Evaluate <tt>R(A0,A1,...)</tt> as a transform either with 62 /// <tt>call\<\></tt> or with <tt>make\<\></tt> depending on 63 /// whether <tt>is_callable\<R\>::value</tt> is \c true or 64 /// \c false. 65 /// 66 /// \param e The current expression 67 /// \param s The current state 68 /// \param d An arbitrary data 69 /// \pre <tt>matches\<Expr, Grammar\>::value</tt> is \c true 70 /// \return <tt>which()(e, s, d)</tt> 71 BOOST_FORCEINLINE operator ()boost::proto::detail::when_impl::impl72 result_type operator ()( 73 typename impl::expr_param e 74 , typename impl::state_param s 75 , typename impl::data_param d 76 ) const 77 { 78 return typename which::template impl<Expr, State, Data>()(e, s, d); 79 } 80 }; 81 }; 82 } 83 84 /// \brief A grammar element and a PrimitiveTransform that associates 85 /// a transform with the grammar. 86 /// 87 /// Use <tt>when\<\></tt> to override a grammar's default transform 88 /// with a custom transform. It is for used when composing larger 89 /// transforms by associating smaller transforms with individual 90 /// rules in your grammar, as in the following transform which 91 /// counts the number of terminals in an expression. 92 /// 93 /// \code 94 /// // Count the terminals in an expression tree. 95 /// // Must be invoked with initial state == mpl::int_<0>(). 96 /// struct CountLeaves 97 /// : or_< 98 /// when<terminal<_>, mpl::next<_state>()> 99 /// , otherwise<fold<_, _state, CountLeaves> > 100 /// > 101 /// {}; 102 /// \endcode 103 /// 104 /// In <tt>when\<G, T\></tt>, when \c T is a class type it is a 105 /// PrimitiveTransform and the following equivalencies hold: 106 /// 107 /// <tt>boost::result_of\<when\<G,T\>(E,S,V)\>::type</tt> is the same as 108 /// <tt>boost::result_of\<T(E,S,V)\>::type</tt>. 109 /// 110 /// <tt>when\<G,T\>()(e,s,d)</tt> is the same as 111 /// <tt>T()(e,s,d)</tt>. 112 template<typename Grammar, typename PrimitiveTransform /*= Grammar*/> 113 struct when 114 : PrimitiveTransform 115 { 116 typedef Grammar first; 117 typedef PrimitiveTransform second; 118 typedef typename Grammar::proto_grammar proto_grammar; 119 }; 120 121 /// \brief A specialization that treats function pointer Transforms as 122 /// if they were function type Transforms. 123 /// 124 /// This specialization requires that \c Fun is actually a function type. 125 /// 126 /// This specialization is required for nested transforms such as 127 /// <tt>when\<G, T0(T1(_))\></tt>. In C++, functions that are used as 128 /// parameters to other functions automatically decay to funtion 129 /// pointer types. In other words, the type <tt>T0(T1(_))</tt> is 130 /// indistinguishable from <tt>T0(T1(*)(_))</tt>. This specialization 131 /// is required to handle these nested function pointer type transforms 132 /// properly. 133 template<typename Grammar, typename Fun> 134 struct when<Grammar, Fun *> 135 : when<Grammar, Fun> 136 {}; 137 138 /// \brief Syntactic sugar for <tt>when\<_, Fun\></tt>, for use 139 /// in grammars to handle all the cases not yet handled. 140 /// 141 /// Use <tt>otherwise\<T\></tt> in your grammars as a synonym for 142 /// <tt>when\<_, T\></tt> as in the following transform which 143 /// counts the number of terminals in an expression. 144 /// 145 /// \code 146 /// // Count the terminals in an expression tree. 147 /// // Must be invoked with initial state == mpl::int_<0>(). 148 /// struct CountLeaves 149 /// : or_< 150 /// when<terminal<_>, mpl::next<_state>()> 151 /// , otherwise<fold<_, _state, CountLeaves> > 152 /// > 153 /// {}; 154 /// \endcode 155 template<typename Fun> 156 struct otherwise 157 : when<_, Fun> 158 {}; 159 160 namespace envns_ 161 { 162 // Define the transforms global 163 BOOST_PROTO_DEFINE_ENV_VAR(transforms_type, transforms); 164 } 165 166 using envns_::transforms; 167 168 /// \brief This specialization uses the Data parameter as a collection 169 /// of transforms that can be indexed by the specified rule. 170 /// 171 /// Use <tt>when\<T, external_transform\></tt> in your code when you would like 172 /// to define a grammar once and use it to evaluate expressions with 173 /// many different sets of transforms. The transforms are found by 174 /// using the Data parameter as a map from rules to transforms. 175 /// 176 /// See \c action_map for an example. 177 template<typename Grammar> 178 struct when<Grammar, external_transform> 179 : proto::transform<when<Grammar, external_transform> > 180 { 181 typedef Grammar first; 182 typedef external_transform second; 183 typedef typename Grammar::proto_grammar proto_grammar; 184 185 template<typename Expr, typename State, typename Data> 186 struct impl 187 : remove_reference< 188 typename mpl::eval_if_c< 189 proto::result_of::has_env_var<Data, transforms_type>::value 190 , proto::result_of::env_var<Data, transforms_type> 191 , proto::result_of::env_var<Data, data_type> 192 >::type 193 >::type::template when<Grammar>::template impl<Expr, State, Data> 194 {}; 195 }; 196 197 /// \brief For defining a map of Rule/Transform pairs for use with 198 /// <tt>when\<T, external_transform\></tt> to make transforms external to the grammar 199 /// 200 /// The following code defines a grammar with a couple of external transforms. 201 /// It also defines an action_map that maps from rules to transforms. It then 202 /// passes that transforms map at the Data parameter to the grammar. In this way, 203 /// the behavior of the grammar can be modified post-hoc by passing a different 204 /// action_map. 205 /// 206 /// \code 207 /// struct int_terminal 208 /// : proto::terminal<int> 209 /// {}; 210 /// 211 /// struct char_terminal 212 /// : proto::terminal<char> 213 /// {}; 214 /// 215 /// struct my_grammar 216 /// : proto::or_< 217 /// proto::when< int_terminal, proto::external_transform > 218 /// , proto::when< char_terminal, proto::external_transform > 219 /// , proto::when< 220 /// proto::plus< my_grammar, my_grammar > 221 /// , proto::fold< _, int(), my_grammar > 222 /// > 223 /// > 224 /// {}; 225 /// 226 /// struct my_transforms 227 /// : proto::external_transforms< 228 /// proto::when<int_terminal, print(proto::_value)> 229 /// , proto::when<char_terminal, print(proto::_value)> 230 /// > 231 /// {}; 232 /// 233 /// proto::literal<int> i(1); 234 /// proto::literal<char> c('a'); 235 /// my_transforms trx; 236 /// 237 /// // Evaluate "i+c" using my_grammar with the specified transforms: 238 /// my_grammar()(i + c, 0, trx); 239 /// \endcode 240 template<BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT(BOOST_MPL_LIMIT_MAP_SIZE, typename T, mpl::na)> 241 struct external_transforms 242 { 243 typedef mpl::map<BOOST_PP_ENUM_PARAMS(BOOST_MPL_LIMIT_MAP_SIZE, T)> map_type; 244 245 template<typename Rule> 246 struct when 247 : proto::when<_, typename mpl::at<map_type, Rule>::type> 248 {}; 249 }; 250 251 // Other specializations of proto::when are generated by the preprocessor... 252 #include <boost/proto/transform/detail/when.hpp> 253 254 /// INTERNAL ONLY 255 /// 256 template<typename Grammar, typename Transform> 257 struct is_callable<when<Grammar, Transform> > 258 : mpl::true_ 259 {}; 260 261 }} // namespace boost::proto 262 263 #if defined(_MSC_VER) 264 # pragma warning(pop) 265 #endif 266 267 #endif 268