xtensor
Loading...
Searching...
No Matches
xstorage.hpp
1/***************************************************************************
2 * Copyright (c) Johan Mabille, Sylvain Corlay and Wolf Vollprecht *
3 * Copyright (c) QuantStack *
4 * *
5 * Distributed under the terms of the BSD 3-Clause License. *
6 * *
7 * The full license is in the file LICENSE, distributed with this software. *
8 ****************************************************************************/
9
10#ifndef XTENSOR_STORAGE_HPP
11#define XTENSOR_STORAGE_HPP
12
13#include <algorithm>
14#include <cstddef>
15#include <functional>
16#include <initializer_list>
17#include <iterator>
18#include <memory>
19#include <type_traits>
20
21#include "xexception.hpp"
22#include "xtensor_config.hpp"
23#include "xtensor_simd.hpp"
24#include "xutils.hpp"
25
26namespace xt
27{
28
29 namespace detail
30 {
31 template <class It>
32 using require_input_iter = typename std::enable_if<
33 std::is_convertible<typename std::iterator_traits<It>::iterator_category, std::input_iterator_tag>::value>::type;
34 }
35
36 template <class C>
37 struct is_contiguous_container : std::true_type
38 {
39 };
40
41 template <class T, class A = std::allocator<T>>
42 class uvector
43 {
44 public:
45
46 using allocator_type = A;
47
48 using value_type = typename std::allocator_traits<A>::value_type;
49 using reference = value_type&;
50 using const_reference = const value_type&;
51 using pointer = typename std::allocator_traits<A>::pointer;
52 using const_pointer = typename std::allocator_traits<A>::const_pointer;
53
54 using size_type = typename std::allocator_traits<A>::size_type;
55 using difference_type = typename std::allocator_traits<A>::difference_type;
56
57 using iterator = pointer;
58 using const_iterator = const_pointer;
59 using reverse_iterator = std::reverse_iterator<iterator>;
60 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
61
62 uvector() noexcept;
63 explicit uvector(const allocator_type& alloc) noexcept;
64 explicit uvector(size_type count, const allocator_type& alloc = allocator_type());
65 uvector(size_type count, const_reference value, const allocator_type& alloc = allocator_type());
66
67 template <class InputIt, class = detail::require_input_iter<InputIt>>
68 uvector(InputIt first, InputIt last, const allocator_type& alloc = allocator_type());
69
70 uvector(std::initializer_list<T> init, const allocator_type& alloc = allocator_type());
71
72 ~uvector();
73
74 uvector(const uvector& rhs);
75 uvector(const uvector& rhs, const allocator_type& alloc);
76 uvector& operator=(const uvector&);
77
78 uvector(uvector&& rhs) noexcept;
79 uvector(uvector&& rhs, const allocator_type& alloc) noexcept;
80 uvector& operator=(uvector&& rhs) noexcept;
81
82 allocator_type get_allocator() const noexcept;
83
84 bool empty() const noexcept;
85 size_type size() const noexcept;
86 void resize(size_type size);
87 size_type max_size() const noexcept;
88 void reserve(size_type new_cap);
89 size_type capacity() const noexcept;
90 void shrink_to_fit();
91 void clear();
92
93 reference operator[](size_type i);
94 const_reference operator[](size_type i) const;
95
96 reference at(size_type i);
97 const_reference at(size_type i) const;
98
99 reference front();
100 const_reference front() const;
101
102 reference back();
103 const_reference back() const;
104
105 pointer data() noexcept;
106 const_pointer data() const noexcept;
107
108 iterator begin() noexcept;
109 iterator end() noexcept;
110
111 const_iterator begin() const noexcept;
112 const_iterator end() const noexcept;
113
114 const_iterator cbegin() const noexcept;
115 const_iterator cend() const noexcept;
116
117 reverse_iterator rbegin() noexcept;
118 reverse_iterator rend() noexcept;
119
120 const_reverse_iterator rbegin() const noexcept;
121 const_reverse_iterator rend() const noexcept;
122
123 const_reverse_iterator crbegin() const noexcept;
124 const_reverse_iterator crend() const noexcept;
125
126 void swap(uvector& rhs) noexcept;
127
128 private:
129
130 template <class I>
131 void init_data(I first, I last);
132
133 void resize_impl(size_type new_size);
134
135 allocator_type m_allocator;
136
137 // Storing a pair of pointers is more efficient for iterating than
138 // storing a pointer to the beginning and the size of the container
139 pointer p_begin;
140 pointer p_end;
141 };
142
143 template <class T, class A>
144 bool operator==(const uvector<T, A>& lhs, const uvector<T, A>& rhs);
145
146 template <class T, class A>
147 bool operator!=(const uvector<T, A>& lhs, const uvector<T, A>& rhs);
148
149 template <class T, class A>
150 bool operator<(const uvector<T, A>& lhs, const uvector<T, A>& rhs);
151
152 template <class T, class A>
153 bool operator<=(const uvector<T, A>& lhs, const uvector<T, A>& rhs);
154
155 template <class T, class A>
156 bool operator>(const uvector<T, A>& lhs, const uvector<T, A>& rhs);
157
158 template <class T, class A>
159 bool operator>=(const uvector<T, A>& lhs, const uvector<T, A>& rhs);
160
161 template <class T, class A>
162 void swap(uvector<T, A>& lhs, uvector<T, A>& rhs) noexcept;
163
164 /**************************
165 * uvector implementation *
166 **************************/
167
168 namespace detail
169 {
170 template <class A>
171 inline typename std::allocator_traits<A>::pointer
172 safe_init_allocate(A& alloc, typename std::allocator_traits<A>::size_type size)
173 {
174 using traits = std::allocator_traits<A>;
175 using pointer = typename traits::pointer;
176 using value_type = typename traits::value_type;
177 pointer res = alloc.allocate(size);
178 if (!xtrivially_default_constructible<value_type>::value)
179 {
180 for (pointer p = res; p != res + size; ++p)
181 {
182 traits::construct(alloc, p, value_type());
183 }
184 }
185 return res;
186 }
187
188 template <class A>
189 inline void safe_destroy_deallocate(
190 A& alloc,
191 typename std::allocator_traits<A>::pointer ptr,
192 typename std::allocator_traits<A>::size_type size
193 )
194 {
195 using traits = std::allocator_traits<A>;
196 using pointer = typename traits::pointer;
197 using value_type = typename traits::value_type;
198 if (ptr != nullptr)
199 {
200 if (!xtrivially_default_constructible<value_type>::value)
201 {
202 for (pointer p = ptr; p != ptr + size; ++p)
203 {
204 traits::destroy(alloc, p);
205 }
206 }
207 traits::deallocate(alloc, ptr, size);
208 }
209 }
210 }
211
212 template <class T, class A>
213 template <class I>
214 inline void uvector<T, A>::init_data(I first, I last)
215 {
216 size_type size = static_cast<size_type>(std::distance(first, last));
217 if (size != size_type(0))
218 {
219 p_begin = m_allocator.allocate(size);
220 std::uninitialized_copy(first, last, p_begin);
221 p_end = p_begin + size;
222 }
223 }
224
225 template <class T, class A>
226 inline void uvector<T, A>::resize_impl(size_type new_size)
227 {
228 size_type old_size = size();
229 pointer old_begin = p_begin;
230 if (new_size != old_size)
231 {
232 p_begin = detail::safe_init_allocate(m_allocator, new_size);
233 p_end = p_begin + new_size;
234 detail::safe_destroy_deallocate(m_allocator, old_begin, old_size);
235 }
236 }
237
238 template <class T, class A>
239 inline uvector<T, A>::uvector() noexcept
240 : uvector(allocator_type())
241 {
242 }
243
244 template <class T, class A>
245 inline uvector<T, A>::uvector(const allocator_type& alloc) noexcept
246 : m_allocator(alloc)
247 , p_begin(nullptr)
248 , p_end(nullptr)
249 {
250 }
251
252 template <class T, class A>
253 inline uvector<T, A>::uvector(size_type count, const allocator_type& alloc)
254 : m_allocator(alloc)
255 , p_begin(nullptr)
256 , p_end(nullptr)
257 {
258 if (count != 0)
259 {
260 p_begin = detail::safe_init_allocate(m_allocator, count);
261 p_end = p_begin + count;
262 }
263 }
264
265 template <class T, class A>
266 inline uvector<T, A>::uvector(size_type count, const_reference value, const allocator_type& alloc)
267 : m_allocator(alloc)
268 , p_begin(nullptr)
269 , p_end(nullptr)
270 {
271 if (count != 0)
272 {
273 p_begin = m_allocator.allocate(count);
274 p_end = p_begin + count;
275 std::uninitialized_fill(p_begin, p_end, value);
276 }
277 }
278
279 template <class T, class A>
280 template <class InputIt, class>
281 inline uvector<T, A>::uvector(InputIt first, InputIt last, const allocator_type& alloc)
282 : m_allocator(alloc)
283 , p_begin(nullptr)
284 , p_end(nullptr)
285 {
286 init_data(first, last);
287 }
288
289 template <class T, class A>
290 inline uvector<T, A>::uvector(std::initializer_list<T> init, const allocator_type& alloc)
291 : m_allocator(alloc)
292 , p_begin(nullptr)
293 , p_end(nullptr)
294 {
295 init_data(init.begin(), init.end());
296 }
297
298 template <class T, class A>
299 inline uvector<T, A>::~uvector()
300 {
301 detail::safe_destroy_deallocate(m_allocator, p_begin, size());
302 p_begin = nullptr;
303 p_end = nullptr;
304 }
305
306 template <class T, class A>
307 inline uvector<T, A>::uvector(const uvector& rhs)
308 : m_allocator(
309 std::allocator_traits<allocator_type>::select_on_container_copy_construction(rhs.get_allocator())
310 )
311 , p_begin(nullptr)
312 , p_end(nullptr)
313 {
314 init_data(rhs.p_begin, rhs.p_end);
315 }
316
317 template <class T, class A>
318 inline uvector<T, A>::uvector(const uvector& rhs, const allocator_type& alloc)
319 : m_allocator(alloc)
320 , p_begin(nullptr)
321 , p_end(nullptr)
322 {
323 init_data(rhs.p_begin, rhs.p_end);
324 }
325
326 template <class T, class A>
327 inline uvector<T, A>& uvector<T, A>::operator=(const uvector& rhs)
328 {
329 // No copy and swap idiom here due to performance issues
330 if (this != &rhs)
331 {
332 m_allocator = std::allocator_traits<allocator_type>::select_on_container_copy_construction(
333 rhs.get_allocator()
334 );
335 resize_impl(rhs.size());
336 if (xtrivially_default_constructible<value_type>::value)
337 {
338 std::uninitialized_copy(rhs.p_begin, rhs.p_end, p_begin);
339 }
340 else
341 {
342 std::copy(rhs.p_begin, rhs.p_end, p_begin);
343 }
344 }
345 return *this;
346 }
347
348 template <class T, class A>
349 inline uvector<T, A>::uvector(uvector&& rhs) noexcept
350 : m_allocator(std::move(rhs.m_allocator))
351 , p_begin(rhs.p_begin)
352 , p_end(rhs.p_end)
353 {
354 rhs.p_begin = nullptr;
355 rhs.p_end = nullptr;
356 }
357
358 template <class T, class A>
359 inline uvector<T, A>::uvector(uvector&& rhs, const allocator_type& alloc) noexcept
360 : m_allocator(alloc)
361 , p_begin(rhs.p_begin)
362 , p_end(rhs.p_end)
363 {
364 rhs.p_begin = nullptr;
365 rhs.p_end = nullptr;
366 }
367
368 template <class T, class A>
369 inline uvector<T, A>& uvector<T, A>::operator=(uvector&& rhs) noexcept
370 {
371 using std::swap;
372 uvector tmp(std::move(rhs));
373 swap(p_begin, tmp.p_begin);
374 swap(p_end, tmp.p_end);
375 return *this;
376 }
377
378 template <class T, class A>
379 inline auto uvector<T, A>::get_allocator() const noexcept -> allocator_type
380 {
381 return allocator_type(m_allocator);
382 }
383
384 template <class T, class A>
385 inline bool uvector<T, A>::empty() const noexcept
386 {
387 return size() == size_type(0);
388 }
389
390 template <class T, class A>
391 inline auto uvector<T, A>::size() const noexcept -> size_type
392 {
393 return static_cast<size_type>(p_end - p_begin);
394 }
395
396 template <class T, class A>
397 inline void uvector<T, A>::resize(size_type size)
398 {
399 resize_impl(size);
400 }
401
402 template <class T, class A>
403 inline auto uvector<T, A>::max_size() const noexcept -> size_type
404 {
405 return m_allocator.max_size();
406 }
407
408 template <class T, class A>
409 inline void uvector<T, A>::reserve(size_type /*new_cap*/)
410 {
411 }
412
413 template <class T, class A>
414 inline auto uvector<T, A>::capacity() const noexcept -> size_type
415 {
416 return size();
417 }
418
419 template <class T, class A>
420 inline void uvector<T, A>::shrink_to_fit()
421 {
422 }
423
424 template <class T, class A>
425 inline void uvector<T, A>::clear()
426 {
427 resize(size_type(0));
428 }
429
430 template <class T, class A>
431 inline auto uvector<T, A>::operator[](size_type i) -> reference
432 {
433 return p_begin[i];
434 }
435
436 template <class T, class A>
437 inline auto uvector<T, A>::operator[](size_type i) const -> const_reference
438 {
439 return p_begin[i];
440 }
441
442 template <class T, class A>
443 inline auto uvector<T, A>::at(size_type i) -> reference
444 {
445 if (i >= size())
446 {
447 XTENSOR_THROW(std::out_of_range, "Out of range in uvector access");
448 }
449 return this->operator[](i);
450 }
451
452 template <class T, class A>
453 inline auto uvector<T, A>::at(size_type i) const -> const_reference
454 {
455 if (i >= size())
456 {
457 XTENSOR_THROW(std::out_of_range, "Out of range in uvector access");
458 }
459 return this->operator[](i);
460 }
461
462 template <class T, class A>
463 inline auto uvector<T, A>::front() -> reference
464 {
465 return p_begin[0];
466 }
467
468 template <class T, class A>
469 inline auto uvector<T, A>::front() const -> const_reference
470 {
471 return p_begin[0];
472 }
473
474 template <class T, class A>
475 inline auto uvector<T, A>::back() -> reference
476 {
477 return *(p_end - 1);
478 }
479
480 template <class T, class A>
481 inline auto uvector<T, A>::back() const -> const_reference
482 {
483 return *(p_end - 1);
484 }
485
486 template <class T, class A>
487 inline auto uvector<T, A>::data() noexcept -> pointer
488 {
489 return p_begin;
490 }
491
492 template <class T, class A>
493 inline auto uvector<T, A>::data() const noexcept -> const_pointer
494 {
495 return p_begin;
496 }
497
498 template <class T, class A>
499 inline auto uvector<T, A>::begin() noexcept -> iterator
500 {
501 return p_begin;
502 }
503
504 template <class T, class A>
505 inline auto uvector<T, A>::end() noexcept -> iterator
506 {
507 return p_end;
508 }
509
510 template <class T, class A>
511 inline auto uvector<T, A>::begin() const noexcept -> const_iterator
512 {
513 return p_begin;
514 }
515
516 template <class T, class A>
517 inline auto uvector<T, A>::end() const noexcept -> const_iterator
518 {
519 return p_end;
520 }
521
522 template <class T, class A>
523 inline auto uvector<T, A>::cbegin() const noexcept -> const_iterator
524 {
525 return begin();
526 }
527
528 template <class T, class A>
529 inline auto uvector<T, A>::cend() const noexcept -> const_iterator
530 {
531 return end();
532 }
533
534 template <class T, class A>
535 inline auto uvector<T, A>::rbegin() noexcept -> reverse_iterator
536 {
537 return reverse_iterator(end());
538 }
539
540 template <class T, class A>
541 inline auto uvector<T, A>::rend() noexcept -> reverse_iterator
542 {
543 return reverse_iterator(begin());
544 }
545
546 template <class T, class A>
547 inline auto uvector<T, A>::rbegin() const noexcept -> const_reverse_iterator
548 {
549 return const_reverse_iterator(end());
550 }
551
552 template <class T, class A>
553 inline auto uvector<T, A>::rend() const noexcept -> const_reverse_iterator
554 {
555 return const_reverse_iterator(begin());
556 }
557
558 template <class T, class A>
559 inline auto uvector<T, A>::crbegin() const noexcept -> const_reverse_iterator
560 {
561 return rbegin();
562 }
563
564 template <class T, class A>
565 inline auto uvector<T, A>::crend() const noexcept -> const_reverse_iterator
566 {
567 return rend();
568 }
569
570 template <class T, class A>
571 inline void uvector<T, A>::swap(uvector<T, A>& rhs) noexcept
572 {
573 using std::swap;
574 swap(m_allocator, rhs.m_allocator);
575 swap(p_begin, rhs.p_begin);
576 swap(p_end, rhs.p_end);
577 }
578
579 template <class T, class A>
580 inline bool operator==(const uvector<T, A>& lhs, const uvector<T, A>& rhs)
581 {
582 return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
583 }
584
585 template <class T, class A>
586 inline bool operator!=(const uvector<T, A>& lhs, const uvector<T, A>& rhs)
587 {
588 return !(lhs == rhs);
589 }
590
591 template <class T, class A>
592 inline bool operator<(const uvector<T, A>& lhs, const uvector<T, A>& rhs)
593 {
594 return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
595 }
596
597 template <class T, class A>
598 inline bool operator<=(const uvector<T, A>& lhs, const uvector<T, A>& rhs)
599 {
600 return !(lhs > rhs);
601 }
602
603 template <class T, class A>
604 inline bool operator>(const uvector<T, A>& lhs, const uvector<T, A>& rhs)
605 {
606 return rhs < lhs;
607 }
608
609 template <class T, class A>
610 inline bool operator>=(const uvector<T, A>& lhs, const uvector<T, A>& rhs)
611 {
612 return !(lhs < rhs);
613 }
614
615 template <class T, class A>
616 inline void swap(uvector<T, A>& lhs, uvector<T, A>& rhs) noexcept
617 {
618 lhs.swap(rhs);
619 }
620
621 /**************************
622 * svector implementation *
623 **************************/
624
625 namespace detail
626 {
627 template <class T>
628 struct allocator_alignment
629 {
630 static constexpr std::size_t value = 0;
631 };
632
633 template <class T, std::size_t A>
634 struct allocator_alignment<xt_simd::aligned_allocator<T, A>>
635 {
636 static constexpr std::size_t value = A;
637 };
638 }
639
640 template <class T, std::size_t N = 4, class A = std::allocator<T>, bool Init = true>
642 {
643 public:
644
646 using allocator_type = A;
647 using size_type = typename std::allocator_traits<A>::size_type;
648 using value_type = typename std::allocator_traits<A>::value_type;
649 using pointer = typename std::allocator_traits<A>::pointer;
650 using const_pointer = typename std::allocator_traits<A>::const_pointer;
651 using reference = value_type&;
652 using const_reference = const value_type&;
653 using difference_type = typename std::allocator_traits<A>::difference_type;
654
655 using iterator = pointer;
656 using const_iterator = const_pointer;
657 using reverse_iterator = std::reverse_iterator<iterator>;
658 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
659
660#if defined(_MSC_VER) && _MSC_VER < 1910
661 static constexpr std::size_t alignment = detail::allocator_alignment<A>::value;
662#else
663 static constexpr std::size_t alignment = detail::allocator_alignment<A>::value != 0
664 ? detail::allocator_alignment<A>::value
665 : alignof(T);
666#endif
667
668 svector() noexcept;
669 ~svector();
670
671 explicit svector(const allocator_type& alloc) noexcept;
672 explicit svector(size_type n, const allocator_type& alloc = allocator_type());
673 svector(size_type n, const value_type& v, const allocator_type& alloc = allocator_type());
674 svector(std::initializer_list<T> il, const allocator_type& alloc = allocator_type());
675
676 svector(const std::vector<T>& vec);
677
678 template <class IT, class = detail::require_input_iter<IT>>
679 svector(IT begin, IT end, const allocator_type& alloc = allocator_type());
680
681 template <std::size_t N2, bool I2, class = std::enable_if_t<N != N2, void>>
682 explicit svector(const svector<T, N2, A, I2>& rhs);
683
684 svector& operator=(const svector& rhs);
685 svector& operator=(svector&& rhs) noexcept(std::is_nothrow_move_assignable<value_type>::value);
686 svector& operator=(const std::vector<T>& rhs);
687 svector& operator=(std::initializer_list<T> il);
688
689 template <std::size_t N2, bool I2, class = std::enable_if_t<N != N2, void>>
690 svector& operator=(const svector<T, N2, A, I2>& rhs);
691
692 svector(const svector& other);
693 svector(svector&& other) noexcept(std::is_nothrow_move_constructible<value_type>::value);
694
695 void assign(size_type n, const value_type& v);
696
697 template <class V>
698 void assign(std::initializer_list<V> il);
699
700 template <class IT>
701 void assign(IT other_begin, IT other_end);
702
703 reference operator[](size_type idx);
704 const_reference operator[](size_type idx) const;
705
706 reference at(size_type idx);
707 const_reference at(size_type idx) const;
708
709 pointer data();
710 const_pointer data() const;
711
712 void push_back(const T& elt);
713 void push_back(T&& elt);
714 void pop_back();
715
716 iterator begin();
717 const_iterator begin() const;
718 const_iterator cbegin() const;
719 iterator end();
720 const_iterator end() const;
721 const_iterator cend() const;
722
723 reverse_iterator rbegin();
724 const_reverse_iterator rbegin() const;
725 const_reverse_iterator crbegin() const;
726 reverse_iterator rend();
727 const_reverse_iterator rend() const;
728 const_reverse_iterator crend() const;
729
730 bool empty() const;
731 size_type size() const;
732 void resize(size_type n);
733 size_type max_size() const noexcept;
734 size_type capacity() const;
735 void reserve(size_type n);
736 void shrink_to_fit();
737 void clear();
738
739 reference front();
740 const_reference front() const;
741 reference back();
742 const_reference back() const;
743
744 bool on_stack();
745
746 iterator erase(const_iterator cit);
747 iterator erase(const_iterator cfirst, const_iterator clast);
748
749 iterator insert(const_iterator it, const T& elt);
750
751 template <class It>
752 iterator insert(const_iterator pos, It first, It last);
753
754 iterator insert(const_iterator pos, std::initializer_list<T> l);
755
756 template <std::size_t ON, class OA, bool InitA>
757 void swap(svector<T, ON, OA, InitA>& rhs);
758
759 allocator_type get_allocator() const noexcept;
760
761 private:
762
763 A m_allocator;
764
765 T* m_begin = std::begin(m_data);
766 T* m_end = std::begin(m_data);
767 T* m_capacity = std::end(m_data);
768
769 // stack allocated memory
770 alignas(alignment) T m_data[N > 0 ? N : 1];
771
772 void grow(size_type min_capacity = 0);
773 void destroy_range(T* begin, T* end);
774 };
775
776 template <class T, std::size_t N, class A, bool Init>
778 {
779 if (!on_stack())
780 {
781 detail::safe_destroy_deallocate(m_allocator, m_begin, static_cast<std::size_t>(m_capacity - m_begin));
782 }
783 }
784
785 template <class T, std::size_t N, class A, bool Init>
786 inline svector<T, N, A, Init>::svector() noexcept
787 : svector(allocator_type())
788 {
789 }
790
791 template <class T, std::size_t N, class A, bool Init>
792 inline svector<T, N, A, Init>::svector(const allocator_type& alloc) noexcept
793 : m_allocator(alloc)
794 {
795 }
796
797 template <class T, std::size_t N, class A, bool Init>
798 inline svector<T, N, A, Init>::svector(size_type n, const allocator_type& alloc)
799 : m_allocator(alloc)
800 {
801 if (Init)
802 {
803 assign(n, T(0));
804 }
805 else
806 {
807 resize(n);
808 }
809 }
810
811 template <class T, std::size_t N, class A, bool Init>
812 template <class IT, class>
813 inline svector<T, N, A, Init>::svector(IT begin, IT end, const allocator_type& alloc)
814 : m_allocator(alloc)
815 {
816 assign(begin, end);
817 }
818
819 template <class T, std::size_t N, class A, bool Init>
820 template <std::size_t N2, bool I2, class>
821 inline svector<T, N, A, Init>::svector(const svector<T, N2, A, I2>& rhs)
822 : m_allocator(rhs.get_allocator())
823 {
824 assign(rhs.begin(), rhs.end());
825 }
826
827 template <class T, std::size_t N, class A, bool Init>
828 inline svector<T, N, A, Init>::svector(const std::vector<T>& vec)
829 {
830 assign(vec.begin(), vec.end());
831 }
832
833 template <class T, std::size_t N, class A, bool Init>
834 inline svector<T, N, A, Init>::svector(size_type n, const value_type& v, const allocator_type& alloc)
835 : m_allocator(alloc)
836 {
837 assign(n, v);
838 }
839
840 template <class T, std::size_t N, class A, bool Init>
841 inline svector<T, N, A, Init>::svector(std::initializer_list<T> il, const allocator_type& alloc)
842 : m_allocator(alloc)
843 {
844 assign(il.begin(), il.end());
845 }
846
847 template <class T, std::size_t N, class A, bool Init>
848 inline svector<T, N, A, Init>& svector<T, N, A, Init>::operator=(const svector& rhs)
849 {
850 assign(rhs.begin(), rhs.end());
851 return *this;
852 }
853
854 template <class T, std::size_t N, class A, bool Init>
855 inline svector<T, N, A, Init>& svector<T, N, A, Init>::operator=(svector&& rhs
856 ) noexcept(std::is_nothrow_move_assignable<value_type>::value)
857 {
858 assign(rhs.begin(), rhs.end());
859 return *this;
860 }
861
862 template <class T, std::size_t N, class A, bool Init>
863 inline svector<T, N, A, Init>& svector<T, N, A, Init>::operator=(const std::vector<T>& rhs)
864 {
865 m_allocator = std::allocator_traits<allocator_type>::select_on_container_copy_construction(
866 rhs.get_allocator()
867 );
868 assign(rhs.begin(), rhs.end());
869 return *this;
870 }
871
872 template <class T, std::size_t N, class A, bool Init>
873 inline svector<T, N, A, Init>& svector<T, N, A, Init>::operator=(std::initializer_list<T> il)
874 {
875 return operator=(self_type(il));
876 }
877
878 template <class T, std::size_t N, class A, bool Init>
879 template <std::size_t N2, bool I2, class>
880 inline svector<T, N, A, Init>& svector<T, N, A, Init>::operator=(const svector<T, N2, A, I2>& rhs)
881 {
882 m_allocator = std::allocator_traits<allocator_type>::select_on_container_copy_construction(
883 rhs.get_allocator()
884 );
885 assign(rhs.begin(), rhs.end());
886 return *this;
887 }
888
889 template <class T, std::size_t N, class A, bool Init>
890 inline svector<T, N, A, Init>::svector(const svector& rhs)
891 : m_allocator(
892 std::allocator_traits<allocator_type>::select_on_container_copy_construction(rhs.get_allocator())
893 )
894 {
895 assign(rhs.begin(), rhs.end());
896 }
897
898 template <class T, std::size_t N, class A, bool Init>
899 inline svector<T, N, A, Init>::svector(svector&& rhs
900 ) noexcept(std::is_nothrow_move_constructible<value_type>::value)
901 {
902 this->swap(rhs);
903 }
904
905 template <class T, std::size_t N, class A, bool Init>
906 inline void svector<T, N, A, Init>::assign(size_type n, const value_type& v)
907 {
908 if (n > N && n > capacity())
909 {
910 grow(n);
911 }
912 m_end = m_begin + n;
913 std::fill(begin(), end(), v);
914 }
915
916 template <class T, std::size_t N, class A, bool Init>
917 template <class V>
918 inline void svector<T, N, A, Init>::assign(std::initializer_list<V> il)
919 {
920 assign(il.begin(), il.end());
921 }
922
923 template <class T, std::size_t N, class A, bool Init>
924 template <class IT>
925 inline void svector<T, N, A, Init>::assign(IT other_begin, IT other_end)
926 {
927 std::size_t size = static_cast<std::size_t>(other_end - other_begin);
928 if (size > N && size > capacity())
929 {
930 grow(size);
931 }
932 std::uninitialized_copy(other_begin, other_end, m_begin);
933 m_end = m_begin + size;
934 }
935
936 template <class T, std::size_t N, class A, bool Init>
937 inline auto svector<T, N, A, Init>::operator[](size_type idx) -> reference
938 {
939 return m_begin[idx];
940 }
941
942 template <class T, std::size_t N, class A, bool Init>
943 inline auto svector<T, N, A, Init>::operator[](size_type idx) const -> const_reference
944 {
945 return m_begin[idx];
946 }
947
948 template <class T, std::size_t N, class A, bool Init>
949 inline auto svector<T, N, A, Init>::at(size_type idx) -> reference
950 {
951 if (idx >= size())
952 {
953 XTENSOR_THROW(std::out_of_range, "Out of range in svector access");
954 }
955 return this->operator[](idx);
956 }
957
958 template <class T, std::size_t N, class A, bool Init>
959 inline auto svector<T, N, A, Init>::at(size_type idx) const -> const_reference
960 {
961 if (idx >= size())
962 {
963 XTENSOR_THROW(std::out_of_range, "Out of range in svector access");
964 }
965 return this->operator[](idx);
966 }
967
968 template <class T, std::size_t N, class A, bool Init>
969 inline auto svector<T, N, A, Init>::data() -> pointer
970 {
971 return m_begin;
972 }
973
974 template <class T, std::size_t N, class A, bool Init>
975 inline auto svector<T, N, A, Init>::data() const -> const_pointer
976 {
977 return m_begin;
978 }
979
980 template <class T, std::size_t N, class A, bool Init>
981 void svector<T, N, A, Init>::resize(size_type n)
982 {
983 if (n > N && n > capacity())
984 {
985 grow(n);
986 }
987 size_type old_size = size();
988 m_end = m_begin + n;
989 if (Init && old_size < size())
990 {
991 std::fill(begin() + old_size, end(), T());
992 }
993 }
994
995 template <class T, std::size_t N, class A, bool Init>
996 inline auto svector<T, N, A, Init>::max_size() const noexcept -> size_type
997 {
998 return m_allocator.max_size();
999 }
1000
1001 template <class T, std::size_t N, class A, bool Init>
1002 inline auto svector<T, N, A, Init>::capacity() const -> size_type
1003 {
1004 return static_cast<std::size_t>(m_capacity - m_begin);
1005 }
1006
1007 template <class T, std::size_t N, class A, bool Init>
1008 inline void svector<T, N, A, Init>::reserve(size_type n)
1009 {
1010 if (n > N && n > capacity())
1011 {
1012 grow(n);
1013 }
1014 }
1015
1016 template <class T, std::size_t N, class A, bool Init>
1017 inline void svector<T, N, A, Init>::shrink_to_fit()
1018 {
1019 // No op for now
1020 }
1021
1022 template <class T, std::size_t N, class A, bool Init>
1023 inline void svector<T, N, A, Init>::clear()
1024 {
1025 resize(size_type(0));
1026 }
1027
1028 template <class T, std::size_t N, class A, bool Init>
1029 void svector<T, N, A, Init>::push_back(const T& elt)
1030 {
1031 if (m_end >= m_capacity)
1032 {
1033 grow();
1034 }
1035 *(m_end++) = elt;
1036 }
1037
1038 template <class T, std::size_t N, class A, bool Init>
1039 void svector<T, N, A, Init>::push_back(T&& elt)
1040 {
1041 if (m_end >= m_capacity)
1042 {
1043 grow();
1044 }
1045 *(m_end++) = std::move(elt);
1046 }
1047
1048 template <class T, std::size_t N, class A, bool Init>
1049 void svector<T, N, A, Init>::pop_back()
1050 {
1051 --m_end;
1052 }
1053
1054 template <class T, std::size_t N, class A, bool Init>
1055 inline auto svector<T, N, A, Init>::begin() -> iterator
1056 {
1057 return m_begin;
1058 }
1059
1060 template <class T, std::size_t N, class A, bool Init>
1061 inline auto svector<T, N, A, Init>::begin() const -> const_iterator
1062 {
1063 return m_begin;
1064 }
1065
1066 template <class T, std::size_t N, class A, bool Init>
1067 inline auto svector<T, N, A, Init>::cbegin() const -> const_iterator
1068 {
1069 return m_begin;
1070 }
1071
1072 template <class T, std::size_t N, class A, bool Init>
1073 inline auto svector<T, N, A, Init>::end() -> iterator
1074 {
1075 return m_end;
1076 }
1077
1078 template <class T, std::size_t N, class A, bool Init>
1079 inline auto svector<T, N, A, Init>::end() const -> const_iterator
1080 {
1081 return m_end;
1082 }
1083
1084 template <class T, std::size_t N, class A, bool Init>
1085 inline auto svector<T, N, A, Init>::cend() const -> const_iterator
1086 {
1087 return m_end;
1088 }
1089
1090 template <class T, std::size_t N, class A, bool Init>
1091 inline auto svector<T, N, A, Init>::rbegin() -> reverse_iterator
1092 {
1093 return reverse_iterator(m_end);
1094 }
1095
1096 template <class T, std::size_t N, class A, bool Init>
1097 inline auto svector<T, N, A, Init>::rbegin() const -> const_reverse_iterator
1098 {
1099 return const_reverse_iterator(m_end);
1100 }
1101
1102 template <class T, std::size_t N, class A, bool Init>
1103 inline auto svector<T, N, A, Init>::crbegin() const -> const_reverse_iterator
1104 {
1105 return const_reverse_iterator(m_end);
1106 }
1107
1108 template <class T, std::size_t N, class A, bool Init>
1109 inline auto svector<T, N, A, Init>::rend() -> reverse_iterator
1110 {
1111 return reverse_iterator(m_begin);
1112 }
1113
1114 template <class T, std::size_t N, class A, bool Init>
1115 inline auto svector<T, N, A, Init>::rend() const -> const_reverse_iterator
1116 {
1117 return const_reverse_iterator(m_begin);
1118 }
1119
1120 template <class T, std::size_t N, class A, bool Init>
1121 inline auto svector<T, N, A, Init>::crend() const -> const_reverse_iterator
1122 {
1123 return const_reverse_iterator(m_begin);
1124 }
1125
1126 template <class T, std::size_t N, class A, bool Init>
1127 inline auto svector<T, N, A, Init>::size() const -> size_type
1128 {
1129 return static_cast<size_type>(m_end - m_begin);
1130 }
1131
1132 template <class T, std::size_t N, class A, bool Init>
1133 inline auto svector<T, N, A, Init>::empty() const -> bool
1134 {
1135 return m_begin == m_end;
1136 }
1137
1138 template <class T, std::size_t N, class A, bool Init>
1139 inline auto svector<T, N, A, Init>::front() -> reference
1140 {
1141 XTENSOR_ASSERT(!empty());
1142 return m_begin[0];
1143 }
1144
1145 template <class T, std::size_t N, class A, bool Init>
1146 inline auto svector<T, N, A, Init>::front() const -> const_reference
1147 {
1148 XTENSOR_ASSERT(!empty());
1149 return m_begin[0];
1150 }
1151
1152 template <class T, std::size_t N, class A, bool Init>
1153 inline auto svector<T, N, A, Init>::back() -> reference
1154 {
1155 XTENSOR_ASSERT(!empty());
1156 return m_end[-1];
1157 }
1158
1159 template <class T, std::size_t N, class A, bool Init>
1160 inline auto svector<T, N, A, Init>::back() const -> const_reference
1161 {
1162 XTENSOR_ASSERT(!empty());
1163 return m_end[-1];
1164 }
1165
1166 template <class T, std::size_t N, class A, bool Init>
1167 inline auto svector<T, N, A, Init>::on_stack() -> bool
1168 {
1169 return m_begin == &m_data[0];
1170 }
1171
1172 template <class T, std::size_t N, class A, bool Init>
1173 inline auto svector<T, N, A, Init>::get_allocator() const noexcept -> allocator_type
1174 {
1175 return m_allocator;
1176 }
1177
1178 template <class T, std::size_t N, class A, bool Init>
1179 inline auto svector<T, N, A, Init>::erase(const_iterator cit) -> iterator
1180 {
1181 auto it = const_cast<pointer>(cit);
1182 iterator ret_val = it;
1183 std::move(it + 1, m_end, it);
1184 --m_end;
1185 return ret_val;
1186 }
1187
1188 template <class T, std::size_t N, class A, bool Init>
1189 inline auto svector<T, N, A, Init>::erase(const_iterator cfirst, const_iterator clast) -> iterator
1190 {
1191 auto first = const_cast<pointer>(cfirst);
1192 auto last = const_cast<pointer>(clast);
1193 if (last == m_end)
1194 {
1195 m_end = first;
1196 return first;
1197 }
1198
1199 iterator new_end = std::move(last, m_end, first);
1200 m_end = new_end;
1201 return first;
1202 }
1203
1204 template <class T, std::size_t N, class A, bool Init>
1205 inline auto svector<T, N, A, Init>::insert(const_iterator cit, const T& elt) -> iterator
1206 {
1207 auto it = const_cast<pointer>(cit);
1208 if (it == m_end)
1209 {
1210 push_back(elt);
1211 return m_end - 1;
1212 }
1213
1214 if (m_end >= m_capacity)
1215 {
1216 std::ptrdiff_t elt_no = it - m_begin;
1217 grow();
1218 it = m_begin + elt_no;
1219 }
1220
1221 (*m_end) = back();
1222 std::move_backward(it, m_end - 1, m_end);
1223 ++m_end;
1224
1225 // Update ref if element moved
1226 const T* elt_ptr = &elt;
1227 bool cond = it <= elt_ptr && elt_ptr < m_end;
1228 // More complicated than incrementing elt_ptr, but this avoids
1229 // false positive array-bounds warning on GCC 10
1230 const T* src_ptr = cond ? it + (elt_ptr - it) + std::ptrdiff_t(1) : elt_ptr;
1231 *it = *src_ptr;
1232 return it;
1233 }
1234
1235 template <class T, std::size_t N, class A, bool Init>
1236 template <class It>
1237 inline auto svector<T, N, A, Init>::insert(const_iterator pos, It first, It last) -> iterator
1238 {
1239 auto it = const_cast<pointer>(pos);
1240 difference_type n = std::distance(first, last);
1241 if (n > 0)
1242 {
1243 if (n > m_capacity - m_end)
1244 {
1245 std::ptrdiff_t elt_no = it - m_begin;
1246 grow(static_cast<size_t>((m_capacity - m_begin) + n));
1247 it = m_begin + elt_no;
1248 }
1249
1250 std::move_backward(it, m_end, m_end + n);
1251 m_end += n;
1252 std::copy(first, last, it);
1253 }
1254 return it;
1255 }
1256
1257 template <class T, std::size_t N, class A, bool Init>
1258 inline auto svector<T, N, A, Init>::insert(const_iterator pos, std::initializer_list<T> l) -> iterator
1259 {
1260 return insert(pos, l.begin(), l.end());
1261 }
1262
1263 template <class T, std::size_t N, class A, bool Init>
1264 inline void svector<T, N, A, Init>::destroy_range(T* begin, T* end)
1265 {
1266 if (!xtrivially_default_constructible<T>::value)
1267 {
1268 while (begin != end)
1269 {
1270 --end;
1271 end->~T();
1272 }
1273 }
1274 }
1275
1276 template <class T, std::size_t N, class A, bool Init>
1277 template <std::size_t ON, class OA, bool InitA>
1278 inline void svector<T, N, A, Init>::swap(svector<T, ON, OA, InitA>& rhs)
1279 {
1280 using std::swap;
1281 if (this == &rhs)
1282 {
1283 return;
1284 }
1285
1286 // We can only avoid copying elements if neither vector is small.
1287 if (!this->on_stack() && !rhs.on_stack())
1288 {
1289 swap(this->m_begin, rhs.m_begin);
1290 swap(this->m_end, rhs.m_end);
1291 swap(this->m_capacity, rhs.m_capacity);
1292 return;
1293 }
1294
1295 size_type rhs_old_size = rhs.size();
1296 size_type old_size = this->size();
1297
1298 if (rhs_old_size > old_size)
1299 {
1300 this->resize(rhs_old_size);
1301 }
1302 else if (old_size > rhs_old_size)
1303 {
1304 rhs.resize(old_size);
1305 }
1306
1307 // Swap the shared elements.
1308 size_type min_size = (std::min)(old_size, rhs_old_size);
1309 for (size_type i = 0; i < min_size; ++i)
1310 {
1311 swap((*this)[i], rhs[i]);
1312 }
1313
1314 // Copy over the extra elts.
1315 if (old_size > rhs_old_size)
1316 {
1317 std::copy(this->begin() + min_size, this->end(), rhs.begin() + min_size);
1318 this->destroy_range(this->begin() + min_size, this->end());
1319 this->m_end = this->begin() + min_size;
1320 }
1321 else if (rhs_old_size > old_size)
1322 {
1323 std::copy(rhs.begin() + min_size, rhs.end(), this->begin() + min_size);
1324 this->destroy_range(rhs.begin() + min_size, rhs.end());
1325 rhs.m_end = rhs.begin() + min_size;
1326 }
1327 }
1328
1329 template <class T, std::size_t N, class A, bool Init>
1330 inline void svector<T, N, A, Init>::grow(size_type min_capacity)
1331 {
1332 size_type current_size = size();
1333 size_type new_capacity = 2 * current_size + 1; // Always grow.
1334 if (new_capacity < min_capacity)
1335 {
1336 new_capacity = min_capacity;
1337 }
1338
1339 T* new_alloc;
1340 // is data stack allocated?
1341 if (m_begin == &m_data[0])
1342 {
1343 new_alloc = m_allocator.allocate(new_capacity);
1344 std::uninitialized_copy(m_begin, m_end, new_alloc);
1345 }
1346 else
1347 {
1348 // If this wasn't grown from the inline copy, grow the allocated space.
1349 new_alloc = m_allocator.allocate(new_capacity);
1350 std::uninitialized_copy(m_begin, m_end, new_alloc);
1351 m_allocator.deallocate(m_begin, std::size_t(m_capacity - m_begin));
1352 }
1353 XTENSOR_ASSERT(new_alloc);
1354
1355 m_end = new_alloc + current_size;
1356 m_begin = new_alloc;
1357 m_capacity = new_alloc + new_capacity;
1358 }
1359
1360 template <class T, std::size_t N, class A, bool Init>
1361 inline bool operator==(const std::vector<T>& lhs, const svector<T, N, A, Init>& rhs)
1362 {
1363 return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
1364 }
1365
1366 template <class T, std::size_t N, class A, bool Init>
1367 inline bool operator==(const svector<T, N, A, Init>& lhs, const std::vector<T>& rhs)
1368 {
1369 return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
1370 }
1371
1372 template <class T, std::size_t N, class A, bool Init>
1373 inline bool operator==(const svector<T, N, A, Init>& lhs, const svector<T, N, A, Init>& rhs)
1374 {
1375 return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
1376 }
1377
1378 template <class T, std::size_t N, class A, bool Init>
1379 inline bool operator!=(const svector<T, N, A, Init>& lhs, const svector<T, N, A, Init>& rhs)
1380 {
1381 return !(lhs == rhs);
1382 }
1383
1384 template <class T, std::size_t N, class A, bool Init>
1385 inline bool operator<(const svector<T, N, A, Init>& lhs, const svector<T, N, A, Init>& rhs)
1386 {
1387 return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
1388 }
1389
1390 template <class T, std::size_t N, class A, bool Init>
1391 inline bool operator<=(const svector<T, N, A, Init>& lhs, const svector<T, N, A, Init>& rhs)
1392 {
1393 return !(lhs > rhs);
1394 }
1395
1396 template <class T, std::size_t N, class A, bool Init>
1397 inline bool operator>(const svector<T, N, A, Init>& lhs, const svector<T, N, A, Init>& rhs)
1398 {
1399 return rhs < lhs;
1400 }
1401
1402 template <class T, std::size_t N, class A, bool Init>
1403 inline bool operator>=(const svector<T, N, A, Init>& lhs, const svector<T, N, A, Init>& rhs)
1404 {
1405 return !(lhs < rhs);
1406 }
1407
1408 template <class T, std::size_t N, class A, bool Init>
1409 inline void swap(svector<T, N, A, Init>& lhs, svector<T, N, A, Init>& rhs) noexcept
1410 {
1411 lhs.swap(rhs);
1412 }
1413
1414 template <class X, class T, std::size_t N, class A, bool B>
1415 struct rebind_container<X, svector<T, N, A, B>>
1416 {
1417 using traits = std::allocator_traits<A>;
1418 using allocator = typename traits::template rebind_alloc<X>;
1420 };
1421
1428 template <class T, std::size_t N, std::size_t Align = XTENSOR_SELECT_ALIGN(T)>
1429 class alignas(Align) aligned_array : public std::array<T, N>
1430 {
1431 public:
1432
1433 // Note: this is for alignment detection. The allocator serves no other purpose than
1434 // that of a trait here.
1435 using allocator_type = std::conditional_t<Align != 0, xt_simd::aligned_allocator<T, Align>, std::allocator<T>>;
1436 };
1437
1438#if defined(_MSC_VER)
1439#define XTENSOR_CONST
1440#else
1441#define XTENSOR_CONST const
1442#endif
1443
1444#if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__)
1445#define GCC4_FALLBACK
1446
1447 namespace const_array_detail
1448 {
1449 template <class T, std::size_t N>
1450 struct array_traits
1451 {
1452 using storage_type = T[N];
1453
1454 static constexpr T& ref(const storage_type& t, std::size_t n) noexcept
1455 {
1456 return const_cast<T&>(t[n]);
1457 }
1458
1459 static constexpr T* ptr(const storage_type& t) noexcept
1460 {
1461 return const_cast<T*>(t);
1462 }
1463 };
1464
1465 template <class T>
1466 struct array_traits<T, 0>
1467 {
1468 struct empty
1469 {
1470 };
1471
1472 using storage_type = empty;
1473
1474 static constexpr T& ref(const storage_type& /*t*/, std::size_t /*n*/) noexcept
1475 {
1476 return *static_cast<T*>(nullptr);
1477 }
1478
1479 static constexpr T* ptr(const storage_type& /*t*/) noexcept
1480 {
1481 return nullptr;
1482 }
1483 };
1484 }
1485#endif
1486
1491 template <class T, std::size_t N>
1493 {
1494 using size_type = std::size_t;
1495 using value_type = T;
1496 using pointer = value_type*;
1497 using const_pointer = const value_type*;
1498 using reference = value_type&;
1499 using const_reference = const value_type&;
1500 using difference_type = std::ptrdiff_t;
1501 using iterator = pointer;
1502 using const_iterator = const_pointer;
1503
1504 using reverse_iterator = std::reverse_iterator<const_iterator>;
1505 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
1506
1507 constexpr const_reference operator[](std::size_t idx) const
1508 {
1509#ifdef GCC4_FALLBACK
1510 return const_array_detail::array_traits<T, N>::ref(m_data, idx);
1511#else
1512 return m_data[idx];
1513#endif
1514 }
1515
1516 constexpr const_iterator begin() const noexcept
1517 {
1518 return cbegin();
1519 }
1520
1521 constexpr const_iterator end() const noexcept
1522 {
1523 return cend();
1524 }
1525
1526 constexpr const_iterator cbegin() const noexcept
1527 {
1528 return data();
1529 }
1530
1531 constexpr const_iterator cend() const noexcept
1532 {
1533 return data() + N;
1534 }
1535
1536 // TODO make constexpr once C++17 arrives
1537 reverse_iterator rbegin() const noexcept
1538 {
1539 return crbegin();
1540 }
1541
1542 reverse_iterator rend() const noexcept
1543 {
1544 return crend();
1545 }
1546
1547 const_reverse_iterator crbegin() const noexcept
1548 {
1549 return const_reverse_iterator(end());
1550 }
1551
1552 const_reverse_iterator crend() const noexcept
1553 {
1554 return const_reverse_iterator(begin());
1555 }
1556
1557 constexpr const_pointer data() const noexcept
1558 {
1559#ifdef GCC4_FALLBACK
1560 return const_array_detail::array_traits<T, N>::ptr(m_data);
1561#else
1562 return m_data;
1563#endif
1564 }
1565
1566 constexpr const_reference front() const noexcept
1567 {
1568#ifdef GCC4_FALLBACK
1569 return const_array_detail::array_traits<T, N>::ref(m_data, 0);
1570#else
1571 return m_data[0];
1572#endif
1573 }
1574
1575 constexpr const_reference back() const noexcept
1576 {
1577#ifdef GCC4_FALLBACK
1578 return N ? const_array_detail::array_traits<T, N>::ref(m_data, N - 1)
1579 : const_array_detail::array_traits<T, N>::ref(m_data, 0);
1580#else
1581 return m_data[size() - 1];
1582#endif
1583 }
1584
1585 constexpr bool empty() const noexcept
1586 {
1587 return size() == size_type(0);
1588 }
1589
1590 constexpr size_type size() const noexcept
1591 {
1592 return N;
1593 }
1594
1595#ifdef GCC4_FALLBACK
1596 XTENSOR_CONST typename const_array_detail::array_traits<T, N>::storage_type m_data;
1597#else
1598 XTENSOR_CONST T m_data[N > 0 ? N : 1];
1599#endif
1600 };
1601
1602#undef GCC4_FALLBACK
1603
1604 template <class T, std::size_t N>
1605 inline bool operator==(const const_array<T, N>& lhs, const const_array<T, N>& rhs)
1606 {
1607 return std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin());
1608 }
1609
1610 template <class T, std::size_t N>
1611 inline bool operator!=(const const_array<T, N>& lhs, const const_array<T, N>& rhs)
1612 {
1613 return !(lhs == rhs);
1614 }
1615
1616 template <class T, std::size_t N>
1617 inline bool operator<(const const_array<T, N>& lhs, const const_array<T, N>& rhs)
1618 {
1619 return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
1620 }
1621
1622 template <class T, std::size_t N>
1623 inline bool operator<=(const const_array<T, N>& lhs, const const_array<T, N>& rhs)
1624 {
1625 return !(lhs > rhs);
1626 }
1627
1628 template <class T, std::size_t N>
1629 inline bool operator>(const const_array<T, N>& lhs, const const_array<T, N>& rhs)
1630 {
1631 return rhs < lhs;
1632 }
1633
1634 template <class T, std::size_t N>
1635 inline bool operator>=(const const_array<T, N>& lhs, const const_array<T, N>& rhs)
1636 {
1637 return !(lhs < rhs);
1638 }
1639
1640// Workaround for rebind_container problems on GCC 8 with C++17 enabled
1641#if defined(__GNUC__) && __GNUC__ > 6 && !defined(__clang__) && __cplusplus >= 201703L
1642 template <class X, class T, std::size_t N>
1643 struct rebind_container<X, aligned_array<T, N>>
1644 {
1645 using type = aligned_array<X, N>;
1646 };
1647
1648 template <class X, class T, std::size_t N>
1649 struct rebind_container<X, const_array<T, N>>
1650 {
1651 using type = const_array<X, N>;
1652 };
1653#endif
1654
1660 template <std::size_t... X>
1662 {
1663 public:
1664
1665#if defined(_MSC_VER)
1666 using cast_type = std::array<std::size_t, sizeof...(X)>;
1667#define XTENSOR_FIXED_SHAPE_CONSTEXPR inline
1668#else
1669 using cast_type = const_array<std::size_t, sizeof...(X)>;
1670#define XTENSOR_FIXED_SHAPE_CONSTEXPR constexpr
1671#endif
1672 using value_type = std::size_t;
1673 using size_type = std::size_t;
1674 using const_iterator = typename cast_type::const_iterator;
1675
1676 static constexpr std::size_t size()
1677 {
1678 return sizeof...(X);
1679 }
1680
1681 template <std::size_t idx>
1682 static constexpr auto get()
1683 {
1684 using tmp_cast_type = std::array<std::size_t, sizeof...(X)>;
1685 return std::get<idx>(tmp_cast_type{X...});
1686 }
1687
1688 XTENSOR_FIXED_SHAPE_CONSTEXPR operator cast_type() const
1689 {
1690 return cast_type({X...});
1691 }
1692
1693 XTENSOR_FIXED_SHAPE_CONSTEXPR auto begin() const
1694 {
1695 return m_array.begin();
1696 }
1697
1698 XTENSOR_FIXED_SHAPE_CONSTEXPR auto end() const
1699 {
1700 return m_array.end();
1701 }
1702
1703 auto rbegin() const
1704 {
1705 return m_array.rbegin();
1706 }
1707
1708 auto rend() const
1709 {
1710 return m_array.rend();
1711 }
1712
1713 XTENSOR_FIXED_SHAPE_CONSTEXPR auto cbegin() const
1714 {
1715 return m_array.cbegin();
1716 }
1717
1718 XTENSOR_FIXED_SHAPE_CONSTEXPR auto cend() const
1719 {
1720 return m_array.cend();
1721 }
1722
1723 XTENSOR_FIXED_SHAPE_CONSTEXPR std::size_t operator[](std::size_t idx) const
1724 {
1725 return m_array[idx];
1726 }
1727
1728 XTENSOR_FIXED_SHAPE_CONSTEXPR bool empty() const
1729 {
1730 return sizeof...(X) == 0;
1731 }
1732
1733 private:
1734
1735 XTENSOR_CONSTEXPR_ENHANCED_STATIC cast_type m_array = cast_type({X...});
1736 };
1737
1738#ifdef XTENSOR_HAS_CONSTEXPR_ENHANCED
1739 template <std::size_t... X>
1740 constexpr typename fixed_shape<X...>::cast_type fixed_shape<X...>::m_array;
1741#endif
1742
1743#undef XTENSOR_FIXED_SHAPE_CONSTEXPR
1744
1745 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End = -1>
1747 {
1748 public:
1749
1750 using value_type = typename E::value_type;
1751 using reference = typename E::reference;
1752 using const_reference = typename E::const_reference;
1753 using pointer = typename E::pointer;
1754 using const_pointer = typename E::const_pointer;
1755
1756 using size_type = typename E::size_type;
1757 using difference_type = typename E::difference_type;
1758
1759 using iterator = typename E::iterator;
1760 using const_iterator = typename E::const_iterator;
1761 using reverse_iterator = typename E::reverse_iterator;
1762 using const_reverse_iterator = typename E::const_reverse_iterator;
1763
1764 explicit sequence_view(const E& container);
1765
1766 template <std::ptrdiff_t OS, std::ptrdiff_t OE>
1767 explicit sequence_view(const sequence_view<E, OS, OE>& other);
1768
1770 operator T() const;
1771
1772 bool empty() const;
1773 size_type size() const;
1774 const_reference operator[](std::size_t idx) const;
1775
1776 const_iterator end() const;
1777 const_iterator begin() const;
1778 const_iterator cend() const;
1779 const_iterator cbegin() const;
1780
1781 const_reverse_iterator rend() const;
1782 const_reverse_iterator rbegin() const;
1783 const_reverse_iterator crend() const;
1784 const_reverse_iterator crbegin() const;
1785
1786 const_reference front() const;
1787 const_reference back() const;
1788
1789 const E& storage() const;
1790
1791 private:
1792
1793 const E& m_sequence;
1794 };
1795
1796 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1798 : m_sequence(container)
1799 {
1800 }
1801
1802 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1803 template <std::ptrdiff_t OS, std::ptrdiff_t OE>
1804 sequence_view<E, Start, End>::sequence_view(const sequence_view<E, OS, OE>& other)
1805 : m_sequence(other.storage())
1806 {
1807 }
1808
1809 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1810 template <class T, class R>
1811 sequence_view<E, Start, End>::operator T() const
1812 {
1813 T ret = xtl::make_sequence<T>(this->size());
1814 std::copy(this->cbegin(), this->cend(), ret.begin());
1815 return ret;
1816 }
1817
1818 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1819 bool sequence_view<E, Start, End>::empty() const
1820 {
1821 return size() == size_type(0);
1822 }
1823
1824 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1825 auto sequence_view<E, Start, End>::size() const -> size_type
1826 {
1827 if (End == -1)
1828 {
1829 return m_sequence.size() - static_cast<size_type>(Start);
1830 }
1831 else
1832 {
1833 return static_cast<size_type>(End - Start);
1834 }
1835 }
1836
1837 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1838 auto sequence_view<E, Start, End>::operator[](std::size_t idx) const -> const_reference
1839 {
1840 return m_sequence[idx + static_cast<std::size_t>(Start)];
1841 }
1842
1843 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1844 auto sequence_view<E, Start, End>::end() const -> const_iterator
1845 {
1846 if (End != -1)
1847 {
1848 return m_sequence.begin() + End;
1849 }
1850 else
1851 {
1852 return m_sequence.end();
1853 }
1854 }
1855
1856 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1857 auto sequence_view<E, Start, End>::begin() const -> const_iterator
1858 {
1859 return m_sequence.begin() + Start;
1860 }
1861
1862 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1863 auto sequence_view<E, Start, End>::cend() const -> const_iterator
1864 {
1865 return end();
1866 }
1867
1868 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1869 auto sequence_view<E, Start, End>::cbegin() const -> const_iterator
1870 {
1871 return begin();
1872 }
1873
1874 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1875 auto sequence_view<E, Start, End>::rend() const -> const_reverse_iterator
1876 {
1877 return const_reverse_iterator(begin());
1878 }
1879
1880 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1881 auto sequence_view<E, Start, End>::rbegin() const -> const_reverse_iterator
1882 {
1883 return const_reverse_iterator(end());
1884 }
1885
1886 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1887 auto sequence_view<E, Start, End>::crend() const -> const_reverse_iterator
1888 {
1889 return rend();
1890 }
1891
1892 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1893 auto sequence_view<E, Start, End>::crbegin() const -> const_reverse_iterator
1894 {
1895 return rbegin();
1896 }
1897
1898 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1899 auto sequence_view<E, Start, End>::front() const -> const_reference
1900 {
1901 return *(m_sequence.begin() + Start);
1902 }
1903
1904 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1905 auto sequence_view<E, Start, End>::back() const -> const_reference
1906 {
1907 if (End == -1)
1908 {
1909 return m_sequence.back();
1910 }
1911 else
1912 {
1913 return m_sequence[static_cast<std::size_t>(End - 1)];
1914 }
1915 }
1916
1917 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1918 const E& sequence_view<E, Start, End>::storage() const
1919 {
1920 return m_sequence;
1921 }
1922
1923 template <class T, std::ptrdiff_t TB, std::ptrdiff_t TE>
1924 inline bool operator==(const sequence_view<T, TB, TE>& lhs, const sequence_view<T, TB, TE>& rhs)
1925 {
1926 return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
1927 }
1928
1929 template <class T, std::ptrdiff_t TB, std::ptrdiff_t TE>
1930 inline bool operator!=(const sequence_view<T, TB, TE>& lhs, const sequence_view<T, TB, TE>& rhs)
1931 {
1932 return !(lhs == rhs);
1933 }
1934}
1935
1936/******************************
1937 * std::tuple_size extensions *
1938 ******************************/
1939
1940// The C++ standard defines tuple_size as a class, however
1941// G++ 8 C++ library does define it as a struct hence we get
1942// clang warnings here
1943
1944// Do not remove space between "#" and "pragma". This is required for CRAN checks.
1945// clang-format off
1946#if defined(__clang__)
1947 # pragma clang diagnostic push
1948 # pragma clang diagnostic ignored "-Wmismatched-tags"
1949#endif
1950// clang-format on
1951
1952namespace std
1953{
1954 template <class T, std::size_t N>
1955 class tuple_size<xt::const_array<T, N>> : public integral_constant<std::size_t, N>
1956 {
1957 };
1958
1959 template <std::size_t... N>
1960 class tuple_size<xt::fixed_shape<N...>> : public integral_constant<std::size_t, sizeof...(N)>
1961 {
1962 };
1963
1964 template <class T, std::ptrdiff_t Start, std::ptrdiff_t End>
1965 class tuple_size<xt::sequence_view<T, Start, End>>
1966 : public integral_constant<std::size_t, std::size_t(End - Start)>
1967 {
1968 };
1969
1970 // Undefine tuple size for not-known sequence view size
1971 template <class T, std::ptrdiff_t Start>
1972 class tuple_size<xt::sequence_view<T, Start, -1>>;
1973}
1974
1975// Do not remove space between "#" and "pragma". This is required for CRAN checks.
1976// clang-format off
1977#if defined(__clang__)
1978 # pragma clang diagnostic pop
1979#endif
1980// clang-format on
1981
1982#undef XTENSOR_CONST
1983
1984#endif
This array class is modeled after std::array but adds optional alignment through a template parameter...
Fixed shape implementation for compile time defined arrays.
standard mathematical functions for xexpressions
bool operator==(const xaxis_iterator< CT > &lhs, const xaxis_iterator< CT > &rhs)
Checks equality of the iterators.
bool operator!=(const xaxis_iterator< CT > &lhs, const xaxis_iterator< CT > &rhs)
Checks inequality of the iterators.
xarray< T, L > empty(const S &shape)
Create a xcontainer (xarray, xtensor or xtensor_fixed) with uninitialized values of with value_type T...
Definition xbuilder.hpp:89
A std::array like class with all member function (except reverse iterators) as constexpr.