xref: /aosp_15_r20/external/pigweed/third_party/fuchsia/repo/sdk/lib/fit/include/lib/fit/traits.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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