1 // Copyright 2018 The Fuchsia Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef LIB_FIT_TRAITS_H_ 6 #define LIB_FIT_TRAITS_H_ 7 8 #include <lib/stdcompat/type_traits.h> 9 10 #include <tuple> 11 #include <type_traits> 12 13 namespace fit { 14 15 // Encapsulates capture of a parameter pack. Typical use is to use instances of this empty struct 16 // for type dispatch in function template deduction/overload resolution. 17 // 18 // Example: 19 // template <typename Callable, typename... Args> 20 // auto inspect_args(Callable c, parameter_pack<Args...>) { 21 // // do something with Args... 22 // } 23 // 24 // template <typename Callable> 25 // auto inspect_args(Callable c) { 26 // return inspect_args(std::move(c), typename callable_traits<Callable>::args{}); 27 // } 28 template <typename... T> 29 struct parameter_pack { 30 static constexpr size_t size = sizeof...(T); 31 32 template <size_t i> 33 using at = typename std::tuple_element_t<i, std::tuple<T...>>; 34 }; 35 36 // |callable_traits| captures elements of interest from function-like types (functions, function 37 // pointers, and functors, including lambdas). Due to common usage patterns, const and non-const 38 // functors are treated identically. 39 // 40 // Member types: 41 // |args| - a |parameter_pack| that captures the parameter types of the function. See 42 // |parameter_pack| for usage and details. 43 // |return_type| - the return type of the function. 44 // |type| - the underlying functor or function pointer type. This member is absent if 45 // |callable_traits| are requested for a raw function signature (as opposed to a 46 // function pointer or functor; e.g. |callable_traits<void()>|). 47 // |signature| - the type of the equivalent function. 48 49 template <typename T> 50 struct callable_traits : public callable_traits<decltype(&T::operator())> {}; 51 52 // Treat mutable call operators the same as const call operators. 53 // 54 // It would be equivalent to erase the const instead, but the common case is lambdas, which are 55 // const, so prefer to nest less deeply for the common const case. 56 template <typename FunctorType, typename ReturnType, typename... ArgTypes> 57 struct callable_traits<ReturnType (FunctorType::*)(ArgTypes...)> 58 : public callable_traits<ReturnType (FunctorType::*)(ArgTypes...) const> {}; 59 60 // Common functor specialization. 61 template <typename FunctorType, typename ReturnType, typename... ArgTypes> 62 struct callable_traits<ReturnType (FunctorType::*)(ArgTypes...) const> 63 : public callable_traits<ReturnType (*)(ArgTypes...)> { 64 using type = FunctorType; 65 }; 66 67 // Function pointer specialization. 68 template <typename ReturnType, typename... ArgTypes> 69 struct callable_traits<ReturnType (*)(ArgTypes...)> 70 : public callable_traits<ReturnType(ArgTypes...)> { 71 using type = ReturnType (*)(ArgTypes...); 72 }; 73 74 // Base specialization. 75 template <typename ReturnType, typename... ArgTypes> 76 struct callable_traits<ReturnType(ArgTypes...)> { 77 using signature = ReturnType(ArgTypes...); 78 using return_type = ReturnType; 79 using args = parameter_pack<ArgTypes...>; 80 81 callable_traits() = delete; 82 }; 83 84 // Determines whether a type has an operator() that can be invoked. 85 template <typename T, typename = cpp17::void_t<>> 86 struct is_callable : public std::false_type {}; 87 template <typename ReturnType, typename... ArgTypes> 88 struct is_callable<ReturnType (*)(ArgTypes...)> : public std::true_type {}; 89 template <typename FunctorType, typename ReturnType, typename... ArgTypes> 90 struct is_callable<ReturnType (FunctorType::*)(ArgTypes...)> : public std::true_type {}; 91 template <typename T> 92 struct is_callable<T, cpp17::void_t<decltype(&T::operator())>> : public std::true_type {}; 93 94 namespace internal { 95 96 template <typename Default, typename AlwaysVoid, template <typename...> class Op, typename... Args> 97 struct detector { 98 using value_t = std::false_type; 99 using type = Default; 100 }; 101 102 template <typename Default, template <typename...> class Op, typename... Args> 103 struct detector<Default, cpp17::void_t<Op<Args...>>, Op, Args...> { 104 using value_t = std::true_type; 105 using type = Op<Args...>; 106 }; 107 108 } // namespace internal 109 110 // Default type when detection fails; |void| could be a legitimate result so we need something else. 111 struct nonesuch { 112 constexpr nonesuch() = delete; 113 constexpr nonesuch(const nonesuch&) = delete; 114 constexpr nonesuch& operator=(const nonesuch&) = delete; 115 constexpr nonesuch(nonesuch&&) = delete; 116 constexpr nonesuch& operator=(nonesuch&&) = delete; 117 118 // This ensures that no one can actually make a value of this type. 119 ~nonesuch() = delete; 120 }; 121 122 // Trait for detecting if |Op<Args...>| resolves to a legitimate type. Without this trait, 123 // metaprogrammers often resort to making their own detectors with |void_t<>|. 124 // 125 // With this trait, they can simply make the core part of the detector, like this to detect an 126 // |operator==|: 127 // 128 // template <typename T> 129 // using equality_t = decltype(std::declval<const T&>() == std::declval<const T&>()); 130 // 131 // And then make their detector like so: 132 // 133 // template <typename T> 134 // using has_equality = fit::is_detected<equality_t, T>; 135 template <template <typename...> class Op, typename... Args> 136 using is_detected = typename ::fit::internal::detector<nonesuch, void, Op, Args...>::value_t; 137 138 template <template <typename...> class Op, typename... Args> 139 constexpr bool is_detected_v = is_detected<Op, Args...>::value; 140 141 // Trait for accessing the result of |Op<Args...>| if it exists, or |fit::nonesuch| otherwise. 142 // 143 // This is advantageous because the same "core" of the detector can be used both for finding if the 144 // prospective type exists at all (with |fit::is_detected|) and what it actually is. 145 template <template <typename...> class Op, typename... Args> 146 using detected_t = typename ::fit::internal::detector<nonesuch, void, Op, Args...>::type; 147 148 // Trait for detecting whether |Op<Args...>| exists (via |::value_t|, which is either 149 // |std::true_type| or |std::false_type|) and accessing its type via |::type| if so, which will 150 // otherwise be |Default|. 151 template <typename Default, template <typename...> class Op, typename... Args> 152 using detected_or = typename ::fit::internal::detector<Default, void, Op, Args...>; 153 154 // Essentially the same as |fit::detected_t| but with a user-specified default instead of 155 // |fit::nonesuch|. 156 template <typename Default, template <typename...> class Op, typename... Args> 157 using detected_or_t = typename detected_or<Default, Op, Args...>::type; 158 159 // Trait for detecting whether |Op<Args...>| exists and is exactly the type |Expected|. 160 // 161 // To reuse the previous example of |operator==| and |equality_t|: 162 // 163 // template <typename T> 164 // using has_equality_exact = fit::is_detected_exact<bool, equality_t, T>; 165 template <typename Expected, template <typename...> class Op, typename... Args> 166 using is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>; 167 168 template <typename Expected, template <typename...> class Op, typename... Args> 169 constexpr bool is_detected_exact_v = is_detected_exact<Expected, Op, Args...>::value; 170 171 // Essentially the same as |fit::is_detected_exact| but tests for convertibility instead of exact 172 // sameness. 173 template <typename To, template <typename...> class Op, typename... Args> 174 using is_detected_convertible = std::is_convertible<detected_t<Op, Args...>, To>; 175 176 template <typename To, template <typename...> class Op, typename... Args> 177 constexpr bool is_detected_convertible_v = is_detected_convertible<To, Op, Args...>::value; 178 179 } // namespace fit 180 181 #endif // LIB_FIT_TRAITS_H_ 182