| 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 | 17214 | 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 |