Line | Branch | Exec | Source |
---|---|---|---|
1 | // SPDX-FileCopyrightText: 2022-2023 Dennis Gläser <dennis.glaeser@iws.uni-stuttgart.de> | ||
2 | // SPDX-License-Identifier: MIT | ||
3 | /*! | ||
4 | * \file | ||
5 | * \ingroup Common | ||
6 | * \brief Common type traits | ||
7 | */ | ||
8 | #ifndef GRIDFORMAT_COMMON_TYPE_TRAITS_HPP_ | ||
9 | #define GRIDFORMAT_COMMON_TYPE_TRAITS_HPP_ | ||
10 | |||
11 | #include <span> | ||
12 | #include <array> | ||
13 | #include <ranges> | ||
14 | #include <variant> | ||
15 | #include <concepts> | ||
16 | #include <type_traits> | ||
17 | #include <algorithm> | ||
18 | |||
19 | #include <gridformat/common/exceptions.hpp> | ||
20 | |||
21 | namespace GridFormat { | ||
22 | |||
23 | //! \addtogroup Common | ||
24 | //! \{ | ||
25 | |||
26 | struct Automatic {}; | ||
27 | inline constexpr Automatic automatic; | ||
28 | template<typename T> | ||
29 | inline constexpr bool is_automatic = std::is_same_v<T, Automatic>; | ||
30 | |||
31 | struct None {}; | ||
32 | inline constexpr None none; | ||
33 | template<typename T> | ||
34 | inline constexpr bool is_none = std::is_same_v<std::remove_cvref_t<T>, None>; | ||
35 | |||
36 | template<typename T> | ||
37 | struct IsScalar : public std::false_type {}; | ||
38 | template<std::integral T> | ||
39 | struct IsScalar<T> : public std::true_type {}; | ||
40 | template<std::floating_point T> | ||
41 | struct IsScalar<T> : public std::true_type {}; | ||
42 | |||
43 | template<typename T> | ||
44 | inline constexpr bool is_scalar = IsScalar<T>::value; | ||
45 | |||
46 | |||
47 | template<typename T> | ||
48 | using LVReferenceOrValue = std::conditional_t<std::is_lvalue_reference_v<T>, T&, std::remove_cvref_t<T>>; | ||
49 | |||
50 | |||
51 | #ifndef DOXYGEN | ||
52 | namespace Detail { | ||
53 | |||
54 | template<std::integral auto v1> | ||
55 | inline constexpr bool all_equal() { | ||
56 | return true; | ||
57 | } | ||
58 | |||
59 | template<std::integral auto v1, std::integral auto v2, std::integral auto... vals> | ||
60 | inline constexpr bool all_equal() { | ||
61 | if constexpr (v1 == v2) | ||
62 | return all_equal<v2, vals...>(); | ||
63 | else | ||
64 | return false; | ||
65 | } | ||
66 | |||
67 | } // namespace Detail | ||
68 | #endif // DOXYGEN | ||
69 | |||
70 | template<std::integral auto v1, std::integral auto... vals> | ||
71 | inline constexpr bool all_equal = Detail::all_equal<v1, vals...>(); | ||
72 | |||
73 | |||
74 | #ifndef DOXYGEN | ||
75 | namespace Detail { | ||
76 | |||
77 | template<std::ranges::range R> | ||
78 | inline constexpr bool has_sub_range = std::ranges::range<std::ranges::range_value_t<R>>; | ||
79 | |||
80 | template<std::ranges::range R, typename Enable = void> | ||
81 | struct MDRangeValueType; | ||
82 | |||
83 | template<std::ranges::range R> | ||
84 | struct MDRangeValueType<R, std::enable_if_t<has_sub_range<R>>> { | ||
85 | using type = typename MDRangeValueType<std::ranges::range_value_t<R>>::type; | ||
86 | }; | ||
87 | |||
88 | template<std::ranges::range R> | ||
89 | struct MDRangeValueType<R, std::enable_if_t<!has_sub_range<R>>> { | ||
90 | using type = std::ranges::range_value_t<R>; | ||
91 | }; | ||
92 | |||
93 | template<std::ranges::range R, typename Enable = void> | ||
94 | struct MDRangeReferenceType; | ||
95 | |||
96 | template<std::ranges::range R> | ||
97 | struct MDRangeReferenceType<R, std::enable_if_t<has_sub_range<R>>> { | ||
98 | using type = typename MDRangeReferenceType<std::ranges::range_reference_t<R>>::type; | ||
99 | }; | ||
100 | |||
101 | template<std::ranges::range R> | ||
102 | struct MDRangeReferenceType<R, std::enable_if_t<!has_sub_range<R>>> { | ||
103 | using type = std::ranges::range_reference_t<R>; | ||
104 | }; | ||
105 | |||
106 | } // namespace Detail | ||
107 | #endif // DOXYGEN | ||
108 | |||
109 | template<std::ranges::range R> | ||
110 | using MDRangeValueType = typename Detail::MDRangeValueType<R>::type; | ||
111 | |||
112 | template<std::ranges::range R> | ||
113 | using MDRangeReferenceType = typename Detail::MDRangeReferenceType<R>::type; | ||
114 | |||
115 | template<std::ranges::range R> requires(is_scalar<MDRangeValueType<R>>) | ||
116 | using MDRangeScalar = MDRangeValueType<R>; | ||
117 | |||
118 | template<std::ranges::range R> | ||
119 | inline constexpr bool has_sub_range = Detail::has_sub_range<R>; | ||
120 | |||
121 | |||
122 | #ifndef DOXYGEN | ||
123 | namespace Detail { | ||
124 | |||
125 | template<std::ranges::range R, typename Enable = void> | ||
126 | struct MDRangeDimension; | ||
127 | |||
128 | template<std::ranges::range R> | ||
129 | struct MDRangeDimension<R, std::enable_if_t<Detail::has_sub_range<R>>> { | ||
130 | static constexpr std::size_t value = 1 + MDRangeDimension<std::ranges::range_value_t<R>>::value; | ||
131 | }; | ||
132 | |||
133 | template<std::ranges::range R> | ||
134 | struct MDRangeDimension<R, std::enable_if_t<!Detail::has_sub_range<R>>> { | ||
135 | static constexpr std::size_t value = 1; | ||
136 | }; | ||
137 | |||
138 | template<std::size_t i, std::size_t depth, std::ranges::range R> | ||
139 | struct MDRangeValueTypeAt; | ||
140 | |||
141 | template<std::size_t i, std::size_t depth, std::ranges::range R> requires(i < depth) | ||
142 | struct MDRangeValueTypeAt<i, depth, R> { | ||
143 | using type = typename MDRangeValueTypeAt<i+1, depth, std::ranges::range_value_t<R>>::type; | ||
144 | }; | ||
145 | |||
146 | template<std::size_t i, std::size_t depth, std::ranges::range R> requires(i == depth) | ||
147 | struct MDRangeValueTypeAt<i, depth, R> { | ||
148 | using type = std::ranges::range_value_t<R>; | ||
149 | }; | ||
150 | |||
151 | } // end namespace Detail | ||
152 | #endif // DOXYGEN | ||
153 | |||
154 | template<std::ranges::range R> | ||
155 | inline constexpr std::size_t mdrange_dimension = Detail::MDRangeDimension<R>::value; | ||
156 | |||
157 | template<std::size_t depth, std::ranges::range R> requires(depth < mdrange_dimension<R>) | ||
158 | using MDRangeValueTypeAt = typename Detail::MDRangeValueTypeAt<0, depth, R>::type; | ||
159 | |||
160 | |||
161 | #ifndef DOXYGEN | ||
162 | namespace Detail { | ||
163 | |||
164 | template<typename T, std::size_t s = sizeof(T)> | ||
165 | std::false_type is_incomplete(T*); | ||
166 | std::true_type is_incomplete(...); | ||
167 | |||
168 | } // end namespace Detail | ||
169 | #endif // DOXYGEN | ||
170 | |||
171 | template<typename T> | ||
172 | inline constexpr bool is_incomplete = decltype(Detail::is_incomplete(std::declval<T*>()))::value; | ||
173 | |||
174 | template<typename T> | ||
175 | inline constexpr bool is_complete = !is_incomplete<T>; | ||
176 | |||
177 | template<typename T, typename... Types> | ||
178 | inline constexpr bool is_any_of = (... or std::same_as<T, Types>); | ||
179 | |||
180 | |||
181 | #ifndef DOXYGEN | ||
182 | namespace Detail { | ||
183 | |||
184 | template<typename T, typename... Types> | ||
185 | struct UniqueVariant { | ||
186 | using type = std::conditional_t< | ||
187 | is_any_of<T, Types...>, | ||
188 | typename UniqueVariant<Types...>::type, | ||
189 | typename UniqueVariant<std::tuple<T>, Types...>::type | ||
190 | >; | ||
191 | }; | ||
192 | |||
193 | template<typename T> | ||
194 | struct UniqueVariant<T> : public std::type_identity<std::variant<T>> {}; | ||
195 | |||
196 | template<typename... Uniques> | ||
197 | struct UniqueVariant<std::tuple<Uniques...>> : public std::type_identity<std::variant<Uniques...>> {}; | ||
198 | |||
199 | template<typename... Uniques, typename T, typename... Types> | ||
200 | struct UniqueVariant<std::tuple<Uniques...>, T, Types...> { | ||
201 | using type = std::conditional_t< | ||
202 | is_any_of<T, Uniques...>, | ||
203 | typename UniqueVariant<std::tuple<Uniques...>, Types...>::type, | ||
204 | typename UniqueVariant<std::tuple<Uniques..., T>, Types...>::type | ||
205 | >; | ||
206 | }; | ||
207 | |||
208 | } // end namespace Detail | ||
209 | #endif // DOXYGEN | ||
210 | |||
211 | template<typename T, typename... Types> | ||
212 | using UniqueVariant = typename Detail::UniqueVariant<T, Types...>::type; | ||
213 | |||
214 | |||
215 | #ifndef DOXYGEN | ||
216 | namespace Detail { | ||
217 | |||
218 | template<typename T> | ||
219 | struct VariantOrClosure; | ||
220 | template<typename T, typename... Ts> | ||
221 | struct VariantOrClosure<std::variant<T, Ts...>> { | ||
222 | template<typename... _Ts> | ||
223 | using Variant = GridFormat::UniqueVariant<T, Ts..., _Ts...>; | ||
224 | }; | ||
225 | |||
226 | template<typename T, typename... Types> | ||
227 | struct VariantOr { | ||
228 | using type = typename VariantOrClosure<T>::template Variant<Types...>; | ||
229 | }; | ||
230 | |||
231 | } // namespace Detail | ||
232 | #endif // DOXYGEN | ||
233 | |||
234 | template<typename T, typename... Types> | ||
235 | using ExtendedVariant = typename Detail::VariantOr<T, Types...>::type; | ||
236 | |||
237 | |||
238 | #ifndef DOXYGEN | ||
239 | namespace Detail { | ||
240 | |||
241 | template<typename T> | ||
242 | struct MergedVariant; | ||
243 | template<typename... Ts> | ||
244 | struct MergedVariant<std::variant<Ts...>> { | ||
245 | template<typename T> | ||
246 | struct Closure; | ||
247 | template<typename... _Ts> | ||
248 | struct Closure<std::variant<_Ts...>> { | ||
249 | using type = GridFormat::ExtendedVariant<std::variant<Ts...>, _Ts...>; | ||
250 | }; | ||
251 | |||
252 | template<typename V2> | ||
253 | using Variant = typename Closure<V2>::type; | ||
254 | }; | ||
255 | |||
256 | } // namespace Detail | ||
257 | #endif // DOXYGEN | ||
258 | |||
259 | template<typename V1, typename V2> | ||
260 | using MergedVariant = typename Detail::MergedVariant<V1>::template Variant<V2>; | ||
261 | |||
262 | |||
263 | #ifndef DOXYGEN | ||
264 | namespace Detail { | ||
265 | template<typename Remove, typename Variant> | ||
266 | struct VariantWithoutSingleType; | ||
267 | template<typename Remove, typename T, typename... Ts> | ||
268 | struct VariantWithoutSingleType<Remove, std::variant<T, Ts...>> { | ||
269 | using type = std::conditional_t< | ||
270 | std::is_same_v<Remove, T>, | ||
271 | std::conditional_t< | ||
272 | sizeof...(Ts) == 0, | ||
273 | std::variant<>, | ||
274 | std::variant<Ts...> | ||
275 | >, | ||
276 | GridFormat::MergedVariant< | ||
277 | std::variant<T>, | ||
278 | typename VariantWithoutSingleType<Remove, std::variant<Ts...>>::type | ||
279 | > | ||
280 | >; | ||
281 | }; | ||
282 | template<typename Remove> | ||
283 | struct VariantWithoutSingleType<Remove, std::variant<>> : public std::type_identity<std::variant<>> {}; | ||
284 | |||
285 | template<typename Variant, typename... Remove> | ||
286 | struct VariantWithout; | ||
287 | template<typename... Ts> | ||
288 | struct VariantWithout<std::variant<Ts...>> : public std::type_identity<std::variant<Ts...>> {}; | ||
289 | template<typename... Ts, typename R, typename... Remove> | ||
290 | struct VariantWithout<std::variant<Ts...>, R, Remove...> { | ||
291 | using type = typename VariantWithout< | ||
292 | typename VariantWithoutSingleType<R, std::variant<Ts...>>::type, | ||
293 | Remove... | ||
294 | >::type; | ||
295 | }; | ||
296 | |||
297 | } // namespace Detail | ||
298 | #endif // DOXYGEN | ||
299 | |||
300 | template<typename T, typename... Remove> | ||
301 | using ReducedVariant = typename Detail::VariantWithout<T, Remove...>::type; | ||
302 | |||
303 | |||
304 | #ifndef DOXYGEN | ||
305 | namespace Detail { | ||
306 | |||
307 | template<typename T> | ||
308 | struct FieldScalar; | ||
309 | |||
310 | template<typename T> requires(is_scalar<T>) | ||
311 | struct FieldScalar<T> : public std::type_identity<T> {}; | ||
312 | |||
313 | template<std::ranges::range R> requires(is_scalar<MDRangeScalar<R>>) | ||
314 | struct FieldScalar<R> : public std::type_identity<MDRangeScalar<R>> {}; | ||
315 | |||
316 | } // end namespace Detail | ||
317 | #endif // DOXYGEN | ||
318 | |||
319 | template<typename T> | ||
320 | using FieldScalar = typename Detail::FieldScalar<T>::type; | ||
321 | |||
322 | |||
323 | #ifndef DOXYGEN | ||
324 | namespace Detail { | ||
325 | |||
326 | template<typename T> | ||
327 | concept HasStaticSizeFunction = requires { | ||
328 | { T::size() } -> std::convertible_to<std::size_t>; | ||
329 | { std::integral_constant<std::size_t, T::size()>{} }; | ||
330 | }; | ||
331 | |||
332 | template<typename T> | ||
333 | concept HasStaticSizeMember = requires { | ||
334 | { T::size } -> std::convertible_to<std::size_t>; | ||
335 | { std::integral_constant<std::size_t, T::size>{} }; | ||
336 | }; | ||
337 | |||
338 | } // namespace Detail | ||
339 | #endif // DOXYGEN | ||
340 | |||
341 | namespace Traits { | ||
342 | |||
343 | template<typename T> | ||
344 | struct StaticSize; | ||
345 | template<typename T, std::size_t s> | ||
346 | struct StaticSize<std::array<T, s>> { | ||
347 | static constexpr std::size_t value = s; | ||
348 | }; | ||
349 | template<typename T, std::size_t s> requires(s != std::dynamic_extent) | ||
350 | struct StaticSize<std::span<T, s>> { | ||
351 | static constexpr std::size_t value = s; | ||
352 | }; | ||
353 | template<typename T, std::size_t s> | ||
354 | struct StaticSize<T[s]> { | ||
355 | static constexpr std::size_t value = s; | ||
356 | }; | ||
357 | template<Detail::HasStaticSizeMember T> | ||
358 | struct StaticSize<T> { | ||
359 | static constexpr std::size_t value = T::size; | ||
360 | }; | ||
361 | template<Detail::HasStaticSizeFunction T> | ||
362 | struct StaticSize<T> { | ||
363 | static constexpr std::size_t value = T::size(); | ||
364 | }; | ||
365 | template<typename T> requires(is_complete<StaticSize<std::remove_const_t<T>>>) | ||
366 | struct StaticSize<T&> : public StaticSize<std::remove_const_t<T>> {}; | ||
367 | |||
368 | } // namespace Traits | ||
369 | |||
370 | template<typename T> | ||
371 | inline constexpr std::size_t static_size = Traits::StaticSize<T>::value; | ||
372 | |||
373 | template<typename T> | ||
374 | inline constexpr bool has_static_size = is_complete<Traits::StaticSize<T>>; | ||
375 | |||
376 | |||
377 | #ifndef DOXYGEN | ||
378 | namespace Detail { | ||
379 | template<std::ranges::range T> requires(has_static_size<T>) | ||
380 | 538 | constexpr T make_range(std::ranges::range_value_t<T> value) { | |
381 | T result; | ||
382 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
538 | std::ranges::fill(result, value); |
383 | 538 | return result; | |
384 | } | ||
385 | } // namespace Detail | ||
386 | #endif // DOXYGEN | ||
387 | |||
388 | template<typename T> | ||
389 | struct DefaultValue; | ||
390 | |||
391 | template<typename T> requires(is_scalar<T>) | ||
392 | struct DefaultValue<T> { | ||
393 | 17137 | static constexpr T get() { return T{0}; } | |
394 | }; | ||
395 | |||
396 | template<std::ranges::range T> requires(has_static_size<T>) | ||
397 | struct DefaultValue<T> { | ||
398 | 538 | static constexpr T get() { return Detail::make_range<T>(DefaultValue<std::ranges::range_value_t<T>>::get()); } | |
399 | }; | ||
400 | |||
401 | // only available if the default value trait provides a constexpr get function | ||
402 | template<typename T> | ||
403 | inline constexpr T default_value = DefaultValue<T>::get(); | ||
404 | |||
405 | //! \} group Common | ||
406 | |||
407 | } // end namespace GridFormat | ||
408 | |||
409 | #endif // GRIDFORMAT_COMMON_TYPE_TRAITS_HPP_ | ||
410 |