Stratax 0.4.0rc1
Loading...
Searching...
No Matches
ArrayView.hpp
Go to the documentation of this file.
1
4#pragma once
5
6#include <array>
7#include <concepts>
8#include <cstddef>
9#include <iterator>
10#include <limits>
11#include <type_traits>
12#include <vector>
13
17
18namespace stratax::core {
19
36template<typename T>
38{
39public:
41 using element_type = T;
43 using value_type = std::remove_const_t<element_type>;
45 using size_type = std::size_t;
47 using difference_type = std::ptrdiff_t;
55 using const_pointer = const value_type*;
58
69 template<bool IsConst>
71 {
72 private:
73 using view_type = std::conditional_t<IsConst, const ArrayView, ArrayView>;
74
75 view_type* view_ = nullptr;
76 size_type index_ = 0;
77
78 public:
80 using iterator_concept = std::random_access_iterator_tag;
82 using iterator_category = std::random_access_iterator_tag;
86 using difference_type = std::ptrdiff_t;
88 using reference = std::conditional_t<IsConst, const_reference, ArrayView::reference>;
89
91 basic_iterator() = default;
93 basic_iterator(view_type* view, size_type index) noexcept
94 : view_(view), index_(index)
95 {}
96
98 template<bool OtherConst>
99 requires (IsConst && !OtherConst)
101 : view_(other.view_), index_(other.index_)
102 {}
103
105 reference operator*() const {return (*view_)[index_];}
107 reference operator[](difference_type offset) const {return *(*this + offset);}
108
110 basic_iterator& operator++() {++index_; return *this;}
112 basic_iterator operator++(int) {auto copy = *this; ++*this; return copy;}
114 basic_iterator& operator--() {--index_; return *this;}
116 basic_iterator operator--(int) {auto copy = *this; --*this; return copy;}
117
120 {
121 index_ = static_cast<size_type>(static_cast<difference_type>(index_) + offset);
122 return *this;
123 }
124
126 basic_iterator& operator-=(difference_type offset) {return *this += -offset;}
127
130 {
131 iterator += offset;
132 return iterator;
133 }
134
137 {
138 return iterator + offset;
139 }
140
143 {
144 iterator -= offset;
145 return iterator;
146 }
147
150 {
151 return static_cast<difference_type>(lhs.index_) -
152 static_cast<difference_type>(rhs.index_);
153 }
154
156 friend bool operator==(const basic_iterator&, const basic_iterator&) = default;
158 friend auto operator<=>(const basic_iterator& lhs, const basic_iterator& rhs)
159 {
160 return lhs.index_ <=> rhs.index_;
161 }
162
163 template<bool>
164 friend class basic_iterator;
165 };
170
184 : data_(data),
185 shape_(shape),
186 strides_(strides)
187 {
188 if (shape_.rank() != strides_.rank())
189 {
190 throw Exceptions::RankError("View shape and strides must have the same rank.");
191 }
192 if (shape_.elements() == 0) return;
193 if (data_ == nullptr)
194 {
195 throw Exceptions::ValueError("A nonempty view requires storage.");
196 }
197 size_type maximum_offset = 0;
198 for (size_type dim = 0; dim < rank(); ++dim)
199 {
200 const auto extent = shape_[dim] - 1;
201 const auto limit = std::numeric_limits<size_type>::max();
202 if (extent != 0 && strides_[dim] > (limit - maximum_offset) / extent)
203 {
204 throw Exceptions::DimensionError("View offset overflow.");
205 }
206 maximum_offset += extent * strides_[dim];
207 }
208 }
209
216 template<typename U>
217 requires (
218 std::is_const_v<element_type> &&
219 !std::is_const_v<U> &&
220 std::same_as<value_type, std::remove_const_t<U>>
221 )
223 : data_(other.data()),
224 shape_(other.shape()),
225 strides_(other.strides())
226 {}
227
229 [[nodiscard]]
230 const Shape& shape() const noexcept
231 {
232 return shape_;
233 }
234
236 [[nodiscard]]
237 const Shape& strides() const noexcept
238 {
239 return strides_;
240 }
241
243 [[nodiscard]]
244 size_type ndim() const noexcept
245 {
246 return rank();
247 }
248
250 [[nodiscard]]
251 size_type rank() const noexcept
252 {
253 return shape_.rank();
254 }
255
257 [[nodiscard]]
258 size_type size() const noexcept
259 {
260 return shape_.elements();
261 }
262
264 [[nodiscard]]
265 bool empty() const noexcept
266 {
267 return size() == 0;
268 }
269
271 [[nodiscard]]
272 pointer data() noexcept
273 {
274 return data_;
275 }
276
278 [[nodiscard]]
279 const_pointer data() const noexcept
280 {
281 return data_;
282 }
283
285 [[nodiscard]]
287 {
288 return const_view(data_, shape_, strides_);
289 }
290
293 {
294 return data_[flat_offset(index)];
295 }
296
299 {
300 return data_[flat_offset(index)];
301 }
302
304 iterator begin() noexcept {return iterator(this, 0);}
306 const_iterator begin() const noexcept {return const_iterator(this, 0);}
308 const_iterator cbegin() const noexcept {return const_iterator(this, 0);}
310 iterator end() noexcept {return iterator(this, size());}
312 const_iterator end() const noexcept {return const_iterator(this, size());}
314 const_iterator cend() const noexcept {return const_iterator(this, size());}
315
324template<typename... Rest>
325requires ((std::is_integral_v<Rest>) && ...)
326reference operator()(size_type first, Rest... rest)
327{
328 constexpr size_type count = sizeof...(Rest) + 1;
329
330 if (count != rank())
331 {
333 "The number of indices must match the view rank.");
334 }
335
336 const std::array<size_type, count> indices{
337 first,
338 static_cast<size_type>(rest)...
339 };
340
341 return data_[indexing::offset(strides_, indices)];
342}
343
352template<typename... Rest>
353requires ((std::is_integral_v<Rest>) && ...)
354const_reference operator()(size_type first, Rest... rest) const
355{
356 constexpr size_type count = sizeof...(Rest) + 1;
357
358 if (count != rank())
359 {
361 "The number of indices must match the view rank.");
362 }
363
364 const std::array<size_type, count> indices{
365 first,
366 static_cast<size_type>(rest)...
367 };
368
369 return data_[indexing::offset(strides_, indices)];
370}
371
377{
378 index = indexing::normalize_index(index, size());
379 return (*this)[static_cast<size_type>(index)];
380}
381
387{
388 index = indexing::normalize_index(index, size());
389 return (*this)[static_cast<size_type>(index)];
390}
391
397reference at(const std::vector<difference_type>& indices)
398{
399 return data_[checked_offset(indices)];
400}
401
407const_reference at(const std::vector<difference_type>& indices) const
408{
409 return data_[checked_offset(indices)];
410}
411
412private:
413 [[nodiscard]]
414 size_type flat_offset(size_type index) const
415 {
416 size_type offset = 0;
417
418 for (size_type dim = rank(); dim-- > 0;)
419 {
420 const size_type coordinate = index % shape_[dim];
421 index /= shape_[dim];
422 offset += coordinate * strides_[dim];
423 }
424
425 return offset;
426 }
427
428 size_type checked_offset(
429 const std::vector<difference_type>& indices) const
430{
431 if (indices.size() != rank())
432 {
434 "The number of indices must match the view rank.");
435 }
436
437 if (empty())
438 {
439 throw Exceptions::IndexError("Cannot index an empty view.");
440 }
441
442 size_type offset = 0;
443
444 for (size_type dim = 0; dim < rank(); ++dim)
445 {
446 const difference_type index =
447 indexing::normalize_index(
448 indices[dim],
449 shape_[dim]);
450
451 offset +=
452 static_cast<size_type>(index) *
453 strides_[dim];
454 }
455
456 return offset;
457}
458
459 pointer data_;
460 Shape shape_;
461 Shape strides_;
462};
463
464} // namespace stratax::core
Stratax exception types derived from std::runtime_error.
Unchecked offsets from element strides.
std::size_t offset(const core::Shape &strides, const IndexContainer &indices)
Computes an unchecked flat offset from strides and normalized indices.
Definition Indexing.hpp:28
Immutable dimension metadata and checked row-major strides.
Invalid dimension or overflow in dimension, element-count, or stride arithmetic.
Index or slice cannot be used for the requested access.
Number of axes or supplied multidimensional indices does not match the required rank.
Invalid argument value, such as an invalid shift count or null nonempty view.
Random-access iterator over logical flat indices of a view.
Definition ArrayView.hpp:71
ArrayView::value_type value_type
Unqualified element type.
Definition ArrayView.hpp:84
friend bool operator==(const basic_iterator &, const basic_iterator &)=default
Compares both the view identity and logical position.
std::random_access_iterator_tag iterator_category
Legacy random-access iterator category tag.
Definition ArrayView.hpp:82
friend basic_iterator operator+(basic_iterator iterator, difference_type offset)
Returns an iterator advanced by a signed offset.
basic_iterator & operator--()
Moves back one position and returns this iterator.
friend auto operator<=>(const basic_iterator &lhs, const basic_iterator &rhs)
Orders logical positions within the same view.
basic_iterator(const basic_iterator< OtherConst > &other) noexcept
Converts a mutable iterator to a read-only iterator.
basic_iterator & operator+=(difference_type offset)
Advances by a signed offset and returns this iterator.
basic_iterator operator++(int)
Advances one position and returns the previous iterator.
friend basic_iterator operator+(difference_type offset, basic_iterator iterator)
Returns an iterator advanced by a signed offset.
std::conditional_t< IsConst, const_reference, ArrayView::reference > reference
Element reference with the iterator constness.
Definition ArrayView.hpp:88
basic_iterator operator--(int)
Moves back one position and returns the previous iterator.
friend difference_type operator-(const basic_iterator &lhs, const basic_iterator &rhs)
Returns the signed logical distance between iterators into the same view.
reference operator*() const
Returns the element at the current logical position.
basic_iterator & operator-=(difference_type offset)
Moves back by a signed offset and returns this iterator.
reference operator[](difference_type offset) const
Returns the element at a relative logical offset.
std::ptrdiff_t difference_type
Signed iterator-distance type.
Definition ArrayView.hpp:86
basic_iterator & operator++()
Advances one position and returns this iterator.
std::random_access_iterator_tag iterator_concept
C++20 random-access iterator concept tag.
Definition ArrayView.hpp:80
basic_iterator()=default
Constructs a singular iterator.
basic_iterator(view_type *view, size_type index) noexcept
Constructs an iterator at a logical flat index, including the end position.
Definition ArrayView.hpp:93
friend basic_iterator operator-(basic_iterator iterator, difference_type offset)
Returns an iterator moved back by a signed offset.
Non-owning view of elements with unsigned element strides.
Definition ArrayView.hpp:38
const Shape & strides() const noexcept
Stored element strides.
ArrayView< const value_type > const_view
View type exposing read-only elements.
Definition ArrayView.hpp:57
reference at(difference_type index)
Checked logical flat access with negative indices.
element_type * pointer
Element pointer with the constness of T.
Definition ArrayView.hpp:53
const_iterator end() const noexcept
Past-the-end iterator.
T element_type
Element type including any const qualifier.
Definition ArrayView.hpp:41
const_iterator begin() const noexcept
First logical-element iterator.
const_reference operator()(size_type first, Rest... rest) const
Accesses an element using multidimensional indices with rank checking only.
const_reference operator[](size_type index) const
Unchecked logical flat access.
ArrayView(const ArrayView< U > &other)
Creates a read-only alias of a mutable view, copying its metadata.
const Shape & shape() const noexcept
Logical extents.
const_pointer data() const noexcept
Read-only base pointer; empty views may have non-null pointers.
std::size_t size_type
Unsigned extent and offset type.
Definition ArrayView.hpp:45
iterator end() noexcept
Past-the-end iterator.
const_iterator cend() const noexcept
Past-the-end iterator.
reference operator()(size_type first, Rest... rest)
Accesses an element using multidimensional indices with rank checking only.
element_type & reference
Element reference, mutable only when T is mutable.
Definition ArrayView.hpp:49
std::remove_const_t< element_type > value_type
Unqualified element type.
Definition ArrayView.hpp:43
size_type size() const noexcept
Number of logical elements, zero for rank zero.
const_reference at(difference_type index) const
Checked logical flat access with negative indices.
basic_iterator< true > const_iterator
Read-only random-access iterator in logical order.
reference operator[](size_type index)
Unchecked logical flat access.
ArrayView(pointer data, const Shape &shape, const Shape &strides)
Constructs a view after validating shape and stride metadata.
reference at(const std::vector< difference_type > &indices)
Checked multidimensional access with negative indices.
bool empty() const noexcept
Whether there are no logical elements.
const value_type & const_reference
Read-only element reference.
Definition ArrayView.hpp:51
const value_type * const_pointer
Read-only element pointer.
Definition ArrayView.hpp:55
size_type ndim() const noexcept
Alias for rank().
std::ptrdiff_t difference_type
Signed index and iterator-distance type.
Definition ArrayView.hpp:47
const_view as_const() const
Read-only alias with copied metadata.
pointer data() noexcept
Base storage pointer; empty views may have non-null pointers.
const_reference at(const std::vector< difference_type > &indices) const
Checked multidimensional access with negative indices.
iterator begin() noexcept
First logical-element iterator.
basic_iterator< false > iterator
Mutable-view random-access iterator in logical order.
const_iterator cbegin() const noexcept
First logical-element iterator.
size_type rank() const noexcept
Number of logical axes.
Stores the dimensions of a multidimensional array.
Definition Shape.hpp:36
size_type elements() const
Computes the total number of elements described by the shape.
Definition Shape.hpp:94
size_type rank() const noexcept
Returns the number of dimensions.
Definition Shape.hpp:124