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 : /// @}
|