1#pragma once
2
3#include <c10/util/C++17.h>
4#include <c10/util/TypeTraits.h>
5
6namespace c10 {
7namespace guts {
8
9template <class... T>
10struct false_t : std::false_type {};
11template <template <class> class... T>
12struct false_higher_t : std::false_type {};
13
14namespace typelist {
15
16/**
17 * Type holding a list of types for compile time type computations
18 */
19template <class... Items>
20struct 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 */
30template <class TypeList>
31struct size final {
32 static_assert(
33 false_t<TypeList>::value,
34 "In typelist::size<T>, T must be typelist<...>.");
35};
36template <class... Types>
37struct 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 */
46template <class TypeList>
47struct to_tuple final {
48 static_assert(
49 false_t<TypeList>::value,
50 "In typelist::to_tuple<T>, T must be typelist<...>.");
51};
52template <class... Types>
53struct to_tuple<typelist<Types...>> final {
54 using type = std::tuple<Types...>;
55};
56template <class TypeList>
57using 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 */
64template <class Tuple>
65struct from_tuple final {
66 static_assert(
67 false_t<Tuple>::value,
68 "In typelist::from_tuple<T>, T must be std::tuple<...>.");
69};
70template <class... Types>
71struct from_tuple<std::tuple<Types...>> final {
72 using type = typelist<Types...>;
73};
74template <class Tuple>
75using 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 */
83template <class... TypeLists>
84struct concat final {
85 static_assert(
86 false_t<TypeLists...>::value,
87 "In typelist::concat<T1, ...>, the T arguments each must be typelist<...>.");
88};
89template <class... Head1Types, class... Head2Types, class... TailLists>
90struct concat<typelist<Head1Types...>, typelist<Head2Types...>, TailLists...>
91 final {
92 using type =
93 typename concat<typelist<Head1Types..., Head2Types...>, TailLists...>::
94 type;
95};
96template <class... HeadTypes>
97struct concat<typelist<HeadTypes...>> final {
98 using type = typelist<HeadTypes...>;
99};
100template <>
101struct concat<> final {
102 using type = typelist<>;
103};
104template <class... TypeLists>
105using 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 */
113template <template <class> class Condition, class TypeList>
114struct filter final {
115 static_assert(
116 false_t<TypeList>::value,
117 "In typelist::filter<Condition, TypeList>, the TypeList argument must be typelist<...>.");
118};
119template <template <class> class Condition, class Head, class... Tail>
120struct 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};
131template <template <class> class Condition>
132struct 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};
138template <template <class> class Condition, class TypeList>
139using 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 */
147template <template <class> class Condition, class TypeList>
148struct 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 */
165namespace detail {
166template <class TypeList, class Type, class Enable = void>
167struct contains {};
168template <class Type>
169struct contains<typelist<>, Type, void> : std::false_type {};
170template <class Type, class Head, class... Tail>
171struct contains<
172 typelist<Head, Tail...>,
173 Type,
174 std::enable_if_t<std::is_same<Head, Type>::value>> : std::true_type {};
175template <class Type, class Head, class... Tail>
176struct 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
182template <class TypeList, class Type>
183using 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 */
192template <template <class> class Condition, class TypeList>
193struct all {
194 static_assert(
195 false_t<TypeList>::value,
196 "In typelist::all<Condition, TypeList>, the TypeList argument must be typelist<...>.");
197};
198template <template <class> class Condition, class... Types>
199struct 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 */
214template <template <class> class Condition, class TypeList>
215struct 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};
220template <template <class> class Condition, class... Types>
221struct 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 */
234template <template <class> class Mapper, class TypeList>
235struct map final {
236 static_assert(
237 false_t<TypeList>::value,
238 "In typelist::map<Mapper, TypeList>, the TypeList argument must be typelist<...>.");
239};
240template <template <class> class Mapper, class... Types>
241struct map<Mapper, typelist<Types...>> final {
242 using type = typelist<Mapper<Types>...>;
243};
244template <template <class> class Mapper, class TypeList>
245using 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 */
252template <class TypeList>
253struct head final {
254 static_assert(
255 false_t<TypeList>::value,
256 "In typelist::head<T>, the T argument must be typelist<...>.");
257};
258template <class Head, class... Tail>
259struct head<typelist<Head, Tail...>> final {
260 using type = Head;
261};
262template <class TypeList>
263using 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 */
270template <class Default, class TypeList>
271struct head_with_default final {
272 using type = Default;
273};
274template <class Default, class Head, class... Tail>
275struct head_with_default<Default, typelist<Head, Tail...>> final {
276 using type = Head;
277};
278template <class Default, class TypeList>
279using 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.
288template <size_t Index, class TypeList>
289struct 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.
297template <class Head, class... Tail>
298struct 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!
304template <size_t Index, class... Ts>
305struct 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.
312template <size_t Index, class Head, class... Tail>
313struct element<Index, typelist<Head, Tail...>>
314 : element<Index - 1, typelist<Tail...>> {};
315
316/// Convenience alias.
317template <size_t Index, class TypeList>
318using 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 */
325template <class TypeList>
326struct last final {
327 static_assert(
328 false_t<TypeList>::value,
329 "In typelist::last<T>, the T argument must be typelist<...>.");
330};
331template <class Head, class... Tail>
332struct last<typelist<Head, Tail...>> final {
333 using type = typename last<typelist<Tail...>>::type;
334};
335template <class Head>
336struct last<typelist<Head>> final {
337 using type = Head;
338};
339template <class TypeList>
340using last_t = typename last<TypeList>::type;
341static_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 */
351namespace detail {
352template <class TypeList, size_t offset, class IndexSequence>
353struct take_elements final {};
354
355template <class TypeList, size_t offset, size_t... Indices>
356struct take_elements<TypeList, offset, std::index_sequence<Indices...>> final {
357 using type = typelist<typename element<offset + Indices, TypeList>::type...>;
358};
359} // namespace detail
360
361template <class TypeList, size_t num>
362struct 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};
372template <class TypeList, size_t num>
373using take_t = typename take<TypeList, num>::type;
374
375template <class TypeList, size_t num>
376struct 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};
388template <class TypeList, size_t num>
389using 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 */
398template <class TypeList, size_t num>
399struct 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};
409template <class TypeList, size_t num>
410using 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 */
417template <class TypeList>
418struct reverse final {
419 static_assert(
420 false_t<TypeList>::value,
421 "In typelist::reverse<T>, the T argument must be typelist<...>.");
422};
423template <class Head, class... Tail>
424struct reverse<typelist<Head, Tail...>> final {
425 using type =
426 concat_t<typename reverse<typelist<Tail...>>::type, typelist<Head>>;
427};
428template <>
429struct reverse<typelist<>> final {
430 using type = typelist<>;
431};
432template <class TypeList>
433using 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 */
441template <class TypeList, template <class> class Condition, class Enable = void>
442struct 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};
447template <template <class> class Condition>
448struct 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};
453template <class Head, class... Tail, template <class> class Condition>
454struct 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};
461template <class Head, class... Tail, template <class> class Condition>
462struct 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 */
488namespace detail {
489template <class T>
490struct type_ final {
491 using type = T;
492};
493template <class TypeList>
494struct 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};
499template <class... Types>
500struct 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
510template <class TypeList, class Func>
511decltype(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