Line data Source code
1 0 : // Distributed under the MIT License. 2 : // See LICENSE.txt for details. 3 : 4 : #pragma once 5 : 6 : #include <complex> 7 : #include <cstddef> 8 : #include <type_traits> 9 : 10 : #include "Utilities/ForceInline.hpp" 11 : #include "Utilities/Gsl.hpp" 12 : #include "Utilities/TypeTraits/IsComplexOfFundamental.hpp" 13 : 14 : /// \ingroup UtilitiesGroup 15 : /// \brief Callable struct which retrieves the `t.size()` for operand `t`. This 16 : /// will cause a compiler error if no such function exists. 17 1 : struct GetContainerSize { 18 : template <typename T> 19 0 : SPECTRE_ALWAYS_INLINE constexpr decltype(auto) operator()(const T& t) const { 20 : return t.size(); 21 : } 22 : }; 23 : 24 : /// \ingroup UtilitiesGroup 25 : /// \brief Callable struct for the subscript operator. Returns `t[i]` 26 1 : struct GetContainerElement { 27 : template <typename T> 28 0 : SPECTRE_ALWAYS_INLINE constexpr decltype(auto) operator()( 29 : T& t, const size_t i) const { 30 : return t[i]; 31 : } 32 : }; 33 : 34 : namespace ContainerHelpers_detail { 35 : // implementation struct for get_element and get_size 36 : template <bool IsFundamentalOrComplexOfFundamental> 37 : struct ContainerImpls; 38 : 39 : template <> 40 : struct ContainerImpls<true> { 41 : template <typename T, typename SubscriptFunction> 42 : static SPECTRE_ALWAYS_INLINE constexpr decltype(auto) get_element( 43 : T& t, const size_t /*i*/, const SubscriptFunction /*at*/) { 44 : return t; 45 : } 46 : 47 : template <typename T, typename SizeFunction> 48 : static SPECTRE_ALWAYS_INLINE constexpr size_t get_size( 49 : const T& /*t*/, const SizeFunction /*size*/) { 50 : return 1; 51 : } 52 : }; 53 : 54 : template <> 55 : struct ContainerImpls<false> { 56 : template <typename T, typename SubscriptFunction> 57 : static SPECTRE_ALWAYS_INLINE constexpr decltype(auto) get_element( 58 : T& t, const size_t i, SubscriptFunction at) { 59 : return at(t, i); 60 : } 61 : 62 : template <typename T, typename SizeFunction> 63 : static SPECTRE_ALWAYS_INLINE constexpr decltype(auto) get_size( 64 : const T& t, SizeFunction size) { 65 : return size(t); 66 : } 67 : }; 68 : } // namespace ContainerHelpers_detail 69 : 70 : /*! 71 : * \ingroup UtilitiesGroup 72 : * \brief Returns the `i`th element if `T` has a subscript operator, otherwise 73 : * if `T` is fundamental or a `std::complex` of a fundamental type, returns 74 : * `t`. 75 : * 76 : * \details This function also optionally takes the user-defined subscript 77 : * function `at`, which can be used to specify a custom indexing function. For 78 : * instance, for a type which is a `std::array` of a `std::array`s, the indexing 79 : * function could be the below callable struct: 80 : * \snippet Test_ContainerHelpers.cpp get_element_example_indexing_callable 81 : * 82 : * which would index the data structure in a manner in which the outer array 83 : * index varies fastest. The indexing function must take as arguments the 84 : * applicable container and a `size_t` index, in that order. This follows the 85 : * convention of `gsl::at`. 86 : * \note `std::complex` are regarded as non-indexable (despite a predictable 87 : * memory layout), so this function acts as the identity on `std::complex` of 88 : * fundamental types 89 : */ 90 : template <typename T, typename SubscriptFunction = GetContainerElement> 91 1 : SPECTRE_ALWAYS_INLINE constexpr decltype(auto) get_element( 92 : T& t, const size_t i, SubscriptFunction at = GetContainerElement{}) { 93 : return ContainerHelpers_detail::ContainerImpls<( 94 : tt::is_complex_of_fundamental_v<std::remove_cv_t<T>> or 95 : std::is_fundamental_v<std::remove_cv_t<T>>)>::get_element(t, i, at); 96 : } 97 : 98 : /*! 99 : * \ingroup UtilitiesGroup 100 : * \brief Retrieve the size of `t` if `t.size()` is a valid expression, 101 : * otherwise if `T` is fundamental or a `std::complex` of a fundamental type, 102 : * returns 1. 103 : * 104 : * \details This function also optionally takes the user-defined `size` 105 : * function, which can be used to specify a custom size function. For 106 : * instance, for a type which is a `std::array` of a `std::array`, the size 107 : * function could be the below callable struct: 108 : * \snippet Test_ContainerHelpers.cpp get_size_example_size_callable 109 : * 110 : * The `size` function must take the single argument of the applicable 111 : * container, and should return a `size_t`. This follows the convention of 112 : * `std::size()` as of C++17. 113 : * \note `std::complex` are regarded as non-indexable (despite a predictable 114 : * memory layout), so this function will return 1 for a `std::complex` of a 115 : * fundamental type 116 : */ 117 : template <typename T, typename SizeFunction = GetContainerSize> 118 1 : SPECTRE_ALWAYS_INLINE constexpr decltype(auto) get_size( 119 : const T& t, SizeFunction size = GetContainerSize{}) { 120 : return ContainerHelpers_detail::ContainerImpls<( 121 : tt::is_complex_of_fundamental_v<std::remove_cv_t<T>> or 122 : std::is_fundamental_v<std::remove_cv_t<T>>)>::get_size(t, size); 123 : } 124 : 125 : /// Fall-back that allows using `min(x)` where `x` can be a vector or a double 126 1 : SPECTRE_ALWAYS_INLINE constexpr double min(const double val) { return val; } 127 : 128 : /// Fall-back that allows using `max(x)` where `x` can be a vector or a double 129 1 : SPECTRE_ALWAYS_INLINE constexpr double max(const double val) { return val; }