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