Jeff Thompson | ef2d5a4 | 2013-08-22 19:09:24 -0700 | [diff] [blame] | 1 | // return_type_traits.hpp -- Boost Lambda Library --------------------------- |
| 2 | |
| 3 | // Copyright (C) 1999, 2000 Jaakko Jarvi (jaakko.jarvi@cs.utu.fi) |
| 4 | // |
| 5 | // Distributed under the Boost Software License, Version 1.0. (See |
| 6 | // accompanying file LICENSE_1_0.txt or copy at |
| 7 | // http://www.boost.org/LICENSE_1_0.txt) |
| 8 | // |
| 9 | // For more information, see www.boost.org |
| 10 | |
| 11 | |
| 12 | #ifndef BOOST_LAMBDA_RETURN_TYPE_TRAITS_HPP |
| 13 | #define BOOST_LAMBDA_RETURN_TYPE_TRAITS_HPP |
| 14 | |
| 15 | #include "ndnboost/mpl/has_xxx.hpp" |
| 16 | |
| 17 | #include <cstddef> // needed for the ptrdiff_t |
| 18 | |
| 19 | namespace ndnboost { |
| 20 | namespace lambda { |
| 21 | |
| 22 | using ::ndnboost::type_traits::ice_and; |
| 23 | using ::ndnboost::type_traits::ice_or; |
| 24 | using ::ndnboost::type_traits::ice_not; |
| 25 | |
| 26 | // Much of the type deduction code for standard arithmetic types |
| 27 | // from Gary Powell |
| 28 | |
| 29 | // different arities: |
| 30 | template <class Act, class A1> struct return_type_1; // 1-ary actions |
| 31 | template <class Act, class A1, class A2> struct return_type_2; // 2-ary |
| 32 | template <class Act, class Args> struct return_type_N; // >3- ary |
| 33 | |
| 34 | template <class Act, class A1> struct return_type_1_prot; |
| 35 | template <class Act, class A1, class A2> struct return_type_2_prot; // 2-ary |
| 36 | template <class Act, class A1> struct return_type_N_prot; // >3-ary |
| 37 | |
| 38 | |
| 39 | namespace detail { |
| 40 | |
| 41 | template<class> class return_type_deduction_failure {}; |
| 42 | |
| 43 | // In some cases return type deduction should fail (an invalid lambda |
| 44 | // expression). Sometimes the lambda expression can be ok, the return type |
| 45 | // just is not deducible (user defined operators). Then return type deduction |
| 46 | // should never be entered at all, and the use of ret<> does this. |
| 47 | // However, for nullary lambda functors, return type deduction is always |
| 48 | // entered, and there seems to be no way around this. |
| 49 | |
| 50 | // (the return type is part of the prototype of the non-template |
| 51 | // operator()(). The prototype is instantiated, even though the body |
| 52 | // is not.) |
| 53 | |
| 54 | // So, in the case the return type deduction should fail, it should not |
| 55 | // fail directly, but rather result in a valid but wrong return type, |
| 56 | // causing a compile time error only if the function is really called. |
| 57 | |
| 58 | |
| 59 | |
| 60 | } // end detail |
| 61 | |
| 62 | |
| 63 | |
| 64 | // return_type_X_prot classes -------------------------------------------- |
| 65 | // These classes are the first layer that gets instantiated from the |
| 66 | // lambda_functor_base sig templates. It will check whether |
| 67 | // the action is protectable and one of arguments is "protected" or its |
| 68 | // evaluation will otherwise result in another lambda functor. |
| 69 | // If this is a case, the result type will be another lambda functor. |
| 70 | |
| 71 | // The arguments are always non-reference types, except for comma action |
| 72 | // where the right argument can be a reference too. This is because it |
| 73 | // matters (in the builtin case) whether the argument is an lvalue or |
| 74 | // rvalue: int i; i, 1 -> rvalue; 1, i -> lvalue |
| 75 | |
| 76 | template <class Act, class A> struct return_type_1_prot { |
| 77 | public: |
| 78 | typedef typename |
| 79 | detail::IF< |
| 80 | // is_protectable<Act>::value && is_lambda_functor<A>::value, |
| 81 | ice_and<is_protectable<Act>::value, is_lambda_functor<A>::value>::value, |
| 82 | lambda_functor< |
| 83 | lambda_functor_base< |
| 84 | Act, |
| 85 | tuple<typename detail::remove_reference_and_cv<A>::type> |
| 86 | > |
| 87 | >, |
| 88 | typename return_type_1<Act, A>::type |
| 89 | >::RET type; |
| 90 | }; |
| 91 | |
| 92 | // take care of the unavoidable instantiation for nullary case |
| 93 | template<class Act> struct return_type_1_prot<Act, null_type> { |
| 94 | typedef null_type type; |
| 95 | }; |
| 96 | |
| 97 | // Unary actions (result from unary operators) |
| 98 | // do not have a default return type. |
| 99 | template<class Act, class A> struct return_type_1 { |
| 100 | typedef typename |
| 101 | detail::return_type_deduction_failure<return_type_1> type; |
| 102 | }; |
| 103 | |
| 104 | |
| 105 | namespace detail { |
| 106 | |
| 107 | template <class T> |
| 108 | class protect_conversion { |
| 109 | typedef typename ndnboost::remove_reference<T>::type non_ref_T; |
| 110 | public: |
| 111 | |
| 112 | // add const to rvalues, so that all rvalues are stored as const in |
| 113 | // the args tuple |
| 114 | typedef typename detail::IF_type< |
| 115 | // ndnboost::is_reference<T>::value && !ndnboost::is_const<non_ref_T>::value, |
| 116 | ice_and<ndnboost::is_reference<T>::value, |
| 117 | ice_not<ndnboost::is_const<non_ref_T>::value>::value>::value, |
| 118 | detail::identity_mapping<T>, |
| 119 | const_copy_argument<non_ref_T> // handles funtion and array |
| 120 | >::type type; // types correctly |
| 121 | }; |
| 122 | |
| 123 | } // end detail |
| 124 | |
| 125 | template <class Act, class A, class B> struct return_type_2_prot { |
| 126 | |
| 127 | // experimental feature |
| 128 | // We may have a lambda functor as a result type of a subexpression |
| 129 | // (if protect) has been used. |
| 130 | // Thus, if one of the parameter types is a lambda functor, the result |
| 131 | // is a lambda functor as well. |
| 132 | // We need to make a conservative choise here. |
| 133 | // The resulting lambda functor stores all const reference arguments as |
| 134 | // const copies. References to non-const are stored as such. |
| 135 | // So if the source of the argument is a const open argument, a bound |
| 136 | // argument stored as a const reference, or a function returning a |
| 137 | // const reference, that information is lost. There is no way of |
| 138 | // telling apart 'real const references' from just 'LL internal |
| 139 | // const references' (or it would be really hard) |
| 140 | |
| 141 | // The return type is a subclass of lambda_functor, which has a converting |
| 142 | // copy constructor. It can copy any lambda functor, that has the same |
| 143 | // action type and code, and a copy compatible argument tuple. |
| 144 | |
| 145 | |
| 146 | typedef typename ndnboost::remove_reference<A>::type non_ref_A; |
| 147 | typedef typename ndnboost::remove_reference<B>::type non_ref_B; |
| 148 | |
| 149 | typedef typename |
| 150 | detail::IF< |
| 151 | // is_protectable<Act>::value && |
| 152 | // (is_lambda_functor<A>::value || is_lambda_functor<B>::value), |
| 153 | ice_and<is_protectable<Act>::value, |
| 154 | ice_or<is_lambda_functor<A>::value, |
| 155 | is_lambda_functor<B>::value>::value>::value, |
| 156 | lambda_functor< |
| 157 | lambda_functor_base< |
| 158 | Act, |
| 159 | tuple<typename detail::protect_conversion<A>::type, |
| 160 | typename detail::protect_conversion<B>::type> |
| 161 | > |
| 162 | >, |
| 163 | typename return_type_2<Act, non_ref_A, non_ref_B>::type |
| 164 | >::RET type; |
| 165 | }; |
| 166 | |
| 167 | // take care of the unavoidable instantiation for nullary case |
| 168 | template<class Act> struct return_type_2_prot<Act, null_type, null_type> { |
| 169 | typedef null_type type; |
| 170 | }; |
| 171 | // take care of the unavoidable instantiation for nullary case |
| 172 | template<class Act, class Other> struct return_type_2_prot<Act, Other, null_type> { |
| 173 | typedef null_type type; |
| 174 | }; |
| 175 | // take care of the unavoidable instantiation for nullary case |
| 176 | template<class Act, class Other> struct return_type_2_prot<Act, null_type, Other> { |
| 177 | typedef null_type type; |
| 178 | }; |
| 179 | |
| 180 | // comma is a special case, as the user defined operator can return |
| 181 | // an lvalue (reference) too, hence it must be handled at this level. |
| 182 | template<class A, class B> |
| 183 | struct return_type_2_comma |
| 184 | { |
| 185 | typedef typename ndnboost::remove_reference<A>::type non_ref_A; |
| 186 | typedef typename ndnboost::remove_reference<B>::type non_ref_B; |
| 187 | |
| 188 | typedef typename |
| 189 | detail::IF< |
| 190 | // is_protectable<other_action<comma_action> >::value && // it is protectable |
| 191 | // (is_lambda_functor<A>::value || is_lambda_functor<B>::value), |
| 192 | ice_and<is_protectable<other_action<comma_action> >::value, // it is protectable |
| 193 | ice_or<is_lambda_functor<A>::value, |
| 194 | is_lambda_functor<B>::value>::value>::value, |
| 195 | lambda_functor< |
| 196 | lambda_functor_base< |
| 197 | other_action<comma_action>, |
| 198 | tuple<typename detail::protect_conversion<A>::type, |
| 199 | typename detail::protect_conversion<B>::type> |
| 200 | > |
| 201 | >, |
| 202 | typename |
| 203 | return_type_2<other_action<comma_action>, non_ref_A, non_ref_B>::type |
| 204 | >::RET type1; |
| 205 | |
| 206 | // if no user defined return_type_2 (or plain_return_type_2) specialization |
| 207 | // matches, then return the righthand argument |
| 208 | typedef typename |
| 209 | detail::IF< |
| 210 | ndnboost::is_same<type1, detail::unspecified>::value, |
| 211 | B, |
| 212 | type1 |
| 213 | >::RET type; |
| 214 | |
| 215 | }; |
| 216 | |
| 217 | |
| 218 | // currently there are no protectable actions with > 2 args |
| 219 | |
| 220 | template<class Act, class Args> struct return_type_N_prot { |
| 221 | typedef typename return_type_N<Act, Args>::type type; |
| 222 | }; |
| 223 | |
| 224 | // take care of the unavoidable instantiation for nullary case |
| 225 | template<class Act> struct return_type_N_prot<Act, null_type> { |
| 226 | typedef null_type type; |
| 227 | }; |
| 228 | |
| 229 | // handle different kind of actions ------------------------ |
| 230 | |
| 231 | // use the return type given in the bind invocation as bind<Ret>(...) |
| 232 | template<int I, class Args, class Ret> |
| 233 | struct return_type_N<function_action<I, Ret>, Args> { |
| 234 | typedef Ret type; |
| 235 | }; |
| 236 | |
| 237 | // ::result_type support |
| 238 | |
| 239 | namespace detail |
| 240 | { |
| 241 | |
| 242 | BOOST_MPL_HAS_XXX_TRAIT_DEF(result_type) |
| 243 | |
| 244 | template<class F> struct get_result_type |
| 245 | { |
| 246 | typedef typename F::result_type type; |
| 247 | }; |
| 248 | |
| 249 | template<class F, class A> struct get_sig |
| 250 | { |
| 251 | typedef typename function_adaptor<F>::template sig<A>::type type; |
| 252 | }; |
| 253 | |
| 254 | } // namespace detail |
| 255 | |
| 256 | // Ret is detail::unspecified, so try to deduce return type |
| 257 | template<int I, class Args> |
| 258 | struct return_type_N<function_action<I, detail::unspecified>, Args > { |
| 259 | |
| 260 | // in the case of function action, the first element in Args is |
| 261 | // some type of function |
| 262 | typedef typename Args::head_type Func; |
| 263 | typedef typename detail::remove_reference_and_cv<Func>::type plain_Func; |
| 264 | |
| 265 | public: |
| 266 | // pass the function to function_adaptor, and get the return type from |
| 267 | // that |
| 268 | typedef typename detail::IF< |
| 269 | detail::has_result_type<plain_Func>::value, |
| 270 | detail::get_result_type<plain_Func>, |
| 271 | detail::get_sig<plain_Func, Args> |
| 272 | >::RET::type type; |
| 273 | }; |
| 274 | |
| 275 | |
| 276 | } // namespace lambda |
| 277 | } // namespace ndnboost |
| 278 | |
| 279 | #endif |
| 280 | |
| 281 | |
| 282 | |