1 #pragma once
2
3 #include "objects/containers/ArrayView.h"
4 #include "objects/wrappers/AlignedStorage.h"
5 #include <initializer_list>
6
7 NAMESPACE_SPH_BEGIN
8
9 const struct EmptyArray {
10 } EMPTY_ARRAY;
11
12 /// \brief Array with fixed number of allocated elements.
13 ///
14 /// Number of actually created elements can be different and it can also be changed; elements can be created
15 /// and destroyed, although it is not possible to construct more elements than the maximum allocated number,
16 /// determined by template parameter N or function \ref maxSize. The array is intentionally non-copyable to
17 /// avoid accidentaly copying values, where move or pass-by-reference is possible.
18 template <typename T, int N, typename TCounter = Size>
19 class StaticArray {
20 private:
21 AlignedStorage<T> data[N];
22 TCounter actSize;
23
24 using StorageType = WrapReference<T>;
25
26 public:
27 /// \brief Default constructor, calls default constructor on all elements.
StaticArray()28 StaticArray() {
29 if (!std::is_trivially_default_constructible<T>::value) {
30 for (TCounter i = 0; i < N; ++i) {
31 data[i].emplace();
32 }
33 }
34 actSize = N;
35 }
36
37 /// \brief Initialize an empty array.
38 ///
39 /// This effectively creates an empty stack; all N elements are allocated, but none is constructed and
40 /// size of the array is zero.
StaticArray(const EmptyArray &)41 StaticArray(const EmptyArray&) {
42 actSize = 0;
43 }
44
45 /// \brief Initialize using initializer_list.
46 ///
47 /// The size of the list must be smaller or equal to N. All N elements are all allocated, elements are
48 /// constructed from initializer_list using copy constructor.
StaticArray(std::initializer_list<StorageType> list)49 StaticArray(std::initializer_list<StorageType> list) {
50 actSize = 0;
51 SPH_ASSERT(list.size() <= N);
52 for (auto& i : list) {
53 data[actSize++].emplace(i);
54 }
55 }
56
57 StaticArray(const StaticArray& other) = delete;
58
StaticArray(StaticArray && other)59 StaticArray(StaticArray&& other) {
60 actSize = 0;
61 for (TCounter i = 0; i < other.size(); ++i) {
62 // move if it contains values, just copy if it contains references
63 data[actSize++].emplace(std::forward<T>(other[i]));
64 }
65 }
66
67 /// \brief Destructor, destroys all constructed elements in the array.
~StaticArray()68 ~StaticArray() {
69 if (!std::is_trivially_destructible<T>::value) {
70 for (TCounter i = 0; i < actSize; ++i) {
71 data[i].destroy();
72 }
73 }
74 actSize = 0;
75 }
76
77 StaticArray& operator=(const StaticArray& other) = delete;
78
79 /// \brief Move operator for arrays holding default-constructible types.
80 ///
81 /// Resizes array and moves all elements of the rhs array.
82 template <typename U, typename = std::enable_if_t<std::is_default_constructible<T>::value, U>>
83 StaticArray& operator=(StaticArray<U, N>&& other) {
84 this->resize(other.size());
85 for (TCounter i = 0; i < other.size(); ++i) {
86 // move if it contains values, just copy if it contains references
87 (*this)[i] = std::forward<T>(other[i]);
88 }
89 return *this;
90 }
91
92 /// \brief Special assignment operator for array of references on left-hand side.
93 ///
94 /// Can be used similarly to std::tie; a function may return multiple values by wrapping them into
95 /// StaticArray<T>, these values can be then saved individual variables by wrapping the variables into
96 /// \ref StaticArray<T&>. A big advantage over tuple is the option to return a variable number of elements
97 /// (i.e. number of arguments is not known at compile time).
98 template <typename U, int M, typename = std::enable_if_t<std::is_lvalue_reference<T>::value, U>>
99 StaticArray& operator=(StaticArray<U, M>&& other) {
100 SPH_ASSERT(this->size() == other.size());
101 for (TCounter i = 0; i < other.size(); ++i) {
102 (*this)[i] = std::forward<U>(other[i]);
103 }
104 return *this;
105 }
106
107 /// \brief Clones the array, calling copy constructor on all elements.
108 ///
109 /// The size of the cloned array corresponds to current size of this array.
clone()110 StaticArray clone() const {
111 StaticArray cloned(EMPTY_ARRAY);
112 for (TCounter i = 0; i < actSize; ++i) {
113 cloned.push(data[i].get());
114 }
115 return cloned;
116 }
117
118 /// \brief Assigns a value to all constructed elements of the array.
119 ///
120 /// Does not resize the array.
fill(const T & value)121 void fill(const T& value) {
122 for (TCounter i = 0; i < actSize; ++i) {
123 data[i].get() = value;
124 }
125 }
126
127 /// \brief Returns the element with given index.
128 INLINE T& operator[](const TCounter idx) noexcept {
129 SPH_ASSERT(idx >= 0 && idx < actSize, idx, actSize);
130 return data[idx].get();
131 }
132
133 /// \brief Returns the element with given index.
134 INLINE const T& operator[](const TCounter idx) const noexcept {
135 SPH_ASSERT(idx >= 0 && idx < actSize, idx, actSize);
136 return data[idx].get();
137 }
138
139 /// \brief Returns the maximum allowed size of the array.
maxSize()140 INLINE constexpr TCounter maxSize() const noexcept {
141 return N;
142 }
143
144 /// \brief Returns the current size of the array (number of constructed elements).
145 ///
146 /// Can be between 0 and N.
size()147 INLINE TCounter size() const {
148 return actSize;
149 }
150
151 /// \brief Return true if the array is empty.
152 ///
153 /// Depends only on number of constructed elements, not allocated size.
empty()154 INLINE bool empty() const {
155 return actSize == 0;
156 }
157
158 /// \brief Inserts a value to the end of the array using copy/move constructor.
159 ///
160 /// The current size of the array must be less than N, checked by assert.
161 template <typename U, typename = std::enable_if_t<std::is_constructible<StorageType, U>::value>>
push(U && value)162 INLINE void push(U&& value) {
163 SPH_ASSERT(actSize < N);
164 data[actSize++].emplace(std::forward<U>(value));
165 }
166
167 /// \brief Removes and destroys element from the end of the array.
168 ///
169 /// The removed element is returned from the function. Array must not be empty, checked by assert.
pop()170 INLINE T pop() {
171 SPH_ASSERT(actSize > 0);
172 T value = data[actSize - 1];
173 data[actSize - 1].destroy();
174 actSize--;
175 return value;
176 }
177
178 /// \brief Changes size of the array.
179 ///
180 /// New size must be between 0 and N. If the array is shrinked, elements from the end of the array are
181 /// destroyed; if the array is enlarged, new elements are created using default constructor.
resize(const TCounter newSize)182 void resize(const TCounter newSize) {
183 SPH_ASSERT(unsigned(newSize) <= N);
184 if (!std::is_trivially_default_constructible<T>::value) {
185 if (newSize > actSize) {
186 for (TCounter i = actSize; i < newSize; ++i) {
187 data[i].emplace();
188 }
189 } else {
190 for (TCounter i = newSize; i < actSize; ++i) {
191 data[i].destroy();
192 }
193 }
194 }
195 actSize = newSize;
196 }
197
begin()198 INLINE Iterator<StorageType> begin() {
199 return Iterator<StorageType>(rawData(), rawData(), rawData() + actSize);
200 }
201
begin()202 INLINE Iterator<const StorageType> begin() const {
203 return Iterator<const StorageType>(rawData(), rawData(), rawData() + actSize);
204 }
205
cbegin()206 INLINE Iterator<const StorageType> cbegin() const {
207 return Iterator<const StorageType>(rawData(), rawData(), rawData() + actSize);
208 }
209
end()210 INLINE Iterator<StorageType> end() {
211 return Iterator<StorageType>(rawData() + actSize, rawData(), rawData() + actSize);
212 }
213
end()214 INLINE Iterator<const StorageType> end() const {
215 return Iterator<const StorageType>(rawData() + actSize, rawData(), rawData() + actSize);
216 }
217
cend()218 INLINE Iterator<const StorageType> cend() const {
219 return Iterator<const StorageType>(rawData() + actSize, rawData(), rawData() + actSize);
220 }
221
222 operator ArrayView<T>() {
223 return ArrayView<T>(rawData(), actSize);
224 }
225
226 operator ArrayView<const T>() const {
227 return ArrayView<const T>(rawData(), actSize);
228 }
229
230 bool operator==(const StaticArray& other) const {
231 if (actSize != other.actSize) {
232 return false;
233 }
234 for (TCounter i = 0; i < actSize; ++i) {
235 if (data[i].get() != other.data[i].get()) {
236 return false;
237 }
238 }
239 return true;
240 }
241
242 bool operator!=(const StaticArray& other) const {
243 return !(*this == other);
244 }
245
246 /// \brief Prints content of array to stream.
247 ///
248 /// Stored values must have overloaded << operator.
249 template <typename TStream>
250 friend TStream& operator<<(TStream& stream, const StaticArray& array) {
251 for (const T& t : array) {
252 stream << t << std::endl;
253 }
254 return stream;
255 }
256
257 private:
rawData()258 INLINE StorageType* rawData() {
259 return reinterpret_cast<StorageType*>(data);
260 }
261
rawData()262 INLINE const StorageType* rawData() const {
263 return reinterpret_cast<const StorageType*>(data);
264 }
265 };
266
267 /// \brief Creates a static array from a list of parameters.
268 ///
269 /// All parameters must have the same type. Both the allocated size of the array and the number of constructed
270 /// elements equal to the number of parameters.
271 template <typename T0, typename... TArgs>
makeStatic(T0 && t0,TArgs &&...rest)272 StaticArray<T0, sizeof...(TArgs) + 1> makeStatic(T0&& t0, TArgs&&... rest) {
273 return StaticArray<T0, sizeof...(TArgs) + 1>({ std::forward<T0>(t0), std::forward<TArgs>(rest)... });
274 }
275
276 /// \brief Creates a static array from a list of l-value references.
277 ///
278 /// All parameters must have the same type. Both the allocated size of the array and the number of constructed
279 /// elements equal to the number of parameters.
280 template <typename T0, typename... TArgs>
tie(T0 & t0,TArgs &...rest)281 StaticArray<T0&, sizeof...(TArgs) + 1> tie(T0& t0, TArgs&... rest) {
282 return StaticArray<T0&, sizeof...(TArgs) + 1>({ t0, rest... });
283 }
284
285 /// \brief Alias for array holding two elements of the same type.
286 template <typename T>
287 using Pair = StaticArray<T, 2>;
288
289
290 /// \brief Container similar to \ref StaticArray, but with constexpr constructors and getters.
291 template <typename T, Size N>
292 class ConstexprArray {
293 private:
294 T data[N];
295
296 public:
297 template <typename... TArgs>
ConstexprArray(TArgs &&...args)298 constexpr ConstexprArray(TArgs&&... args)
299 : data{ std::forward<TArgs>(args)... } {}
300
301 constexpr const T& operator[](const Size idx) const {
302 return data[idx];
303 }
304
305 constexpr T& operator[](const Size idx) {
306 return data[idx];
307 }
308 };
309
310 NAMESPACE_SPH_END
311