Stratax 0.3.1
Loading...
Searching...
No Matches
ArrayView.hpp
1#pragma once
2
3#include <array>
4#include <concepts>
5#include <cstddef>
6#include <iterator>
7#include <type_traits>
8#include <vector>
9
10#include <stratax/core/Shape.hpp>
11#include <stratax/indexing/Indexing.hpp>
12#include <stratax/exceptions/Exceptions.hpp>
13
14namespace stratax::core {
15
16template<typename T>
18{
19public:
20 using element_type = T;
21 using value_type = std::remove_const_t<element_type>;
22 using size_type = std::size_t;
23 using difference_type = std::ptrdiff_t;
24 using reference = element_type&;
25 using const_reference = const value_type&;
26 using pointer = element_type*;
27 using const_pointer = const value_type*;
29
30 template<bool IsConst>
32 {
33 private:
34 using view_type = std::conditional_t<IsConst, const ArrayView, ArrayView>;
35
36 view_type* view_ = nullptr;
37 size_type index_ = 0;
38
39 public:
40 using iterator_concept = std::random_access_iterator_tag;
41 using iterator_category = std::random_access_iterator_tag;
42 using value_type = ArrayView::value_type;
43 using difference_type = std::ptrdiff_t;
44 using reference = std::conditional_t<IsConst, const_reference, ArrayView::reference>;
45
46 basic_iterator() = default;
47 basic_iterator(view_type* view, size_type index) noexcept
48 : view_(view), index_(index)
49 {}
50
51 template<bool OtherConst>
52 requires (IsConst && !OtherConst)
53 basic_iterator(const basic_iterator<OtherConst>& other) noexcept
54 : view_(other.view_), index_(other.index_)
55 {}
56
57 reference operator*() const {return (*view_)[index_];}
58 reference operator[](difference_type offset) const {return *(*this + offset);}
59
60 basic_iterator& operator++() {++index_; return *this;}
61 basic_iterator operator++(int) {auto copy = *this; ++*this; return copy;}
62 basic_iterator& operator--() {--index_; return *this;}
63 basic_iterator operator--(int) {auto copy = *this; --*this; return copy;}
64
65 basic_iterator& operator+=(difference_type offset)
66 {
67 index_ = static_cast<size_type>(static_cast<difference_type>(index_) + offset);
68 return *this;
69 }
70
71 basic_iterator& operator-=(difference_type offset) {return *this += -offset;}
72
73 friend basic_iterator operator+(basic_iterator iterator, difference_type offset)
74 {
75 iterator += offset;
76 return iterator;
77 }
78
79 friend basic_iterator operator+(difference_type offset, basic_iterator iterator)
80 {
81 return iterator + offset;
82 }
83
84 friend basic_iterator operator-(basic_iterator iterator, difference_type offset)
85 {
86 iterator -= offset;
87 return iterator;
88 }
89
90 friend difference_type operator-(const basic_iterator& lhs, const basic_iterator& rhs)
91 {
92 return static_cast<difference_type>(lhs.index_) -
93 static_cast<difference_type>(rhs.index_);
94 }
95
96 friend bool operator==(const basic_iterator&, const basic_iterator&) = default;
97 friend auto operator<=>(const basic_iterator& lhs, const basic_iterator& rhs)
98 {
99 return lhs.index_ <=> rhs.index_;
100 }
101
102 template<bool>
103 friend class basic_iterator;
104 };
105
108
109 ArrayView(pointer data, const Shape& shape, const Shape& strides)
110 : data_(data),
111 shape_(shape),
112 strides_(strides)
113 {}
114
115 template<typename U>
116 requires (
117 std::is_const_v<element_type> &&
118 !std::is_const_v<U> &&
119 std::same_as<value_type, std::remove_const_t<U>>
120 )
121 ArrayView(const ArrayView<U>& other)
122 : data_(other.data()),
123 shape_(other.shape()),
124 strides_(other.strides())
125 {}
126
127 [[nodiscard]]
128 const Shape& shape() const noexcept
129 {
130 return shape_;
131 }
132
133 [[nodiscard]]
134 const Shape& strides() const noexcept
135 {
136 return strides_;
137 }
138
139 [[nodiscard]]
140 size_type ndim() const noexcept
141 {
142 return rank();
143 }
144
145 [[nodiscard]]
146 size_type rank() const noexcept
147 {
148 return shape_.rank();
149 }
150
151 [[nodiscard]]
152 size_type size() const noexcept
153 {
154 return shape_.elements();
155 }
156
157 [[nodiscard]]
158 bool empty() const noexcept
159 {
160 return size() == 0;
161 }
162
163 [[nodiscard]]
164 pointer data() noexcept
165 {
166 return data_;
167 }
168
169 [[nodiscard]]
170 const_pointer data() const noexcept
171 {
172 return data_;
173 }
174
175 [[nodiscard]]
176 const_view as_const() const
177 {
178 return const_view(data_, shape_, strides_);
179 }
180
181 reference operator[](size_type index)
182 {
183 return data_[flat_offset(index)];
184 }
185
186 const_reference operator[](size_type index) const
187 {
188 return data_[flat_offset(index)];
189 }
190
191 iterator begin() noexcept {return iterator(this, 0);}
192 const_iterator begin() const noexcept {return const_iterator(this, 0);}
193 const_iterator cbegin() const noexcept {return const_iterator(this, 0);}
194 iterator end() noexcept {return iterator(this, size());}
195 const_iterator end() const noexcept {return const_iterator(this, size());}
196 const_iterator cend() const noexcept {return const_iterator(this, size());}
197
198 template<typename... Rest>
199requires ((std::is_integral_v<Rest>) && ...)
200reference operator()(size_type first, Rest... rest)
201{
202 constexpr size_type count = sizeof...(Rest) + 1;
203
204 if (count != rank())
205 {
207 "The number of indices must match the view rank.");
208 }
209
210 const std::array<size_type, count> indices{
211 first,
212 static_cast<size_type>(rest)...
213 };
214
215 return data_[indexing::offset(strides_, indices)];
216}
217
218template<typename... Rest>
219requires ((std::is_integral_v<Rest>) && ...)
220const_reference operator()(size_type first, Rest... rest) const
221{
222 constexpr size_type count = sizeof...(Rest) + 1;
223
224 if (count != rank())
225 {
227 "The number of indices must match the view rank.");
228 }
229
230 const std::array<size_type, count> indices{
231 first,
232 static_cast<size_type>(rest)...
233 };
234
235 return data_[indexing::offset(strides_, indices)];
236}
237
238reference at(difference_type index)
239{
240 index = indexing::normalize_index(index, size());
241 return (*this)[static_cast<size_type>(index)];
242}
243
244const_reference at(difference_type index) const
245{
246 index = indexing::normalize_index(index, size());
247 return (*this)[static_cast<size_type>(index)];
248}
249
250reference at(const std::vector<difference_type>& indices)
251{
252 return data_[checked_offset(indices)];
253}
254
255const_reference at(const std::vector<difference_type>& indices) const
256{
257 return data_[checked_offset(indices)];
258}
259
260private:
261 [[nodiscard]]
262 size_type flat_offset(size_type index) const
263 {
264 size_type offset = 0;
265
266 for (size_type dim = rank(); dim-- > 0;)
267 {
268 const size_type coordinate = index % shape_[dim];
269 index /= shape_[dim];
270 offset += coordinate * strides_[dim];
271 }
272
273 return offset;
274 }
275
276 size_type checked_offset(
277 const std::vector<difference_type>& indices) const
278{
279 if (indices.size() != rank())
280 {
282 "The number of indices must match the view rank.");
283 }
284
285 size_type offset = 0;
286
287 for (size_type dim = 0; dim < rank(); ++dim)
288 {
289 const difference_type index =
290 indexing::normalize_index(
291 indices[dim],
292 shape_[dim]);
293
294 offset +=
295 static_cast<size_type>(index) *
296 strides_[dim];
297 }
298
299 return offset;
300}
301
302 pointer data_;
303 Shape shape_;
304 Shape strides_;
305};
306
307} // namespace stratax::core
Stores the dimensions of a multidimensional array.
Definition Shape.hpp:33
size_type elements() const
Computes the total number of elements described by the shape.
Definition Shape.hpp:91
size_type rank() const noexcept
Returns the number of dimensions.
Definition Shape.hpp:121