-
Notifications
You must be signed in to change notification settings - Fork 16
Expand file tree
/
Copy pathtype_traits.hpp
More file actions
362 lines (309 loc) · 11.3 KB
/
type_traits.hpp
File metadata and controls
362 lines (309 loc) · 11.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
#pragma once
#include <stdx/compiler.hpp>
#include <stdx/ct_conversions.hpp>
#include <boost/mp11/algorithm.hpp>
#include <type_traits>
#include <utility>
namespace stdx {
inline namespace v1 {
template <typename E> constexpr auto to_underlying(E e) noexcept {
if constexpr (std::is_enum_v<E>) {
return static_cast<std::underlying_type_t<E>>(e);
} else {
return e;
}
}
template <typename E>
using underlying_type_t = decltype(to_underlying(std::declval<E>()));
template <typename T> struct remove_cvref {
using type = std::remove_cv_t<std::remove_reference_t<T>>;
};
template <typename T> using remove_cvref_t = typename remove_cvref<T>::type;
namespace detail {
template <bool> struct conditional;
template <> struct conditional<true> {
template <typename T, typename> using choice_t = T;
};
template <> struct conditional<false> {
template <typename, typename U> using choice_t = U;
};
} // namespace detail
template <bool B, typename T, typename U>
using conditional_t = typename detail::conditional<B>::template choice_t<T, U>;
template <template <typename...> typename P, typename X, typename Y = void>
using type_or_t = conditional_t<P<X>::value, X, Y>;
template <typename T>
constexpr bool is_function_v =
not std::is_reference_v<T> and not std::is_const_v<std::add_const_t<T>>;
namespace detail {
struct call_base {
auto operator()() -> void;
};
template <typename, bool> struct callable_test : call_base {};
template <typename F>
struct callable_test<F, true> : remove_cvref_t<F>, call_base {};
template <typename F, typename = void> constexpr auto is_func_obj = true;
template <typename F>
constexpr auto is_func_obj<
F,
std::void_t<decltype(&callable_test<F, std::is_class_v<F>>::operator())>> =
false;
} // namespace detail
template <typename T>
constexpr bool is_function_object_v = detail::is_func_obj<T>;
template <typename T>
constexpr bool is_callable_v = is_function_v<T> or is_function_object_v<T>;
constexpr auto is_constant_evaluated() noexcept -> bool {
return __builtin_is_constant_evaluated();
}
template <typename T> struct type_identity {
using type = T;
};
template <typename T> using type_identity_t = typename type_identity<T>::type;
template <typename T>
constexpr static auto type_identity_v = type_identity<T>{};
namespace detail {
template <typename T, template <typename...> typename U>
constexpr bool is_type_specialization_of_v = false;
template <typename... Ts, template <typename...> typename U>
constexpr bool is_type_specialization_of_v<U<Ts...> &, U> = true;
template <typename... Ts, template <typename...> typename U>
constexpr bool is_type_specialization_of_v<U<Ts...> const &, U> = true;
template <typename T, template <auto...> typename U>
constexpr bool is_value_specialization_of_v = false;
template <auto... Vs, template <auto...> typename U>
constexpr bool is_value_specialization_of_v<U<Vs...> &, U> = true;
template <auto... Vs, template <auto...> typename U>
constexpr bool is_value_specialization_of_v<U<Vs...> const &, U> = true;
} // namespace detail
template <typename U, template <typename...> typename T>
constexpr bool is_specialization_of_v =
detail::is_type_specialization_of_v<U &, T>;
template <typename U, template <typename...> typename T>
constexpr bool is_type_specialization_of_v =
detail::is_type_specialization_of_v<U &, T>;
template <typename U, template <auto...> typename T>
constexpr bool is_value_specialization_of_v =
detail::is_value_specialization_of_v<U &, T>;
template <typename U, template <typename...> typename T>
constexpr auto is_specialization_of()
-> std::bool_constant<is_specialization_of_v<U, T>> {
return {};
}
template <typename U, template <auto...> typename T>
constexpr auto is_specialization_of()
-> std::bool_constant<is_value_specialization_of_v<U, T>> {
return {};
}
template <typename E>
constexpr bool is_scoped_enum_v =
std::is_enum_v<E> and not std::is_convertible_v<E, underlying_type_t<E>>;
template <typename E>
using is_scoped_enum = std::bool_constant<is_scoped_enum_v<E>>;
template <typename...> struct type_list {};
template <auto...> struct value_list {};
namespace detail {
template <typename L> struct for_each_t {
static_assert(always_false_v<L>,
"template_for_each must be called with a type list, "
"value_list, or std::integer_sequence");
};
template <template <typename...> typename L, typename... Ts>
struct for_each_t<L<Ts...>> {
template <typename F> constexpr auto operator()(F &&f) const {
(f.template operator()<Ts>(), ...);
}
};
template <template <auto...> typename L, auto... Vs>
struct for_each_t<L<Vs...>> {
template <typename F> constexpr auto operator()(F &&f) const {
(f.template operator()<Vs>(), ...);
}
};
template <template <typename X, X...> typename L, typename T, T... Vs>
struct for_each_t<L<T, Vs...>> {
template <typename F> constexpr auto operator()(F &&f) const {
(f.template operator()<Vs>(), ...);
}
};
} // namespace detail
template <typename L>
constexpr static auto template_for_each = detail::for_each_t<L>{};
namespace detail {
template <typename L> struct apply_sequence_t {
static_assert(always_false_v<L>,
"apply_sequence must be called with a type list, "
"value_list, or std::integer_sequence");
};
template <template <typename...> typename L, typename... Ts>
struct apply_sequence_t<L<Ts...>> {
template <typename F> constexpr auto operator()(F &&f) const {
return f.template operator()<Ts...>();
}
};
template <template <auto...> typename L, auto... Vs>
struct apply_sequence_t<L<Vs...>> {
template <typename F> constexpr auto operator()(F &&f) const {
return f.template operator()<Vs...>();
}
};
template <template <typename X, X...> typename L, typename T, T... Vs>
struct apply_sequence_t<L<T, Vs...>> {
template <typename F> constexpr auto operator()(F &&f) const {
return f.template operator()<Vs...>();
}
};
} // namespace detail
template <typename L>
constexpr static auto apply_sequence = detail::apply_sequence_t<L>{};
template <typename T, typename U>
constexpr bool is_same_unqualified_v =
std::is_same_v<remove_cvref_t<T>, remove_cvref_t<U>>;
namespace detail {
template <typename T> struct any_t;
template <typename T, typename... Ts> constexpr auto try_construct() -> T {
if constexpr (std::is_constructible_v<T, Ts...>) {
return T{Ts{}...};
} else if constexpr (sizeof...(Ts) < 10) {
return try_construct<T, Ts..., any_t<T>>();
} else {
throw;
}
}
template <typename T> struct any_t {
// NOLINTNEXTLINE(modernize-use-constraints)
template <typename U, std::enable_if_t<not std::is_same_v<T, U>, int> = 0>
// NOLINTNEXTLINE(google-explicit-constructor)
constexpr operator U() {
return try_construct<U>();
}
};
template <auto> using void_v = void;
template <typename T, typename = void> constexpr auto detect_structural = false;
template <typename T> constexpr auto detect_structural<T &, void> = true;
template <typename T>
constexpr auto detect_structural<T, void_v<try_construct<T>()>> = true;
} // namespace detail
template <typename T>
constexpr bool is_structural_v = detail::detect_structural<T>;
template <typename T, typename = void> constexpr auto is_cx_value_v = false;
template <typename T>
constexpr auto is_cx_value_v<T, std::void_t<typename T::cx_value_t>> = true;
#if __cplusplus >= 202002L
namespace detail {
template <typename T> struct shrinkwrap {
using is_shrinkwrapped = void;
using type = T;
};
template <typename T>
concept is_shrinkwrapped = requires { typename T::is_shrinkwrapped; };
template <typename T,
auto F = []()->auto (*)() -> shrinkwrap<T> { return nullptr; }>
CONSTEVAL auto shrink() {
return F;
}
template <typename T>
concept is_shrunk = requires(T const &t) {
{ t()() } -> is_shrinkwrapped;
};
template <typename T> CONSTEVAL auto maybe_expand() -> T;
template <is_shrunk T>
CONSTEVAL auto maybe_expand() -> typename decltype(T{}()())::type;
template <typename T> constexpr auto maybe_expand(T &&t) -> decltype(auto) {
return T(std::forward<T>(t));
}
template <is_shrunk T>
constexpr auto maybe_expand(T &&) ->
typename decltype(std::remove_cvref_t<T>{}()())::type {
using R = typename decltype(std::remove_cvref_t<T>{}()())::type;
static_assert(std::is_default_constructible_v<R>,
"expand(T) cannot default-construct the return value: maybe "
"use expand_t<T> instead?");
static_assert(
std::is_empty_v<R>,
"Danger! expand(T) would return a default-constructed but non-empty "
"object: use expand_t<T>{} instead if this is what you want");
return R{};
}
} // namespace detail
template <typename T> CONSTEVAL auto shrink() -> decltype(detail::shrink<T>()) {
static_assert(
always_false_v<T>,
"shrink<T>() should not be called outside an unevaluated context");
}
template <typename T>
CONSTEVAL auto shrink(T const &) -> decltype(detail::shrink<T>()) {
return {};
}
template <typename T>
CONSTEVAL auto expand() -> decltype(detail::maybe_expand<T>()) {
using R = decltype(detail::maybe_expand<T>());
static_assert(
always_false_v<R>,
"expand<T>() should not be called outside an unevaluated context");
}
template <typename T> constexpr auto expand(T &&t) -> decltype(auto) {
return detail::maybe_expand(std::forward<T>(t));
}
#else
template <typename T> CONSTEVAL auto shrink() -> T {
static_assert(
always_false_v<T>,
"shrink<T>() should not be called outside an unevaluated context");
}
template <typename T> constexpr auto shrink(T &&t) -> decltype(auto) {
return T(std::forward<T>(t));
}
template <typename T> CONSTEVAL auto expand() -> T {
static_assert(
always_false_v<T>,
"expand<T>() should not be called outside an unevaluated context");
}
template <typename T> constexpr auto expand(T &&t) -> decltype(auto) {
return T(std::forward<T>(t));
}
#endif
template <typename T> using shrink_t = decltype(shrink<T>());
template <typename T> using expand_t = decltype(expand<T>());
STDX_PRAGMA(diagnostic push)
#ifdef __clang__
STDX_PRAGMA(diagnostic ignored "-Wunknown-warning-option")
STDX_PRAGMA(diagnostic ignored "-Wc++26-extensions")
#endif
template <unsigned int N, typename... Ts>
using nth_t =
#if __cpp_pack_indexing >= 202311L
Ts...[N];
#elif __has_builtin(__type_pack_element)
__type_pack_element<N, Ts...>;
#else
boost::mp11::mp_at_c<type_list<Ts...>, N>;
#endif
STDX_PRAGMA(diagnostic pop)
#if __cplusplus >= 202002L
namespace detail {
template <auto V> struct value_wrapper {
constexpr static auto value = V;
};
} // namespace detail
STDX_PRAGMA(diagnostic push)
#ifdef __clang__
STDX_PRAGMA(diagnostic ignored "-Wunknown-warning-option")
STDX_PRAGMA(diagnostic ignored "-Wc++26-extensions")
#endif
template <unsigned int N, auto... Vs>
constexpr auto nth_v =
#if __cpp_pack_indexing >= 202311L
Vs...[N];
#else
boost::mp11::mp_at_c<type_list<detail::value_wrapper<Vs>...>, N>::value;
#endif
STDX_PRAGMA(diagnostic pop)
#endif
template <typename T, typename = void> constexpr auto is_complete_v = false;
template <typename T>
constexpr auto is_complete_v<T, detail::void_v<sizeof(T)>> = true;
template <typename T, typename U>
constexpr auto is_same_template_v = template_base<T>() == template_base<U>();
} // namespace v1
} // namespace stdx