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