Line data Source code
1 0 : // Distributed under the MIT License. 2 : // See LICENSE.txt for details. 3 : 4 : #pragma once 5 : 6 : #include <array> 7 : #include <complex> 8 : #include <cstddef> 9 : #include <type_traits> 10 : #include <utility> 11 : 12 : #include "DataStructures/ComplexDataVector.hpp" 13 : #include "DataStructures/DataVector.hpp" 14 : #include "Utilities/Gsl.hpp" 15 : #include "Utilities/TMPL.hpp" 16 : 17 : /// \ingroup DataStructuresGroup 18 : /// A comma-separated list of valid template arguments to MathWrapper. 19 : /// Useful for explicit instantiations. 20 : /// 21 : /// \snippet Helpers/DataStructures/MathWrapperDetail.cpp MATH_WRAPPER_TYPES_instantiate 22 1 : #define MATH_WRAPPER_TYPES \ 23 : double, std::complex<double>, DataVector, ComplexDataVector 24 : 25 : /// \ingroup DataStructuresGroup 26 : /// Type-erased data for performing math on. 27 : /// 28 : /// This class can only be instantiated with possibly const-qualified 29 : /// types from \ref MATH_WRAPPER_TYPES, which can be assumed to 30 : /// support the mathematical operations of a linear-algebra vector. 31 : /// Instances of this class with those template arguments can be 32 : /// created using overloads of `make_math_wrapper` (passing a `const 33 : /// T&` for const versions and a `gsl::not_null<T*>` for mutable 34 : /// versions). Other data structures (such as `Variables`) can add 35 : /// additional overloads implemented on top of these basic ones. 36 : /// 37 : /// \snippet Test_MathWrapper.cpp MathWrapper 38 : template <typename T> 39 1 : class MathWrapper { 40 : private: 41 0 : using MutableT = std::remove_const_t<T>; 42 : 43 : static_assert( 44 : tmpl::list_contains_v<tmpl::list<MATH_WRAPPER_TYPES>, MutableT>); 45 : 46 : template <typename U = T, 47 : bool IsVector = 48 : not(std::is_same_v<std::decay_t<T>, double> or 49 : std::is_same_v<std::decay_t<T>, std::complex<double>>), 50 : bool IsConst = std::is_const_v<T>> 51 0 : struct Impl { 52 0 : using scalar_type = std::remove_const_t<U>; 53 0 : T& data; 54 0 : Impl(const gsl::not_null<T*> data_in) : data(*data_in) {} 55 : }; 56 : 57 : template <typename U> 58 0 : struct Impl<U, true, false> { 59 0 : using scalar_type = typename U::value_type; 60 : // NOLINTNEXTLINE(spectre-mutable) 61 0 : mutable T data; 62 0 : Impl(const gsl::not_null<T*> data_in) : data(std::move(*data_in)) {} 63 : }; 64 : 65 : template <typename U> 66 0 : struct Impl<U, true, true> { 67 0 : using scalar_type = typename U::value_type; 68 0 : const T data; 69 : // Need to invoke the move-from-mutable constructor on DataVector, etc. 70 0 : Impl(const gsl::not_null<MutableT*> data_in) : data(std::move(*data_in)) {} 71 : }; 72 : 73 0 : explicit MathWrapper(const gsl::not_null<MutableT*> data) : data_(data) {} 74 : 75 0 : friend MathWrapper<T> make_math_wrapper( 76 : tmpl::conditional_t<std::is_const_v<T>, T&, gsl::not_null<T*>>); 77 : 78 : public: 79 : /// The class's template parameter. 80 1 : using value_type = T; 81 : /// Scalar type for linear-algebra operations. Either double or 82 : /// std::complex<double>. 83 1 : using scalar_type = typename Impl<>::scalar_type; 84 : 85 0 : T& operator*() const { return data_.data; } 86 0 : T* operator->() const { return &data_.data; } 87 : 88 0 : MathWrapper(MathWrapper&&) = default; 89 : 90 0 : MathWrapper() = delete; 91 0 : MathWrapper(const MathWrapper&) = delete; 92 0 : MathWrapper& operator=(const MathWrapper&) = delete; 93 0 : MathWrapper& operator=(MathWrapper&&) = delete; 94 : 95 : /// Convert MathWrapper wrapping a mutable value to one wrapping a 96 : /// const one. 97 : /// 98 : /// These methods will fail to compile if called on a MathWrapper 99 : /// wrapping a const value. The `to_const` method is useful because 100 : /// C++ fails to resolve the implicit conversion in many cases. 101 : /// @{ 102 1 : operator MathWrapper<const T>() const; 103 : 104 1 : MathWrapper<const T> to_const() const { 105 : return static_cast<MathWrapper<const T>>(*this); 106 : } 107 : /// @} 108 : 109 : private: 110 0 : Impl<> data_; 111 : }; 112 : 113 : /// \ingroup DataStructuresGroup 114 : /// A fundamental overload of the MathWrapper construction functions. 115 : /// 116 : /// Additional overloads can be implemented in terms of the 117 : /// fundamental overloads. 118 : /// @{ 119 1 : inline MathWrapper<double> make_math_wrapper( 120 : const gsl::not_null<double*> data) { 121 : return MathWrapper<double>(data); 122 : } 123 : 124 1 : inline MathWrapper<const double> make_math_wrapper(const double& data) { 125 : return MathWrapper<const double>(const_cast<double*>(&data)); 126 : } 127 : 128 1 : inline MathWrapper<std::complex<double>> make_math_wrapper( 129 : const gsl::not_null<std::complex<double>*> data) { 130 : return MathWrapper<std::complex<double>>(data); 131 : } 132 : 133 1 : inline MathWrapper<const std::complex<double>> make_math_wrapper( 134 : const std::complex<double>& data) { 135 : return MathWrapper<const std::complex<double>>( 136 : const_cast<std::complex<double>*>(&data)); 137 : } 138 : 139 1 : inline MathWrapper<DataVector> make_math_wrapper( 140 : const gsl::not_null<DataVector*> data) { 141 : if (UNLIKELY(data->size() == 0)) { 142 : DataVector empty{}; 143 : return MathWrapper<DataVector>(&empty); 144 : } 145 : DataVector referencing(data->data(), data->size()); 146 : return MathWrapper<DataVector>(&referencing); 147 : } 148 : 149 1 : inline MathWrapper<const DataVector> make_math_wrapper(const DataVector& data) { 150 : if (UNLIKELY(data.size() == 0)) { 151 : DataVector empty{}; 152 : return MathWrapper<const DataVector>(&empty); 153 : } 154 : DataVector referencing(const_cast<double*>(data.data()), data.size()); 155 : return MathWrapper<const DataVector>(&referencing); 156 : } 157 : 158 1 : inline MathWrapper<ComplexDataVector> make_math_wrapper( 159 : const gsl::not_null<ComplexDataVector*> data) { 160 : if (UNLIKELY(data->size() == 0)) { 161 : ComplexDataVector empty{}; 162 : return MathWrapper<ComplexDataVector>(&empty); 163 : } 164 : ComplexDataVector referencing(data->data(), data->size()); 165 : return MathWrapper<ComplexDataVector>(&referencing); 166 : } 167 : 168 1 : inline MathWrapper<const ComplexDataVector> make_math_wrapper( 169 : const ComplexDataVector& data) { 170 : if (UNLIKELY(data.size() == 0)) { 171 : ComplexDataVector empty{}; 172 : return MathWrapper<const ComplexDataVector>(&empty); 173 : } 174 : ComplexDataVector referencing(const_cast<std::complex<double>*>(data.data()), 175 : data.size()); 176 : return MathWrapper<const ComplexDataVector>(&referencing); 177 : } 178 : /// @} 179 : 180 : template <typename T> 181 : MathWrapper<T>::operator MathWrapper<const T>() const { 182 : return make_math_wrapper(data_.data); 183 : } 184 : 185 : template <typename T, size_t N> 186 0 : auto make_math_wrapper(const gsl::not_null<std::array<T, N>*> array) { 187 : DataVector referencing(array->data(), array->size()); 188 : return make_math_wrapper(&referencing); 189 : } 190 : 191 : template <typename T, size_t N> 192 0 : auto make_math_wrapper(const std::array<T, N>& array) { 193 : const DataVector referencing(const_cast<double*>(array.data()), array.size()); 194 : return make_math_wrapper(referencing); 195 : } 196 : 197 : /// \ingroup DataStructuresGroup 198 : /// The `value_type` for a MathWrapper wrapping `T`. 199 : template <typename T> 200 1 : using math_wrapper_type = typename decltype(make_math_wrapper( 201 : std::declval<tmpl::conditional_t<std::is_const_v<T>, const T&, 202 : gsl::not_null<T*>>>()))::value_type; 203 : 204 : /// \ingroup DataStructuresGroup 205 : /// A fundamental overload for owning type-erasure. Returns its argument 206 : /// unchanged. 207 : /// 208 : /// Additional overloads should always return the `math_wrapper_type` 209 : /// of their argument, and should be implemented to avoid allocations 210 : /// and copying whenever possible. 211 : /// @{ 212 1 : inline double into_math_wrapper_type(double&& data) { return data; } 213 : 214 1 : inline std::complex<double> into_math_wrapper_type( 215 : std::complex<double>&& data) { 216 : return data; 217 : } 218 : 219 1 : inline DataVector into_math_wrapper_type(DataVector&& data) { 220 : return std::move(data); 221 : } 222 : 223 1 : inline ComplexDataVector into_math_wrapper_type(ComplexDataVector&& data) { 224 : return std::move(data); 225 : } 226 : /// @}