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 (!std::is_trivially_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 (!std::is_trivially_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 (std::is_trivially_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 static constexpr std::size_t alignment = detail::allocator_alignment<A>::value != 0
653 ? detail::allocator_alignment<A>::value
654 : alignof(T);
655
656 svector() noexcept;
657 ~svector();
658
659 explicit svector(const allocator_type& alloc) noexcept;
660 explicit svector(size_type n, const allocator_type& alloc = allocator_type());
661 svector(size_type n, const value_type& v, const allocator_type& alloc = allocator_type());
662 svector(std::initializer_list<T> il, const allocator_type& alloc = allocator_type());
663
664 svector(const std::vector<T>& vec);
665
666 template <std::input_iterator IT>
667 svector(IT begin, IT end, const allocator_type& alloc = allocator_type());
668
669 template <std::size_t N2, bool I2>
670 explicit svector(const svector<T, N2, A, I2>& rhs)
671 requires(N != N2);
672
673 svector& operator=(const svector& rhs);
674 svector& operator=(svector&& rhs) noexcept(std::is_nothrow_move_assignable<value_type>::value);
675 svector& operator=(const std::vector<T>& rhs);
676 svector& operator=(std::initializer_list<T> il);
677
678 template <std::size_t N2, bool I2>
679 svector& operator=(const svector<T, N2, A, I2>& rhs)
680 requires(N != N2);
681
682 svector(const svector& other);
683 svector(svector&& other) noexcept(std::is_nothrow_move_constructible<value_type>::value);
684
685 void assign(size_type n, const value_type& v);
686
687 template <class V>
688 void assign(std::initializer_list<V> il);
689
690 template <class IT>
691 void assign(IT other_begin, IT other_end);
692
693 reference operator[](size_type idx);
694 const_reference operator[](size_type idx) const;
695
696 reference at(size_type idx);
697 const_reference at(size_type idx) const;
698
699 pointer data();
700 const_pointer data() const;
701
702 void push_back(const T& elt);
703 void push_back(T&& elt);
704 void pop_back();
705
706 iterator begin();
707 const_iterator begin() const;
708 const_iterator cbegin() const;
709 iterator end();
710 const_iterator end() const;
711 const_iterator cend() const;
712
713 reverse_iterator rbegin();
714 const_reverse_iterator rbegin() const;
715 const_reverse_iterator crbegin() const;
716 reverse_iterator rend();
717 const_reverse_iterator rend() const;
718 const_reverse_iterator crend() const;
719
720 bool empty() const;
721 size_type size() const;
722 void resize(size_type n);
723 size_type max_size() const noexcept;
724 size_type capacity() const;
725 void reserve(size_type n);
726 void shrink_to_fit();
727 void clear();
728
729 reference front();
730 const_reference front() const;
731 reference back();
732 const_reference back() const;
733
734 bool on_stack();
735
736 iterator erase(const_iterator cit);
737 iterator erase(const_iterator cfirst, const_iterator clast);
738
739 iterator insert(const_iterator it, const T& elt);
740
741 template <class It>
742 iterator insert(const_iterator pos, It first, It last);
743
744 iterator insert(const_iterator pos, std::initializer_list<T> l);
745
746 template <std::size_t ON, class OA, bool InitA>
747 void swap(svector<T, ON, OA, InitA>& rhs);
748
749 allocator_type get_allocator() const noexcept;
750
751 private:
752
753 A m_allocator;
754
755 T* m_begin = std::begin(m_data);
756 T* m_end = std::begin(m_data);
757 T* m_capacity = std::end(m_data);
758
759 // stack allocated memory
760 alignas(alignment) T m_data[N > 0 ? N : 1];
761
762 void grow(size_type min_capacity = 0);
763 void destroy_range(T* begin, T* end);
764 };
765
766 template <class T, std::size_t N, class A, bool Init>
767 inline svector<T, N, A, Init>::~svector()
768 {
769 if (!on_stack())
770 {
771 detail::safe_destroy_deallocate(m_allocator, m_begin, static_cast<std::size_t>(m_capacity - m_begin));
772 }
773 }
774
775 template <class T, std::size_t N, class A, bool Init>
776 inline svector<T, N, A, Init>::svector() noexcept
777 : svector(allocator_type())
778 {
779 }
780
781 template <class T, std::size_t N, class A, bool Init>
782 inline svector<T, N, A, Init>::svector(const allocator_type& alloc) noexcept
783 : m_allocator(alloc)
784 {
785 }
786
787 template <class T, std::size_t N, class A, bool Init>
788 inline svector<T, N, A, Init>::svector(size_type n, const allocator_type& alloc)
789 : m_allocator(alloc)
790 {
791 if (Init)
792 {
793 assign(n, T(0));
794 }
795 else
796 {
797 resize(n);
798 }
799 }
800
801 template <class T, std::size_t N, class A, bool Init>
802 template <std::input_iterator IT>
803 inline svector<T, N, A, Init>::svector(IT begin, IT end, const allocator_type& alloc)
804 : m_allocator(alloc)
805 {
806 assign(begin, end);
807 }
808
809 template <class T, std::size_t N, class A, bool Init>
810 template <std::size_t N2, bool I2>
811 inline svector<T, N, A, Init>::svector(const svector<T, N2, A, I2>& rhs)
812 requires(N != N2)
813 : m_allocator(rhs.get_allocator())
814 {
815 assign(rhs.begin(), rhs.end());
816 }
817
818 template <class T, std::size_t N, class A, bool Init>
819 inline svector<T, N, A, Init>::svector(const std::vector<T>& vec)
820 {
821 assign(vec.begin(), vec.end());
822 }
823
824 template <class T, std::size_t N, class A, bool Init>
825 inline svector<T, N, A, Init>::svector(size_type n, const value_type& v, const allocator_type& alloc)
826 : m_allocator(alloc)
827 {
828 assign(n, v);
829 }
830
831 template <class T, std::size_t N, class A, bool Init>
832 inline svector<T, N, A, Init>::svector(std::initializer_list<T> il, const allocator_type& alloc)
833 : m_allocator(alloc)
834 {
835 assign(il.begin(), il.end());
836 }
837
838 template <class T, std::size_t N, class A, bool Init>
839 inline svector<T, N, A, Init>& svector<T, N, A, Init>::operator=(const svector& rhs)
840 {
841 assign(rhs.begin(), rhs.end());
842 return *this;
843 }
844
845 template <class T, std::size_t N, class A, bool Init>
846 inline svector<T, N, A, Init>& svector<T, N, A, Init>::operator=(svector&& rhs
847 ) noexcept(std::is_nothrow_move_assignable<value_type>::value)
848 {
849 assign(rhs.begin(), rhs.end());
850 return *this;
851 }
852
853 template <class T, std::size_t N, class A, bool Init>
854 inline svector<T, N, A, Init>& svector<T, N, A, Init>::operator=(const std::vector<T>& rhs)
855 {
856 m_allocator = std::allocator_traits<allocator_type>::select_on_container_copy_construction(
857 rhs.get_allocator()
858 );
859 assign(rhs.begin(), rhs.end());
860 return *this;
861 }
862
863 template <class T, std::size_t N, class A, bool Init>
864 inline svector<T, N, A, Init>& svector<T, N, A, Init>::operator=(std::initializer_list<T> il)
865 {
866 return operator=(self_type(il));
867 }
868
869 template <class T, std::size_t N, class A, bool Init>
870 template <std::size_t N2, bool I2>
871 inline svector<T, N, A, Init>& svector<T, N, A, Init>::operator=(const svector<T, N2, A, I2>& rhs)
872 requires(N != N2)
873 {
874 m_allocator = std::allocator_traits<allocator_type>::select_on_container_copy_construction(
875 rhs.get_allocator()
876 );
877 assign(rhs.begin(), rhs.end());
878 return *this;
879 }
880
881 template <class T, std::size_t N, class A, bool Init>
882 inline svector<T, N, A, Init>::svector(const svector& rhs)
883 : m_allocator(
884 std::allocator_traits<allocator_type>::select_on_container_copy_construction(rhs.get_allocator())
885 )
886 {
887 assign(rhs.begin(), rhs.end());
888 }
889
890 template <class T, std::size_t N, class A, bool Init>
891 inline svector<T, N, A, Init>::svector(svector&& rhs
892 ) noexcept(std::is_nothrow_move_constructible<value_type>::value)
893 {
894 this->swap(rhs);
895 }
896
897 template <class T, std::size_t N, class A, bool Init>
898 inline void svector<T, N, A, Init>::assign(size_type n, const value_type& v)
899 {
900 if (n > N && n > capacity())
901 {
902 grow(n);
903 }
904 m_end = m_begin + n;
905 std::fill(begin(), end(), v);
906 }
907
908 template <class T, std::size_t N, class A, bool Init>
909 template <class V>
910 inline void svector<T, N, A, Init>::assign(std::initializer_list<V> il)
911 {
912 assign(il.begin(), il.end());
913 }
914
915 template <class T, std::size_t N, class A, bool Init>
916 template <class IT>
917 inline void svector<T, N, A, Init>::assign(IT other_begin, IT other_end)
918 {
919 std::size_t size = static_cast<std::size_t>(other_end - other_begin);
920 if (size > N && size > capacity())
921 {
922 grow(size);
923 }
924 std::uninitialized_copy(other_begin, other_end, m_begin);
925 m_end = m_begin + size;
926 }
927
928 template <class T, std::size_t N, class A, bool Init>
929 inline auto svector<T, N, A, Init>::operator[](size_type idx) -> reference
930 {
931 return m_begin[idx];
932 }
933
934 template <class T, std::size_t N, class A, bool Init>
935 inline auto svector<T, N, A, Init>::operator[](size_type idx) const -> const_reference
936 {
937 return m_begin[idx];
938 }
939
940 template <class T, std::size_t N, class A, bool Init>
941 inline auto svector<T, N, A, Init>::at(size_type idx) -> reference
942 {
943 if (idx >= size())
944 {
945 XTENSOR_THROW(std::out_of_range, "Out of range in svector access");
946 }
947 return this->operator[](idx);
948 }
949
950 template <class T, std::size_t N, class A, bool Init>
951 inline auto svector<T, N, A, Init>::at(size_type idx) const -> const_reference
952 {
953 if (idx >= size())
954 {
955 XTENSOR_THROW(std::out_of_range, "Out of range in svector access");
956 }
957 return this->operator[](idx);
958 }
959
960 template <class T, std::size_t N, class A, bool Init>
961 inline auto svector<T, N, A, Init>::data() -> pointer
962 {
963 return m_begin;
964 }
965
966 template <class T, std::size_t N, class A, bool Init>
967 inline auto svector<T, N, A, Init>::data() const -> const_pointer
968 {
969 return m_begin;
970 }
971
972 template <class T, std::size_t N, class A, bool Init>
973 void svector<T, N, A, Init>::resize(size_type n)
974 {
975 if (n > N && n > capacity())
976 {
977 grow(n);
978 }
979 size_type old_size = size();
980 m_end = m_begin + n;
981 if (Init && old_size < size())
982 {
983 std::fill(begin() + old_size, end(), T());
984 }
985 }
986
987 template <class T, std::size_t N, class A, bool Init>
988 inline auto svector<T, N, A, Init>::max_size() const noexcept -> size_type
989 {
990 return m_allocator.max_size();
991 }
992
993 template <class T, std::size_t N, class A, bool Init>
994 inline auto svector<T, N, A, Init>::capacity() const -> size_type
995 {
996 return static_cast<std::size_t>(m_capacity - m_begin);
997 }
998
999 template <class T, std::size_t N, class A, bool Init>
1000 inline void svector<T, N, A, Init>::reserve(size_type n)
1001 {
1002 if (n > N && n > capacity())
1003 {
1004 grow(n);
1005 }
1006 }
1007
1008 template <class T, std::size_t N, class A, bool Init>
1009 inline void svector<T, N, A, Init>::shrink_to_fit()
1010 {
1011 // No op for now
1012 }
1013
1014 template <class T, std::size_t N, class A, bool Init>
1015 inline void svector<T, N, A, Init>::clear()
1016 {
1017 resize(size_type(0));
1018 }
1019
1020 template <class T, std::size_t N, class A, bool Init>
1021 void svector<T, N, A, Init>::push_back(const T& elt)
1022 {
1023 if (m_end >= m_capacity)
1024 {
1025 grow();
1026 }
1027 *(m_end++) = elt;
1028 }
1029
1030 template <class T, std::size_t N, class A, bool Init>
1031 void svector<T, N, A, Init>::push_back(T&& elt)
1032 {
1033 if (m_end >= m_capacity)
1034 {
1035 grow();
1036 }
1037 *(m_end++) = std::move(elt);
1038 }
1039
1040 template <class T, std::size_t N, class A, bool Init>
1041 void svector<T, N, A, Init>::pop_back()
1042 {
1043 --m_end;
1044 }
1045
1046 template <class T, std::size_t N, class A, bool Init>
1047 inline auto svector<T, N, A, Init>::begin() -> iterator
1048 {
1049 return m_begin;
1050 }
1051
1052 template <class T, std::size_t N, class A, bool Init>
1053 inline auto svector<T, N, A, Init>::begin() const -> const_iterator
1054 {
1055 return m_begin;
1056 }
1057
1058 template <class T, std::size_t N, class A, bool Init>
1059 inline auto svector<T, N, A, Init>::cbegin() const -> const_iterator
1060 {
1061 return m_begin;
1062 }
1063
1064 template <class T, std::size_t N, class A, bool Init>
1065 inline auto svector<T, N, A, Init>::end() -> iterator
1066 {
1067 return m_end;
1068 }
1069
1070 template <class T, std::size_t N, class A, bool Init>
1071 inline auto svector<T, N, A, Init>::end() const -> const_iterator
1072 {
1073 return m_end;
1074 }
1075
1076 template <class T, std::size_t N, class A, bool Init>
1077 inline auto svector<T, N, A, Init>::cend() const -> const_iterator
1078 {
1079 return m_end;
1080 }
1081
1082 template <class T, std::size_t N, class A, bool Init>
1083 inline auto svector<T, N, A, Init>::rbegin() -> reverse_iterator
1084 {
1085 return reverse_iterator(m_end);
1086 }
1087
1088 template <class T, std::size_t N, class A, bool Init>
1089 inline auto svector<T, N, A, Init>::rbegin() const -> const_reverse_iterator
1090 {
1091 return const_reverse_iterator(m_end);
1092 }
1093
1094 template <class T, std::size_t N, class A, bool Init>
1095 inline auto svector<T, N, A, Init>::crbegin() const -> const_reverse_iterator
1096 {
1097 return const_reverse_iterator(m_end);
1098 }
1099
1100 template <class T, std::size_t N, class A, bool Init>
1101 inline auto svector<T, N, A, Init>::rend() -> reverse_iterator
1102 {
1103 return reverse_iterator(m_begin);
1104 }
1105
1106 template <class T, std::size_t N, class A, bool Init>
1107 inline auto svector<T, N, A, Init>::rend() const -> const_reverse_iterator
1108 {
1109 return const_reverse_iterator(m_begin);
1110 }
1111
1112 template <class T, std::size_t N, class A, bool Init>
1113 inline auto svector<T, N, A, Init>::crend() const -> const_reverse_iterator
1114 {
1115 return const_reverse_iterator(m_begin);
1116 }
1117
1118 template <class T, std::size_t N, class A, bool Init>
1119 inline auto svector<T, N, A, Init>::size() const -> size_type
1120 {
1121 return static_cast<size_type>(m_end - m_begin);
1122 }
1123
1124 template <class T, std::size_t N, class A, bool Init>
1125 inline auto svector<T, N, A, Init>::empty() const -> bool
1126 {
1127 return m_begin == m_end;
1128 }
1129
1130 template <class T, std::size_t N, class A, bool Init>
1131 inline auto svector<T, N, A, Init>::front() -> reference
1132 {
1133 XTENSOR_ASSERT(!empty());
1134 return m_begin[0];
1135 }
1136
1137 template <class T, std::size_t N, class A, bool Init>
1138 inline auto svector<T, N, A, Init>::front() const -> const_reference
1139 {
1140 XTENSOR_ASSERT(!empty());
1141 return m_begin[0];
1142 }
1143
1144 template <class T, std::size_t N, class A, bool Init>
1145 inline auto svector<T, N, A, Init>::back() -> reference
1146 {
1147 XTENSOR_ASSERT(!empty());
1148 return m_end[-1];
1149 }
1150
1151 template <class T, std::size_t N, class A, bool Init>
1152 inline auto svector<T, N, A, Init>::back() const -> const_reference
1153 {
1154 XTENSOR_ASSERT(!empty());
1155 return m_end[-1];
1156 }
1157
1158 template <class T, std::size_t N, class A, bool Init>
1159 inline auto svector<T, N, A, Init>::on_stack() -> bool
1160 {
1161 return m_begin == &m_data[0];
1162 }
1163
1164 template <class T, std::size_t N, class A, bool Init>
1165 inline auto svector<T, N, A, Init>::get_allocator() const noexcept -> allocator_type
1166 {
1167 return m_allocator;
1168 }
1169
1170 template <class T, std::size_t N, class A, bool Init>
1171 inline auto svector<T, N, A, Init>::erase(const_iterator cit) -> iterator
1172 {
1173 auto it = const_cast<pointer>(cit);
1174 iterator ret_val = it;
1175 std::move(it + 1, m_end, it);
1176 --m_end;
1177 return ret_val;
1178 }
1179
1180 template <class T, std::size_t N, class A, bool Init>
1181 inline auto svector<T, N, A, Init>::erase(const_iterator cfirst, const_iterator clast) -> iterator
1182 {
1183 auto first = const_cast<pointer>(cfirst);
1184 auto last = const_cast<pointer>(clast);
1185 if (last == m_end)
1186 {
1187 m_end = first;
1188 return first;
1189 }
1190
1191 iterator new_end = std::move(last, m_end, first);
1192 m_end = new_end;
1193 return first;
1194 }
1195
1196 template <class T, std::size_t N, class A, bool Init>
1197 inline auto svector<T, N, A, Init>::insert(const_iterator cit, const T& elt) -> iterator
1198 {
1199 auto it = const_cast<pointer>(cit);
1200 if (it == m_end)
1201 {
1202 push_back(elt);
1203 return m_end - 1;
1204 }
1205
1206 if (m_end >= m_capacity)
1207 {
1208 std::ptrdiff_t elt_no = it - m_begin;
1209 grow();
1210 it = m_begin + elt_no;
1211 }
1212
1213 (*m_end) = back();
1214 std::move_backward(it, m_end - 1, m_end);
1215 ++m_end;
1216
1217 // Update ref if element moved
1218 const T* elt_ptr = &elt;
1219 bool cond = it <= elt_ptr && elt_ptr < m_end;
1220 // More complicated than incrementing elt_ptr, but this avoids
1221 // false positive array-bounds warning on GCC 10
1222 const T* src_ptr = cond ? it + (elt_ptr - it) + std::ptrdiff_t(1) : elt_ptr;
1223 *it = *src_ptr;
1224 return it;
1225 }
1226
1227 template <class T, std::size_t N, class A, bool Init>
1228 template <class It>
1229 inline auto svector<T, N, A, Init>::insert(const_iterator pos, It first, It last) -> iterator
1230 {
1231 auto it = const_cast<pointer>(pos);
1232 difference_type n = std::distance(first, last);
1233 if (n > 0)
1234 {
1235 if (n > m_capacity - m_end)
1236 {
1237 std::ptrdiff_t elt_no = it - m_begin;
1238 grow(static_cast<size_t>((m_capacity - m_begin) + n));
1239 it = m_begin + elt_no;
1240 }
1241
1242 std::move_backward(it, m_end, m_end + n);
1243 m_end += n;
1244 std::copy(first, last, it);
1245 }
1246 return it;
1247 }
1248
1249 template <class T, std::size_t N, class A, bool Init>
1250 inline auto svector<T, N, A, Init>::insert(const_iterator pos, std::initializer_list<T> l) -> iterator
1251 {
1252 return insert(pos, l.begin(), l.end());
1253 }
1254
1255 template <class T, std::size_t N, class A, bool Init>
1256 inline void svector<T, N, A, Init>::destroy_range(T* begin, T* end)
1257 {
1258 if (!std::is_trivially_default_constructible<T>::value)
1259 {
1260 while (begin != end)
1261 {
1262 --end;
1263 end->~T();
1264 }
1265 }
1266 }
1267
1268 template <class T, std::size_t N, class A, bool Init>
1269 template <std::size_t ON, class OA, bool InitA>
1270 inline void svector<T, N, A, Init>::swap(svector<T, ON, OA, InitA>& rhs)
1271 {
1272 using std::swap;
1273 if (this == &rhs)
1274 {
1275 return;
1276 }
1277
1278 // We can only avoid copying elements if neither vector is small.
1279 if (!this->on_stack() && !rhs.on_stack())
1280 {
1281 swap(this->m_begin, rhs.m_begin);
1282 swap(this->m_end, rhs.m_end);
1283 swap(this->m_capacity, rhs.m_capacity);
1284 return;
1285 }
1286
1287 size_type rhs_old_size = rhs.size();
1288 size_type old_size = this->size();
1289
1290 if (rhs_old_size > old_size)
1291 {
1292 this->resize(rhs_old_size);
1293 }
1294 else if (old_size > rhs_old_size)
1295 {
1296 rhs.resize(old_size);
1297 }
1298
1299 // Swap the shared elements.
1300 size_type min_size = (std::min)(old_size, rhs_old_size);
1301 for (size_type i = 0; i < min_size; ++i)
1302 {
1303 swap((*this)[i], rhs[i]);
1304 }
1305
1306 // Copy over the extra elts.
1307 if (old_size > rhs_old_size)
1308 {
1309 std::copy(this->begin() + min_size, this->end(), rhs.begin() + min_size);
1310 this->destroy_range(this->begin() + min_size, this->end());
1311 this->m_end = this->begin() + min_size;
1312 }
1313 else if (rhs_old_size > old_size)
1314 {
1315 std::copy(rhs.begin() + min_size, rhs.end(), this->begin() + min_size);
1316 this->destroy_range(rhs.begin() + min_size, rhs.end());
1317 rhs.m_end = rhs.begin() + min_size;
1318 }
1319 }
1320
1321 template <class T, std::size_t N, class A, bool Init>
1322 inline void svector<T, N, A, Init>::grow(size_type min_capacity)
1323 {
1324 size_type current_size = size();
1325 size_type new_capacity = 2 * current_size + 1; // Always grow.
1326 if (new_capacity < min_capacity)
1327 {
1328 new_capacity = min_capacity;
1329 }
1330
1331 T* new_alloc;
1332 // is data stack allocated?
1333 if (m_begin == &m_data[0])
1334 {
1335 new_alloc = m_allocator.allocate(new_capacity);
1336 std::uninitialized_copy(m_begin, m_end, new_alloc);
1337 }
1338 else
1339 {
1340 // If this wasn't grown from the inline copy, grow the allocated space.
1341 new_alloc = m_allocator.allocate(new_capacity);
1342 std::uninitialized_copy(m_begin, m_end, new_alloc);
1343 m_allocator.deallocate(m_begin, std::size_t(m_capacity - m_begin));
1344 }
1345 XTENSOR_ASSERT(new_alloc);
1346
1347 m_end = new_alloc + current_size;
1348 m_begin = new_alloc;
1349 m_capacity = new_alloc + new_capacity;
1350 }
1351
1352 template <class T, std::size_t N, class A, bool Init>
1353 inline bool operator==(const std::vector<T>& lhs, const svector<T, N, A, Init>& rhs)
1354 {
1355 return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
1356 }
1357
1358 template <class T, std::size_t N, class A, bool Init>
1359 inline bool operator==(const svector<T, N, A, Init>& lhs, const std::vector<T>& rhs)
1360 {
1361 return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
1362 }
1363
1364 template <class T, std::size_t N, class A, bool Init>
1365 inline bool operator==(const svector<T, N, A, Init>& lhs, const svector<T, N, A, Init>& rhs)
1366 {
1367 return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
1368 }
1369
1370 template <class T, std::size_t N, class A, bool Init>
1371 inline bool operator!=(const svector<T, N, A, Init>& lhs, const svector<T, N, A, Init>& rhs)
1372 {
1373 return !(lhs == rhs);
1374 }
1375
1376 template <class T, std::size_t N, class A, bool Init>
1377 inline bool operator<(const svector<T, N, A, Init>& lhs, const svector<T, N, A, Init>& rhs)
1378 {
1379 return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
1380 }
1381
1382 template <class T, std::size_t N, class A, bool Init>
1383 inline bool operator<=(const svector<T, N, A, Init>& lhs, const svector<T, N, A, Init>& rhs)
1384 {
1385 return !(lhs > rhs);
1386 }
1387
1388 template <class T, std::size_t N, class A, bool Init>
1389 inline bool operator>(const svector<T, N, A, Init>& lhs, const svector<T, N, A, Init>& rhs)
1390 {
1391 return rhs < lhs;
1392 }
1393
1394 template <class T, std::size_t N, class A, bool Init>
1395 inline bool operator>=(const svector<T, N, A, Init>& lhs, const svector<T, N, A, Init>& rhs)
1396 {
1397 return !(lhs < rhs);
1398 }
1399
1400 template <class T, std::size_t N, class A, bool Init>
1401 inline void swap(svector<T, N, A, Init>& lhs, svector<T, N, A, Init>& rhs) noexcept
1402 {
1403 lhs.swap(rhs);
1404 }
1405
1406 template <class X, class T, std::size_t N, class A, bool B>
1407 struct rebind_container<X, svector<T, N, A, B>>
1408 {
1409 using traits = std::allocator_traits<A>;
1410 using allocator = typename traits::template rebind_alloc<X>;
1411 using type = svector<X, N, allocator, B>;
1412 };
1413
1420 template <class T, std::size_t N, std::size_t Align = XTENSOR_SELECT_ALIGN(T)>
1421 class alignas(Align) aligned_array : public std::array<T, N>
1422 {
1423 public:
1424
1425 // Note: this is for alignment detection. The allocator serves no other purpose than
1426 // that of a trait here.
1427 using allocator_type = std::conditional_t<Align != 0, xt_simd::aligned_allocator<T, Align>, std::allocator<T>>;
1428 };
1429
1430#if defined(_MSC_VER)
1431#define XTENSOR_CONST
1432#else
1433#define XTENSOR_CONST const
1434#endif
1435
1440 template <class T, std::size_t N>
1442 {
1443 using size_type = std::size_t;
1444 using value_type = T;
1445 using pointer = value_type*;
1446 using const_pointer = const value_type*;
1447 using reference = value_type&;
1448 using const_reference = const value_type&;
1449 using difference_type = std::ptrdiff_t;
1450 using iterator = pointer;
1451 using const_iterator = const_pointer;
1452
1453 using reverse_iterator = std::reverse_iterator<const_iterator>;
1454 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
1455
1456 constexpr const_reference operator[](std::size_t idx) const
1457 {
1458 return m_data[idx];
1459 }
1460
1461 constexpr const_iterator begin() const noexcept
1462 {
1463 return cbegin();
1464 }
1465
1466 constexpr const_iterator end() const noexcept
1467 {
1468 return cend();
1469 }
1470
1471 constexpr const_iterator cbegin() const noexcept
1472 {
1473 return data();
1474 }
1475
1476 constexpr const_iterator cend() const noexcept
1477 {
1478 return data() + N;
1479 }
1480
1481 // TODO make constexpr once C++17 arrives
1482 reverse_iterator rbegin() const noexcept
1483 {
1484 return crbegin();
1485 }
1486
1487 reverse_iterator rend() const noexcept
1488 {
1489 return crend();
1490 }
1491
1492 const_reverse_iterator crbegin() const noexcept
1493 {
1494 return const_reverse_iterator(end());
1495 }
1496
1497 const_reverse_iterator crend() const noexcept
1498 {
1499 return const_reverse_iterator(begin());
1500 }
1501
1502 constexpr const_pointer data() const noexcept
1503 {
1504 return m_data;
1505 }
1506
1507 constexpr const_reference front() const noexcept
1508 {
1509 return m_data[0];
1510 }
1511
1512 constexpr const_reference back() const noexcept
1513 {
1514 return m_data[size() - 1];
1515 }
1516
1517 constexpr bool empty() const noexcept
1518 {
1519 return size() == size_type(0);
1520 }
1521
1522 constexpr size_type size() const noexcept
1523 {
1524 return N;
1525 }
1526
1527 XTENSOR_CONST T m_data[N > 0 ? N : 1];
1528 };
1529
1530 template <class T, std::size_t N>
1531 inline bool operator==(const const_array<T, N>& lhs, const const_array<T, N>& rhs)
1532 {
1533 return std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin());
1534 }
1535
1536 template <class T, std::size_t N>
1537 inline bool operator!=(const const_array<T, N>& lhs, const const_array<T, N>& rhs)
1538 {
1539 return !(lhs == rhs);
1540 }
1541
1542 template <class T, std::size_t N>
1543 inline bool operator<(const const_array<T, N>& lhs, const const_array<T, N>& rhs)
1544 {
1545 return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
1546 }
1547
1548 template <class T, std::size_t N>
1549 inline bool operator<=(const const_array<T, N>& lhs, const const_array<T, N>& rhs)
1550 {
1551 return !(lhs > rhs);
1552 }
1553
1554 template <class T, std::size_t N>
1555 inline bool operator>(const const_array<T, N>& lhs, const const_array<T, N>& rhs)
1556 {
1557 return rhs < lhs;
1558 }
1559
1560 template <class T, std::size_t N>
1561 inline bool operator>=(const const_array<T, N>& lhs, const const_array<T, N>& rhs)
1562 {
1563 return !(lhs < rhs);
1564 }
1565
1566// Workaround for rebind_container problems when C++17 feature is enabled
1567#ifdef __cpp_template_template_args
1568 template <class X, class T, std::size_t N>
1569 struct rebind_container<X, aligned_array<T, N>>
1570 {
1571 using type = aligned_array<X, N>;
1572 };
1573
1574 template <class X, class T, std::size_t N>
1575 struct rebind_container<X, const_array<T, N>>
1576 {
1577 using type = const_array<X, N>;
1578 };
1579#endif
1580
1586 template <std::size_t... X>
1588 {
1589 public:
1590
1591 using cast_type = const_array<std::size_t, sizeof...(X)>;
1592 using value_type = std::size_t;
1593 using size_type = std::size_t;
1594 using const_iterator = typename cast_type::const_iterator;
1595
1596 static constexpr std::size_t size()
1597 {
1598 return sizeof...(X);
1599 }
1600
1601 template <std::size_t idx>
1602 static constexpr auto get()
1603 {
1604 using tmp_cast_type = std::array<std::size_t, sizeof...(X)>;
1605 return std::get<idx>(tmp_cast_type{X...});
1606 }
1607
1608 constexpr operator cast_type() const
1609 {
1610 return cast_type({X...});
1611 }
1612
1613 constexpr auto begin() const
1614 {
1615 return m_array.begin();
1616 }
1617
1618 constexpr auto end() const
1619 {
1620 return m_array.end();
1621 }
1622
1623 auto rbegin() const
1624 {
1625 return m_array.rbegin();
1626 }
1627
1628 auto rend() const
1629 {
1630 return m_array.rend();
1631 }
1632
1633 constexpr auto cbegin() const
1634 {
1635 return m_array.cbegin();
1636 }
1637
1638 constexpr auto cend() const
1639 {
1640 return m_array.cend();
1641 }
1642
1643 constexpr std::size_t operator[](std::size_t idx) const
1644 {
1645 return m_array[idx];
1646 }
1647
1648 constexpr bool empty() const
1649 {
1650 return sizeof...(X) == 0;
1651 }
1652
1653 private:
1654
1655 XTENSOR_CONSTEXPR_ENHANCED_STATIC cast_type m_array = cast_type({X...});
1656 };
1657
1658 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End = -1>
1659 class sequence_view
1660 {
1661 public:
1662
1663 using value_type = typename E::value_type;
1664 using reference = typename E::reference;
1665 using const_reference = typename E::const_reference;
1666 using pointer = typename E::pointer;
1667 using const_pointer = typename E::const_pointer;
1668
1669 using size_type = typename E::size_type;
1670 using difference_type = typename E::difference_type;
1671
1672 using iterator = typename E::iterator;
1673 using const_iterator = typename E::const_iterator;
1674 using reverse_iterator = typename E::reverse_iterator;
1675 using const_reverse_iterator = typename E::const_reverse_iterator;
1676
1677 explicit sequence_view(const E& container);
1678
1679 template <std::ptrdiff_t OS, std::ptrdiff_t OE>
1680 explicit sequence_view(const sequence_view<E, OS, OE>& other);
1681
1682 template <class T, class R = decltype(std::declval<T>().begin())>
1683 operator T() const;
1684
1685 bool empty() const;
1686 size_type size() const;
1687 const_reference operator[](std::size_t idx) const;
1688
1689 const_iterator end() const;
1690 const_iterator begin() const;
1691 const_iterator cend() const;
1692 const_iterator cbegin() const;
1693
1694 const_reverse_iterator rend() const;
1695 const_reverse_iterator rbegin() const;
1696 const_reverse_iterator crend() const;
1697 const_reverse_iterator crbegin() const;
1698
1699 const_reference front() const;
1700 const_reference back() const;
1701
1702 const E& storage() const;
1703
1704 private:
1705
1706 const E& m_sequence;
1707 };
1708
1709 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1710 sequence_view<E, Start, End>::sequence_view(const E& container)
1711 : m_sequence(container)
1712 {
1713 }
1714
1715 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1716 template <std::ptrdiff_t OS, std::ptrdiff_t OE>
1717 sequence_view<E, Start, End>::sequence_view(const sequence_view<E, OS, OE>& other)
1718 : m_sequence(other.storage())
1719 {
1720 }
1721
1722 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1723 template <class T, class R>
1725 {
1726 T ret = xtl::make_sequence<T>(this->size());
1727 std::copy(this->cbegin(), this->cend(), ret.begin());
1728 return ret;
1729 }
1730
1731 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1732 bool sequence_view<E, Start, End>::empty() const
1733 {
1734 return size() == size_type(0);
1735 }
1736
1737 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1738 auto sequence_view<E, Start, End>::size() const -> size_type
1739 {
1740 if (End == -1)
1741 {
1742 return m_sequence.size() - static_cast<size_type>(Start);
1743 }
1744 else
1745 {
1746 return static_cast<size_type>(End - Start);
1747 }
1748 }
1749
1750 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1751 auto sequence_view<E, Start, End>::operator[](std::size_t idx) const -> const_reference
1752 {
1753 return m_sequence[idx + static_cast<std::size_t>(Start)];
1754 }
1755
1756 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1757 auto sequence_view<E, Start, End>::end() const -> const_iterator
1758 {
1759 if (End != -1)
1760 {
1761 return m_sequence.begin() + End;
1762 }
1763 else
1764 {
1765 return m_sequence.end();
1766 }
1767 }
1768
1769 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1770 auto sequence_view<E, Start, End>::begin() const -> const_iterator
1771 {
1772 return m_sequence.begin() + Start;
1773 }
1774
1775 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1776 auto sequence_view<E, Start, End>::cend() const -> const_iterator
1777 {
1778 return end();
1779 }
1780
1781 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1782 auto sequence_view<E, Start, End>::cbegin() const -> const_iterator
1783 {
1784 return begin();
1785 }
1786
1787 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1788 auto sequence_view<E, Start, End>::rend() const -> const_reverse_iterator
1789 {
1790 return const_reverse_iterator(begin());
1791 }
1792
1793 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1794 auto sequence_view<E, Start, End>::rbegin() const -> const_reverse_iterator
1795 {
1796 return const_reverse_iterator(end());
1797 }
1798
1799 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1800 auto sequence_view<E, Start, End>::crend() const -> const_reverse_iterator
1801 {
1802 return rend();
1803 }
1804
1805 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1806 auto sequence_view<E, Start, End>::crbegin() const -> const_reverse_iterator
1807 {
1808 return rbegin();
1809 }
1810
1811 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1812 auto sequence_view<E, Start, End>::front() const -> const_reference
1813 {
1814 return *(m_sequence.begin() + Start);
1815 }
1816
1817 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1818 auto sequence_view<E, Start, End>::back() const -> const_reference
1819 {
1820 if (End == -1)
1821 {
1822 return m_sequence.back();
1823 }
1824 else
1825 {
1826 return m_sequence[static_cast<std::size_t>(End - 1)];
1827 }
1828 }
1829
1830 template <class E, std::ptrdiff_t Start, std::ptrdiff_t End>
1831 const E& sequence_view<E, Start, End>::storage() const
1832 {
1833 return m_sequence;
1834 }
1835
1836 template <class T, std::ptrdiff_t TB, std::ptrdiff_t TE>
1837 inline bool operator==(const sequence_view<T, TB, TE>& lhs, const sequence_view<T, TB, TE>& rhs)
1838 {
1839 return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
1840 }
1841
1842 template <class T, std::ptrdiff_t TB, std::ptrdiff_t TE>
1843 inline bool operator!=(const sequence_view<T, TB, TE>& lhs, const sequence_view<T, TB, TE>& rhs)
1844 {
1845 return !(lhs == rhs);
1846 }
1847}
1848
1849/******************************
1850 * std::tuple_size extensions *
1851 ******************************/
1852
1853// The C++ standard defines tuple_size as a class, however
1854// G++ 8 C++ library does define it as a struct hence we get
1855// clang warnings here
1856
1857// Do not remove space between "#" and "pragma". This is required for CRAN checks.
1858// clang-format off
1859#if defined(__clang__)
1860 # pragma clang diagnostic push
1861 # pragma clang diagnostic ignored "-Wmismatched-tags"
1862#endif
1863// clang-format on
1864
1865namespace std
1866{
1867 template <class T, std::size_t N>
1868 class tuple_size<xt::const_array<T, N>> : public integral_constant<std::size_t, N>
1869 {
1870 };
1871
1872 template <std::size_t... N>
1873 class tuple_size<xt::fixed_shape<N...>> : public integral_constant<std::size_t, sizeof...(N)>
1874 {
1875 };
1876
1877 template <class T, std::ptrdiff_t Start, std::ptrdiff_t End>
1878 class tuple_size<xt::sequence_view<T, Start, End>>
1879 : public integral_constant<std::size_t, std::size_t(End - Start)>
1880 {
1881 };
1882
1883 // Undefine tuple size for not-known sequence view size
1884 template <class T, std::ptrdiff_t Start>
1885 class tuple_size<xt::sequence_view<T, Start, -1>>;
1886}
1887
1888// Do not remove space between "#" and "pragma". This is required for CRAN checks.
1889// clang-format off
1890#if defined(__clang__)
1891 # pragma clang diagnostic pop
1892#endif
1893// clang-format on
1894
1895#undef XTENSOR_CONST
1896
1897#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.