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 : 87 0 : MathWrapper(MathWrapper&&) = default; 88 : 89 0 : MathWrapper() = delete; 90 0 : MathWrapper(const MathWrapper&) = delete; 91 0 : MathWrapper& operator=(const MathWrapper&) = delete; 92 0 : MathWrapper& operator=(MathWrapper&&) = delete; 93 : 94 : /// Convert MathWrapper wrapping a mutable value to one wrapping a 95 : /// const one. 96 : /// 97 : /// These methods will fail to compile if called on a MathWrapper 98 : /// wrapping a const value. The `to_const` method is useful because 99 : /// C++ fails to resolve the implicit conversion in many cases. 100 : /// @{ 101 1 : operator MathWrapper<const T>() const; 102 : 103 1 : MathWrapper<const T> to_const() const { 104 : return static_cast<MathWrapper<const T>>(*this); 105 : } 106 : /// @} 107 : 108 : private: 109 0 : Impl<> data_; 110 : }; 111 : 112 : /// \ingroup DataStructuresGroup 113 : /// A fundamental overload of the MathWrapper construction functions. 114 : /// 115 : /// Additional overloads can be implemented in terms of the 116 : /// fundamental overloads. 117 : /// @{ 118 1 : inline MathWrapper<double> make_math_wrapper( 119 : const gsl::not_null<double*> data) { 120 : return MathWrapper<double>(data); 121 : } 122 : 123 1 : inline MathWrapper<const double> make_math_wrapper(const double& data) { 124 : return MathWrapper<const double>(const_cast<double*>(&data)); 125 : } 126 : 127 1 : inline MathWrapper<std::complex<double>> make_math_wrapper( 128 : const gsl::not_null<std::complex<double>*> data) { 129 : return MathWrapper<std::complex<double>>(data); 130 : } 131 : 132 1 : inline MathWrapper<const std::complex<double>> make_math_wrapper( 133 : const std::complex<double>& data) { 134 : return MathWrapper<const std::complex<double>>( 135 : const_cast<std::complex<double>*>(&data)); 136 : } 137 : 138 1 : inline MathWrapper<DataVector> make_math_wrapper( 139 : const gsl::not_null<DataVector*> data) { 140 : DataVector referencing(data->data(), data->size()); 141 : return MathWrapper<DataVector>(&referencing); 142 : } 143 : 144 1 : inline MathWrapper<const DataVector> make_math_wrapper(const DataVector& data) { 145 : DataVector referencing(const_cast<double*>(data.data()), data.size()); 146 : return MathWrapper<const DataVector>(&referencing); 147 : } 148 : 149 1 : inline MathWrapper<ComplexDataVector> make_math_wrapper( 150 : const gsl::not_null<ComplexDataVector*> data) { 151 : ComplexDataVector referencing(data->data(), data->size()); 152 : return MathWrapper<ComplexDataVector>(&referencing); 153 : } 154 : 155 1 : inline MathWrapper<const ComplexDataVector> make_math_wrapper( 156 : const ComplexDataVector& data) { 157 : ComplexDataVector referencing(const_cast<std::complex<double>*>(data.data()), 158 : data.size()); 159 : return MathWrapper<const ComplexDataVector>(&referencing); 160 : } 161 : /// @} 162 : 163 : template <typename T> 164 : MathWrapper<T>::operator MathWrapper<const T>() const { 165 : return make_math_wrapper(data_.data); 166 : } 167 : 168 : template <typename T, size_t N> 169 0 : auto make_math_wrapper(const gsl::not_null<std::array<T, N>*> array) { 170 : DataVector referencing(array->data(), array->size()); 171 : return make_math_wrapper(&referencing); 172 : } 173 : 174 : template <typename T, size_t N> 175 0 : auto make_math_wrapper(const std::array<T, N>& array) { 176 : const DataVector referencing(const_cast<double*>(array.data()), array.size()); 177 : return make_math_wrapper(referencing); 178 : } 179 : 180 : /// \ingroup DataStructuresGroup 181 : /// The `value_type` for a MathWrapper wrapping `T`. 182 : template <typename T> 183 1 : using math_wrapper_type = typename decltype(make_math_wrapper( 184 : std::declval<tmpl::conditional_t<std::is_const_v<T>, const T&, 185 : gsl::not_null<T*>>>()))::value_type; 186 : 187 : /// \ingroup DataStructuresGroup 188 : /// A fundamental overload for owning type-erasure. Returns its argument 189 : /// unchanged. 190 : /// 191 : /// Additional overloads should always return the `math_wrapper_type` 192 : /// of their argument, and should be implemented to avoid allocations 193 : /// and copying whenever possible. 194 : /// @{ 195 1 : inline double into_math_wrapper_type(double&& data) { return data; } 196 : 197 1 : inline std::complex<double> into_math_wrapper_type( 198 : std::complex<double>&& data) { 199 : return data; 200 : } 201 : 202 1 : inline DataVector into_math_wrapper_type(DataVector&& data) { 203 : return std::move(data); 204 : } 205 : 206 1 : inline ComplexDataVector into_math_wrapper_type(ComplexDataVector&& data) { 207 : return std::move(data); 208 : } 209 : /// @}