1 | #pragma once |
2 | |
3 | #include <c10/util/C++17.h> |
4 | #include <c10/util/TypeTraits.h> |
5 | |
6 | namespace c10 { |
7 | namespace guts { |
8 | |
9 | template <class... T> |
10 | struct false_t : std::false_type {}; |
11 | template <template <class> class... T> |
12 | struct false_higher_t : std::false_type {}; |
13 | |
14 | namespace typelist { |
15 | |
16 | /** |
17 | * Type holding a list of types for compile time type computations |
18 | */ |
19 | template <class... Items> |
20 | struct typelist final { |
21 | public: |
22 | typelist() = delete; // not for instantiation |
23 | }; |
24 | |
25 | /** |
26 | * Returns the number of types in a typelist |
27 | * Example: |
28 | * 3 == size<typelist<int, int, double>>::value |
29 | */ |
30 | template <class TypeList> |
31 | struct size final { |
32 | static_assert( |
33 | false_t<TypeList>::value, |
34 | "In typelist::size<T>, T must be typelist<...>." ); |
35 | }; |
36 | template <class... Types> |
37 | struct size<typelist<Types...>> final { |
38 | static constexpr size_t value = sizeof...(Types); |
39 | }; |
40 | |
41 | /** |
42 | * Transforms a list of types into a tuple holding these types. |
43 | * Example: |
44 | * std::tuple<int, string> == to_tuple_t<typelist<int, string>> |
45 | */ |
46 | template <class TypeList> |
47 | struct to_tuple final { |
48 | static_assert( |
49 | false_t<TypeList>::value, |
50 | "In typelist::to_tuple<T>, T must be typelist<...>." ); |
51 | }; |
52 | template <class... Types> |
53 | struct to_tuple<typelist<Types...>> final { |
54 | using type = std::tuple<Types...>; |
55 | }; |
56 | template <class TypeList> |
57 | using to_tuple_t = typename to_tuple<TypeList>::type; |
58 | |
59 | /** |
60 | * Creates a typelist containing the types of a given tuple. |
61 | * Example: |
62 | * typelist<int, string> == from_tuple_t<std::tuple<int, string>> |
63 | */ |
64 | template <class Tuple> |
65 | struct from_tuple final { |
66 | static_assert( |
67 | false_t<Tuple>::value, |
68 | "In typelist::from_tuple<T>, T must be std::tuple<...>." ); |
69 | }; |
70 | template <class... Types> |
71 | struct from_tuple<std::tuple<Types...>> final { |
72 | using type = typelist<Types...>; |
73 | }; |
74 | template <class Tuple> |
75 | using from_tuple_t = typename from_tuple<Tuple>::type; |
76 | |
77 | /** |
78 | * Concatenates multiple type lists. |
79 | * Example: |
80 | * typelist<int, string, int> == concat_t<typelist<int, string>, |
81 | * typelist<int>> |
82 | */ |
83 | template <class... TypeLists> |
84 | struct concat final { |
85 | static_assert( |
86 | false_t<TypeLists...>::value, |
87 | "In typelist::concat<T1, ...>, the T arguments each must be typelist<...>." ); |
88 | }; |
89 | template <class... Head1Types, class... Head2Types, class... TailLists> |
90 | struct concat<typelist<Head1Types...>, typelist<Head2Types...>, TailLists...> |
91 | final { |
92 | using type = |
93 | typename concat<typelist<Head1Types..., Head2Types...>, TailLists...>:: |
94 | type; |
95 | }; |
96 | template <class... HeadTypes> |
97 | struct concat<typelist<HeadTypes...>> final { |
98 | using type = typelist<HeadTypes...>; |
99 | }; |
100 | template <> |
101 | struct concat<> final { |
102 | using type = typelist<>; |
103 | }; |
104 | template <class... TypeLists> |
105 | using concat_t = typename concat<TypeLists...>::type; |
106 | |
107 | /** |
108 | * Filters the types in a type list by a type trait. |
109 | * Examples: |
110 | * typelist<int&, const string&&> == filter_t<std::is_reference, |
111 | * typelist<void, string, int&, bool, const string&&, int>> |
112 | */ |
113 | template <template <class> class Condition, class TypeList> |
114 | struct filter final { |
115 | static_assert( |
116 | false_t<TypeList>::value, |
117 | "In typelist::filter<Condition, TypeList>, the TypeList argument must be typelist<...>." ); |
118 | }; |
119 | template <template <class> class Condition, class Head, class... Tail> |
120 | struct filter<Condition, typelist<Head, Tail...>> final { |
121 | static_assert( |
122 | is_type_condition<Condition>::value, |
123 | "In typelist::filter<Condition, TypeList>, the Condition argument must be a condition type trait, i.e. have a static constexpr bool ::value member." ); |
124 | using type = std::conditional_t< |
125 | Condition<Head>::value, |
126 | concat_t< |
127 | typelist<Head>, |
128 | typename filter<Condition, typelist<Tail...>>::type>, |
129 | typename filter<Condition, typelist<Tail...>>::type>; |
130 | }; |
131 | template <template <class> class Condition> |
132 | struct filter<Condition, typelist<>> final { |
133 | static_assert( |
134 | is_type_condition<Condition>::value, |
135 | "In typelist::filter<Condition, TypeList>, the Condition argument must be a condition type trait, i.e. have a static constexpr bool ::value member." ); |
136 | using type = typelist<>; |
137 | }; |
138 | template <template <class> class Condition, class TypeList> |
139 | using filter_t = typename filter<Condition, TypeList>::type; |
140 | |
141 | /** |
142 | * Counts how many types in the list fulfill a type trait |
143 | * Examples: |
144 | * 2 == count_if<std::is_reference, typelist<void, string, int&, bool, const |
145 | * string&&, int>> |
146 | */ |
147 | template <template <class> class Condition, class TypeList> |
148 | struct count_if final { |
149 | static_assert( |
150 | is_type_condition<Condition>::value, |
151 | "In typelist::count_if<Condition, TypeList>, the Condition argument must be a condition type trait, i.e. have a static constexpr bool ::value member." ); |
152 | static_assert( |
153 | is_instantiation_of<typelist, TypeList>::value, |
154 | "In typelist::count_if<Condition, TypeList>, the TypeList argument must be typelist<...>." ); |
155 | // TODO Direct implementation might be faster |
156 | static constexpr size_t value = size<filter_t<Condition, TypeList>>::value; |
157 | }; |
158 | |
159 | /** |
160 | * Checks if a typelist contains a certain type. |
161 | * Examples: |
162 | * contains<typelist<int, string>, string> == true_type |
163 | * contains<typelist<int, string>, double> == false_type |
164 | */ |
165 | namespace detail { |
166 | template <class TypeList, class Type, class Enable = void> |
167 | struct contains {}; |
168 | template <class Type> |
169 | struct contains<typelist<>, Type, void> : std::false_type {}; |
170 | template <class Type, class Head, class... Tail> |
171 | struct contains< |
172 | typelist<Head, Tail...>, |
173 | Type, |
174 | std::enable_if_t<std::is_same<Head, Type>::value>> : std::true_type {}; |
175 | template <class Type, class Head, class... Tail> |
176 | struct contains< |
177 | typelist<Head, Tail...>, |
178 | Type, |
179 | std::enable_if_t<!std::is_same<Head, Type>::value>> |
180 | : contains<typelist<Tail...>, Type> {}; |
181 | } // namespace detail |
182 | template <class TypeList, class Type> |
183 | using contains = typename detail::contains<TypeList, Type>::type; |
184 | |
185 | /** |
186 | * Returns true iff the type trait is true for all types in the type list |
187 | * Examples: |
188 | * true == all<std::is_reference, typelist<int&, const float&&, const |
189 | * MyClass&>>::value false == all<std::is_reference, typelist<int&, const |
190 | * float&&, MyClass>>::value |
191 | */ |
192 | template <template <class> class Condition, class TypeList> |
193 | struct all { |
194 | static_assert( |
195 | false_t<TypeList>::value, |
196 | "In typelist::all<Condition, TypeList>, the TypeList argument must be typelist<...>." ); |
197 | }; |
198 | template <template <class> class Condition, class... Types> |
199 | struct all<Condition, typelist<Types...>> |
200 | : guts::conjunction<Condition<Types>...> { |
201 | static_assert( |
202 | is_type_condition<Condition>::value, |
203 | "In typelist::all<Condition, TypeList>, the Condition argument must be a condition type trait, i.e. have a static constexpr bool ::value member." ); |
204 | }; |
205 | |
206 | /** |
207 | * Returns true iff the type trait is true for any type in the type list |
208 | * Examples: |
209 | * true == true_for_any_type<std::is_reference, typelist<int, const |
210 | * float&&, const MyClass>>::value false == |
211 | * true_for_any_type<std::is_reference, typelist<int, const float, |
212 | * MyClass>>::value |
213 | */ |
214 | template <template <class> class Condition, class TypeList> |
215 | struct true_for_any_type final { |
216 | static_assert( |
217 | false_t<TypeList>::value, |
218 | "In typelist::true_for_any_type<Condition, TypeList>, the TypeList argument must be typelist<...>." ); |
219 | }; |
220 | template <template <class> class Condition, class... Types> |
221 | struct true_for_any_type<Condition, typelist<Types...>> final |
222 | : guts::disjunction<Condition<Types>...> { |
223 | static_assert( |
224 | is_type_condition<Condition>::value, |
225 | "In typelist::true_for_any_type<Condition, TypeList>, the Condition argument must be a condition type trait, i.e. have a static constexpr bool ::value member." ); |
226 | }; |
227 | |
228 | /** |
229 | * Maps types of a type list using a type trait |
230 | * Example: |
231 | * typelist<int&, double&, string&> == map_t<std::add_lvalue_reference_t, |
232 | * typelist<int, double, string>> |
233 | */ |
234 | template <template <class> class Mapper, class TypeList> |
235 | struct map final { |
236 | static_assert( |
237 | false_t<TypeList>::value, |
238 | "In typelist::map<Mapper, TypeList>, the TypeList argument must be typelist<...>." ); |
239 | }; |
240 | template <template <class> class Mapper, class... Types> |
241 | struct map<Mapper, typelist<Types...>> final { |
242 | using type = typelist<Mapper<Types>...>; |
243 | }; |
244 | template <template <class> class Mapper, class TypeList> |
245 | using map_t = typename map<Mapper, TypeList>::type; |
246 | |
247 | /** |
248 | * Returns the first element of a type list. |
249 | * Example: |
250 | * int == head_t<typelist<int, string>> |
251 | */ |
252 | template <class TypeList> |
253 | struct head final { |
254 | static_assert( |
255 | false_t<TypeList>::value, |
256 | "In typelist::head<T>, the T argument must be typelist<...>." ); |
257 | }; |
258 | template <class Head, class... Tail> |
259 | struct head<typelist<Head, Tail...>> final { |
260 | using type = Head; |
261 | }; |
262 | template <class TypeList> |
263 | using head_t = typename head<TypeList>::type; |
264 | |
265 | /** |
266 | * Returns the first element of a type list, or the specified default if the |
267 | * type list is empty. Example: int == head_t<bool, typelist<int, string>> |
268 | * bool == head_t<bool, typelist<>> |
269 | */ |
270 | template <class Default, class TypeList> |
271 | struct head_with_default final { |
272 | using type = Default; |
273 | }; |
274 | template <class Default, class Head, class... Tail> |
275 | struct head_with_default<Default, typelist<Head, Tail...>> final { |
276 | using type = Head; |
277 | }; |
278 | template <class Default, class TypeList> |
279 | using head_with_default_t = typename head_with_default<Default, TypeList>::type; |
280 | |
281 | /** |
282 | * Returns the N-th element of a type list. |
283 | * Example: |
284 | * int == element_t<1, typelist<float, int, char>> |
285 | */ |
286 | |
287 | /// Base template. |
288 | template <size_t Index, class TypeList> |
289 | struct element final { |
290 | static_assert( |
291 | false_t<TypeList>::value, |
292 | "In typelist::element<T>, the T argument must be typelist<...>." ); |
293 | }; |
294 | |
295 | /// Successful case, we have reached the zero index and can "return" the head |
296 | /// type. |
297 | template <class Head, class... Tail> |
298 | struct element<0, typelist<Head, Tail...>> { |
299 | using type = Head; |
300 | }; |
301 | |
302 | /// Error case, we have an index but ran out of types! It will only be selected |
303 | /// if `Ts...` is actually empty! |
304 | template <size_t Index, class... Ts> |
305 | struct element<Index, typelist<Ts...>> { |
306 | static_assert( |
307 | Index < sizeof...(Ts), |
308 | "Index is out of bounds in typelist::element" ); |
309 | }; |
310 | |
311 | /// Shave off types until we hit the <0, Head, Tail...> or <Index> case. |
312 | template <size_t Index, class Head, class... Tail> |
313 | struct element<Index, typelist<Head, Tail...>> |
314 | : element<Index - 1, typelist<Tail...>> {}; |
315 | |
316 | /// Convenience alias. |
317 | template <size_t Index, class TypeList> |
318 | using element_t = typename element<Index, TypeList>::type; |
319 | |
320 | /** |
321 | * Returns the last element of a type list. |
322 | * Example: |
323 | * int == last_t<typelist<int, string>> |
324 | */ |
325 | template <class TypeList> |
326 | struct last final { |
327 | static_assert( |
328 | false_t<TypeList>::value, |
329 | "In typelist::last<T>, the T argument must be typelist<...>." ); |
330 | }; |
331 | template <class Head, class... Tail> |
332 | struct last<typelist<Head, Tail...>> final { |
333 | using type = typename last<typelist<Tail...>>::type; |
334 | }; |
335 | template <class Head> |
336 | struct last<typelist<Head>> final { |
337 | using type = Head; |
338 | }; |
339 | template <class TypeList> |
340 | using last_t = typename last<TypeList>::type; |
341 | static_assert( |
342 | std::is_same<int, last_t<typelist<double, float, int>>>::value, |
343 | "" ); |
344 | |
345 | /** |
346 | * Take/drop a number of arguments from a typelist. |
347 | * Example: |
348 | * typelist<int, string> == take_t<typelist<int, string, bool>, 2> |
349 | * typelist<bool> == drop_t<typelist<int, string, bool>, 2> |
350 | */ |
351 | namespace detail { |
352 | template <class TypeList, size_t offset, class IndexSequence> |
353 | struct take_elements final {}; |
354 | |
355 | template <class TypeList, size_t offset, size_t... Indices> |
356 | struct take_elements<TypeList, offset, std::index_sequence<Indices...>> final { |
357 | using type = typelist<typename element<offset + Indices, TypeList>::type...>; |
358 | }; |
359 | } // namespace detail |
360 | |
361 | template <class TypeList, size_t num> |
362 | struct take final { |
363 | static_assert( |
364 | is_instantiation_of<typelist, TypeList>::value, |
365 | "In typelist::take<T, num>, the T argument must be typelist<...>." ); |
366 | static_assert( |
367 | num <= size<TypeList>::value, |
368 | "Tried to typelist::take more elements than there are in the list" ); |
369 | using type = typename detail:: |
370 | take_elements<TypeList, 0, std::make_index_sequence<num>>::type; |
371 | }; |
372 | template <class TypeList, size_t num> |
373 | using take_t = typename take<TypeList, num>::type; |
374 | |
375 | template <class TypeList, size_t num> |
376 | struct drop final { |
377 | static_assert( |
378 | is_instantiation_of<typelist, TypeList>::value, |
379 | "In typelist::drop<T, num>, the T argument must be typelist<...>." ); |
380 | static_assert( |
381 | num <= size<TypeList>::value, |
382 | "Tried to typelist::drop more elements than there are in the list" ); |
383 | using type = typename detail::take_elements< |
384 | TypeList, |
385 | num, |
386 | std::make_index_sequence<size<TypeList>::value - num>>::type; |
387 | }; |
388 | template <class TypeList, size_t num> |
389 | using drop_t = typename drop<TypeList, num>::type; |
390 | |
391 | /** |
392 | * Like drop, but returns an empty list rather than an assertion error if `num` |
393 | * is larger than the size of the TypeList. |
394 | * Example: |
395 | * typelist<> == drop_if_nonempty_t<typelist<string, bool>, 2> |
396 | * typelist<> == drop_if_nonempty_t<typelist<int, string, bool>, 3> |
397 | */ |
398 | template <class TypeList, size_t num> |
399 | struct drop_if_nonempty final { |
400 | static_assert( |
401 | is_instantiation_of<typelist, TypeList>::value, |
402 | "In typelist::drop<T, num>, the T argument must be typelist<...>." ); |
403 | using type = typename detail::take_elements< |
404 | TypeList, |
405 | min(num, size<TypeList>::value), |
406 | std::make_index_sequence< |
407 | size<TypeList>::value - min(num, size<TypeList>::value)>>::type; |
408 | }; |
409 | template <class TypeList, size_t num> |
410 | using drop_if_nonempty_t = typename drop_if_nonempty<TypeList, num>::type; |
411 | |
412 | /** |
413 | * Reverses a typelist. |
414 | * Example: |
415 | * typelist<int, string> == reverse_t<typelist<string, int>> |
416 | */ |
417 | template <class TypeList> |
418 | struct reverse final { |
419 | static_assert( |
420 | false_t<TypeList>::value, |
421 | "In typelist::reverse<T>, the T argument must be typelist<...>." ); |
422 | }; |
423 | template <class Head, class... Tail> |
424 | struct reverse<typelist<Head, Tail...>> final { |
425 | using type = |
426 | concat_t<typename reverse<typelist<Tail...>>::type, typelist<Head>>; |
427 | }; |
428 | template <> |
429 | struct reverse<typelist<>> final { |
430 | using type = typelist<>; |
431 | }; |
432 | template <class TypeList> |
433 | using reverse_t = typename reverse<TypeList>::type; |
434 | |
435 | /** |
436 | * Find the index of the first type in a typelist fulfilling a type trait |
437 | * condition. Example: |
438 | * |
439 | * 2 == find_if<typelist<char, int, char&, int&>, std::is_reference>::value |
440 | */ |
441 | template <class TypeList, template <class> class Condition, class Enable = void> |
442 | struct find_if final { |
443 | static_assert( |
444 | false_t<TypeList>::value, |
445 | "In typelist::find_if<TypeList, Condition>, the TypeList argument must be typelist<...>." ); |
446 | }; |
447 | template <template <class> class Condition> |
448 | struct find_if<typelist<>, Condition, void> final { |
449 | static_assert( |
450 | false_higher_t<Condition>::value, |
451 | "In typelist::find_if<Type/List, Condition>, didn't find any type fulfilling the Condition." ); |
452 | }; |
453 | template <class Head, class... Tail, template <class> class Condition> |
454 | struct find_if< |
455 | typelist<Head, Tail...>, |
456 | Condition, |
457 | std::enable_if_t<Condition<Head>::value>> |
458 | final { |
459 | static constexpr size_t value = 0; |
460 | }; |
461 | template <class Head, class... Tail, template <class> class Condition> |
462 | struct find_if< |
463 | typelist<Head, Tail...>, |
464 | Condition, |
465 | std::enable_if_t<!Condition<Head>::value>> |
466 | final { |
467 | static constexpr size_t value = |
468 | 1 + find_if<typelist<Tail...>, Condition>::value; |
469 | }; |
470 | |
471 | /** |
472 | * Maps a list of types into a list of values. |
473 | * Examples: |
474 | * // Example 1 |
475 | * auto sizes = |
476 | * map_types_to_values<typelist<int64_t, bool, uint32_t>>( |
477 | * [] (auto t) { return sizeof(decltype(t)::type); } |
478 | * ); |
479 | * // sizes == std::tuple<size_t, size_t, size_t>{8, 1, 4} |
480 | * |
481 | * // Example 2 |
482 | * auto shared_ptrs = |
483 | * map_types_to_values<typelist<int, double>>( |
484 | * [] (auto t) { return make_shared<typename decltype(t)::type>(); } |
485 | * ); |
486 | * // shared_ptrs == std::tuple<shared_ptr<int>, shared_ptr<double>>() |
487 | */ |
488 | namespace detail { |
489 | template <class T> |
490 | struct type_ final { |
491 | using type = T; |
492 | }; |
493 | template <class TypeList> |
494 | struct map_types_to_values final { |
495 | static_assert( |
496 | false_t<TypeList>::value, |
497 | "In typelist::map_types_to_values<T>, the T argument must be typelist<...>." ); |
498 | }; |
499 | template <class... Types> |
500 | struct map_types_to_values<typelist<Types...>> final { |
501 | template <class Func> |
502 | static std::tuple<c10::invoke_result_t<Func, type_<Types>>...> call( |
503 | Func&& func) { |
504 | return std::tuple<c10::invoke_result_t<Func, type_<Types>>...>{ |
505 | std::forward<Func>(func)(type_<Types>())...}; |
506 | } |
507 | }; |
508 | } // namespace detail |
509 | |
510 | template <class TypeList, class Func> |
511 | decltype(auto) map_types_to_values(Func&& func) { |
512 | return detail::map_types_to_values<TypeList>::call(std::forward<Func>(func)); |
513 | } |
514 | |
515 | } // namespace typelist |
516 | } // namespace guts |
517 | } // namespace c10 |
518 | |