/*************************************************************************** * Copyright (c) Johan Mabille, Sylvain Corlay and Wolf Vollprecht * * Copyright (c) QuantStack * * * * Distributed under the terms of the BSD 3-Clause License. * * * * The full license is in the file LICENSE, distributed with this software. * ****************************************************************************/ #ifndef XTENSOR_STORAGE_HPP #define XTENSOR_STORAGE_HPP #include #include #include #include #include #include #include #include "xexception.hpp" #include "xtensor_config.hpp" #include "xtensor_simd.hpp" #include "xutils.hpp" namespace xt { namespace detail { template using require_input_iter = typename std::enable_if< std::is_convertible::iterator_category, std::input_iterator_tag>::value>::type; } template struct is_contiguous_container : std::true_type { }; template > class uvector { public: using allocator_type = A; using value_type = typename std::allocator_traits::value_type; using reference = value_type&; using const_reference = const value_type&; using pointer = typename std::allocator_traits::pointer; using const_pointer = typename std::allocator_traits::const_pointer; using size_type = typename std::allocator_traits::size_type; using difference_type = typename std::allocator_traits::difference_type; using iterator = pointer; using const_iterator = const_pointer; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; uvector() noexcept; explicit uvector(const allocator_type& alloc) noexcept; explicit uvector(size_type count, const allocator_type& alloc = allocator_type()); uvector(size_type count, const_reference value, const allocator_type& alloc = allocator_type()); template > uvector(InputIt first, InputIt last, const allocator_type& alloc = allocator_type()); uvector(std::initializer_list init, const allocator_type& alloc = allocator_type()); ~uvector(); uvector(const uvector& rhs); uvector(const uvector& rhs, const allocator_type& alloc); uvector& operator=(const uvector&); uvector(uvector&& rhs) noexcept; uvector(uvector&& rhs, const allocator_type& alloc) noexcept; uvector& operator=(uvector&& rhs) noexcept; allocator_type get_allocator() const noexcept; bool empty() const noexcept; size_type size() const noexcept; void resize(size_type size); size_type max_size() const noexcept; void reserve(size_type new_cap); size_type capacity() const noexcept; void shrink_to_fit(); void clear(); reference operator[](size_type i); const_reference operator[](size_type i) const; reference at(size_type i); const_reference at(size_type i) const; reference front(); const_reference front() const; reference back(); const_reference back() const; pointer data() noexcept; const_pointer data() const noexcept; iterator begin() noexcept; iterator end() noexcept; const_iterator begin() const noexcept; const_iterator end() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; reverse_iterator rbegin() noexcept; reverse_iterator rend() noexcept; const_reverse_iterator rbegin() const noexcept; const_reverse_iterator rend() const noexcept; const_reverse_iterator crbegin() const noexcept; const_reverse_iterator crend() const noexcept; void swap(uvector& rhs) noexcept; private: template void init_data(I first, I last); void resize_impl(size_type new_size); allocator_type m_allocator; // Storing a pair of pointers is more efficient for iterating than // storing a pointer to the beginning and the size of the container pointer p_begin; pointer p_end; }; template bool operator==(const uvector& lhs, const uvector& rhs); template bool operator!=(const uvector& lhs, const uvector& rhs); template bool operator<(const uvector& lhs, const uvector& rhs); template bool operator<=(const uvector& lhs, const uvector& rhs); template bool operator>(const uvector& lhs, const uvector& rhs); template bool operator>=(const uvector& lhs, const uvector& rhs); template void swap(uvector& lhs, uvector& rhs) noexcept; /************************** * uvector implementation * **************************/ namespace detail { template inline typename std::allocator_traits::pointer safe_init_allocate(A& alloc, typename std::allocator_traits::size_type size) { using traits = std::allocator_traits; using pointer = typename traits::pointer; using value_type = typename traits::value_type; pointer res = alloc.allocate(size); if (!xtrivially_default_constructible::value) { for (pointer p = res; p != res + size; ++p) { traits::construct(alloc, p, value_type()); } } return res; } template inline void safe_destroy_deallocate( A& alloc, typename std::allocator_traits::pointer ptr, typename std::allocator_traits::size_type size ) { using traits = std::allocator_traits; using pointer = typename traits::pointer; using value_type = typename traits::value_type; if (ptr != nullptr) { if (!xtrivially_default_constructible::value) { for (pointer p = ptr; p != ptr + size; ++p) { traits::destroy(alloc, p); } } traits::deallocate(alloc, ptr, size); } } } template template inline void uvector::init_data(I first, I last) { size_type size = static_cast(std::distance(first, last)); if (size != size_type(0)) { p_begin = m_allocator.allocate(size); std::uninitialized_copy(first, last, p_begin); p_end = p_begin + size; } } template inline void uvector::resize_impl(size_type new_size) { size_type old_size = size(); pointer old_begin = p_begin; if (new_size != old_size) { p_begin = detail::safe_init_allocate(m_allocator, new_size); p_end = p_begin + new_size; detail::safe_destroy_deallocate(m_allocator, old_begin, old_size); } } template inline uvector::uvector() noexcept : uvector(allocator_type()) { } template inline uvector::uvector(const allocator_type& alloc) noexcept : m_allocator(alloc) , p_begin(nullptr) , p_end(nullptr) { } template inline uvector::uvector(size_type count, const allocator_type& alloc) : m_allocator(alloc) , p_begin(nullptr) , p_end(nullptr) { if (count != 0) { p_begin = detail::safe_init_allocate(m_allocator, count); p_end = p_begin + count; } } template inline uvector::uvector(size_type count, const_reference value, const allocator_type& alloc) : m_allocator(alloc) , p_begin(nullptr) , p_end(nullptr) { if (count != 0) { p_begin = m_allocator.allocate(count); p_end = p_begin + count; std::uninitialized_fill(p_begin, p_end, value); } } template template inline uvector::uvector(InputIt first, InputIt last, const allocator_type& alloc) : m_allocator(alloc) , p_begin(nullptr) , p_end(nullptr) { init_data(first, last); } template inline uvector::uvector(std::initializer_list init, const allocator_type& alloc) : m_allocator(alloc) , p_begin(nullptr) , p_end(nullptr) { init_data(init.begin(), init.end()); } template inline uvector::~uvector() { detail::safe_destroy_deallocate(m_allocator, p_begin, size()); p_begin = nullptr; p_end = nullptr; } template inline uvector::uvector(const uvector& rhs) : m_allocator( std::allocator_traits::select_on_container_copy_construction(rhs.get_allocator()) ) , p_begin(nullptr) , p_end(nullptr) { init_data(rhs.p_begin, rhs.p_end); } template inline uvector::uvector(const uvector& rhs, const allocator_type& alloc) : m_allocator(alloc) , p_begin(nullptr) , p_end(nullptr) { init_data(rhs.p_begin, rhs.p_end); } template inline uvector& uvector::operator=(const uvector& rhs) { // No copy and swap idiom here due to performance issues if (this != &rhs) { m_allocator = std::allocator_traits::select_on_container_copy_construction( rhs.get_allocator() ); resize_impl(rhs.size()); if (xtrivially_default_constructible::value) { std::uninitialized_copy(rhs.p_begin, rhs.p_end, p_begin); } else { std::copy(rhs.p_begin, rhs.p_end, p_begin); } } return *this; } template inline uvector::uvector(uvector&& rhs) noexcept : m_allocator(std::move(rhs.m_allocator)) , p_begin(rhs.p_begin) , p_end(rhs.p_end) { rhs.p_begin = nullptr; rhs.p_end = nullptr; } template inline uvector::uvector(uvector&& rhs, const allocator_type& alloc) noexcept : m_allocator(alloc) , p_begin(rhs.p_begin) , p_end(rhs.p_end) { rhs.p_begin = nullptr; rhs.p_end = nullptr; } template inline uvector& uvector::operator=(uvector&& rhs) noexcept { using std::swap; uvector tmp(std::move(rhs)); swap(p_begin, tmp.p_begin); swap(p_end, tmp.p_end); return *this; } template inline auto uvector::get_allocator() const noexcept -> allocator_type { return allocator_type(m_allocator); } template inline bool uvector::empty() const noexcept { return size() == size_type(0); } template inline auto uvector::size() const noexcept -> size_type { return static_cast(p_end - p_begin); } template inline void uvector::resize(size_type size) { resize_impl(size); } template inline auto uvector::max_size() const noexcept -> size_type { return m_allocator.max_size(); } template inline void uvector::reserve(size_type /*new_cap*/) { } template inline auto uvector::capacity() const noexcept -> size_type { return size(); } template inline void uvector::shrink_to_fit() { } template inline void uvector::clear() { resize(size_type(0)); } template inline auto uvector::operator[](size_type i) -> reference { return p_begin[i]; } template inline auto uvector::operator[](size_type i) const -> const_reference { return p_begin[i]; } template inline auto uvector::at(size_type i) -> reference { if (i >= size()) { XTENSOR_THROW(std::out_of_range, "Out of range in uvector access"); } return this->operator[](i); } template inline auto uvector::at(size_type i) const -> const_reference { if (i >= size()) { XTENSOR_THROW(std::out_of_range, "Out of range in uvector access"); } return this->operator[](i); } template inline auto uvector::front() -> reference { return p_begin[0]; } template inline auto uvector::front() const -> const_reference { return p_begin[0]; } template inline auto uvector::back() -> reference { return *(p_end - 1); } template inline auto uvector::back() const -> const_reference { return *(p_end - 1); } template inline auto uvector::data() noexcept -> pointer { return p_begin; } template inline auto uvector::data() const noexcept -> const_pointer { return p_begin; } template inline auto uvector::begin() noexcept -> iterator { return p_begin; } template inline auto uvector::end() noexcept -> iterator { return p_end; } template inline auto uvector::begin() const noexcept -> const_iterator { return p_begin; } template inline auto uvector::end() const noexcept -> const_iterator { return p_end; } template inline auto uvector::cbegin() const noexcept -> const_iterator { return begin(); } template inline auto uvector::cend() const noexcept -> const_iterator { return end(); } template inline auto uvector::rbegin() noexcept -> reverse_iterator { return reverse_iterator(end()); } template inline auto uvector::rend() noexcept -> reverse_iterator { return reverse_iterator(begin()); } template inline auto uvector::rbegin() const noexcept -> const_reverse_iterator { return const_reverse_iterator(end()); } template inline auto uvector::rend() const noexcept -> const_reverse_iterator { return const_reverse_iterator(begin()); } template inline auto uvector::crbegin() const noexcept -> const_reverse_iterator { return rbegin(); } template inline auto uvector::crend() const noexcept -> const_reverse_iterator { return rend(); } template inline void uvector::swap(uvector& rhs) noexcept { using std::swap; swap(m_allocator, rhs.m_allocator); swap(p_begin, rhs.p_begin); swap(p_end, rhs.p_end); } template inline bool operator==(const uvector& lhs, const uvector& rhs) { return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); } template inline bool operator!=(const uvector& lhs, const uvector& rhs) { return !(lhs == rhs); } template inline bool operator<(const uvector& lhs, const uvector& rhs) { return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } template inline bool operator<=(const uvector& lhs, const uvector& rhs) { return !(lhs > rhs); } template inline bool operator>(const uvector& lhs, const uvector& rhs) { return rhs < lhs; } template inline bool operator>=(const uvector& lhs, const uvector& rhs) { return !(lhs < rhs); } template inline void swap(uvector& lhs, uvector& rhs) noexcept { lhs.swap(rhs); } /************************** * svector implementation * **************************/ namespace detail { template struct allocator_alignment { static constexpr std::size_t value = 0; }; template struct allocator_alignment> { static constexpr std::size_t value = A; }; } template , bool Init = true> class svector { public: using self_type = svector; using allocator_type = A; using size_type = typename std::allocator_traits::size_type; using value_type = typename std::allocator_traits::value_type; using pointer = typename std::allocator_traits::pointer; using const_pointer = typename std::allocator_traits::const_pointer; using reference = value_type&; using const_reference = const value_type&; using difference_type = typename std::allocator_traits::difference_type; using iterator = pointer; using const_iterator = const_pointer; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; #if defined(_MSC_VER) && _MSC_VER < 1910 static constexpr std::size_t alignment = detail::allocator_alignment::value; #else static constexpr std::size_t alignment = detail::allocator_alignment::value != 0 ? detail::allocator_alignment::value : alignof(T); #endif svector() noexcept; ~svector(); explicit svector(const allocator_type& alloc) noexcept; explicit svector(size_type n, const allocator_type& alloc = allocator_type()); svector(size_type n, const value_type& v, const allocator_type& alloc = allocator_type()); svector(std::initializer_list il, const allocator_type& alloc = allocator_type()); svector(const std::vector& vec); template > svector(IT begin, IT end, const allocator_type& alloc = allocator_type()); template > explicit svector(const svector& rhs); svector& operator=(const svector& rhs); svector& operator=(svector&& rhs) noexcept(std::is_nothrow_move_assignable::value); svector& operator=(const std::vector& rhs); svector& operator=(std::initializer_list il); template > svector& operator=(const svector& rhs); svector(const svector& other); svector(svector&& other) noexcept(std::is_nothrow_move_constructible::value); void assign(size_type n, const value_type& v); template void assign(std::initializer_list il); template void assign(IT other_begin, IT other_end); reference operator[](size_type idx); const_reference operator[](size_type idx) const; reference at(size_type idx); const_reference at(size_type idx) const; pointer data(); const_pointer data() const; void push_back(const T& elt); void push_back(T&& elt); void pop_back(); iterator begin(); const_iterator begin() const; const_iterator cbegin() const; iterator end(); const_iterator end() const; const_iterator cend() const; reverse_iterator rbegin(); const_reverse_iterator rbegin() const; const_reverse_iterator crbegin() const; reverse_iterator rend(); const_reverse_iterator rend() const; const_reverse_iterator crend() const; bool empty() const; size_type size() const; void resize(size_type n); size_type max_size() const noexcept; size_type capacity() const; void reserve(size_type n); void shrink_to_fit(); void clear(); reference front(); const_reference front() const; reference back(); const_reference back() const; bool on_stack(); iterator erase(const_iterator cit); iterator erase(const_iterator cfirst, const_iterator clast); iterator insert(const_iterator it, const T& elt); template iterator insert(const_iterator pos, It first, It last); iterator insert(const_iterator pos, std::initializer_list l); template void swap(svector& rhs); allocator_type get_allocator() const noexcept; private: A m_allocator; T* m_begin = std::begin(m_data); T* m_end = std::begin(m_data); T* m_capacity = std::end(m_data); // stack allocated memory alignas(alignment) T m_data[N > 0 ? N : 1]; void grow(size_type min_capacity = 0); void destroy_range(T* begin, T* end); }; template inline svector::~svector() { if (!on_stack()) { detail::safe_destroy_deallocate(m_allocator, m_begin, static_cast(m_capacity - m_begin)); } } template inline svector::svector() noexcept : svector(allocator_type()) { } template inline svector::svector(const allocator_type& alloc) noexcept : m_allocator(alloc) { } template inline svector::svector(size_type n, const allocator_type& alloc) : m_allocator(alloc) { if (Init) { assign(n, T(0)); } else { resize(n); } } template template inline svector::svector(IT begin, IT end, const allocator_type& alloc) : m_allocator(alloc) { assign(begin, end); } template template inline svector::svector(const svector& rhs) : m_allocator(rhs.get_allocator()) { assign(rhs.begin(), rhs.end()); } template inline svector::svector(const std::vector& vec) { assign(vec.begin(), vec.end()); } template inline svector::svector(size_type n, const value_type& v, const allocator_type& alloc) : m_allocator(alloc) { assign(n, v); } template inline svector::svector(std::initializer_list il, const allocator_type& alloc) : m_allocator(alloc) { assign(il.begin(), il.end()); } template inline svector& svector::operator=(const svector& rhs) { assign(rhs.begin(), rhs.end()); return *this; } template inline svector& svector::operator=(svector&& rhs ) noexcept(std::is_nothrow_move_assignable::value) { assign(rhs.begin(), rhs.end()); return *this; } template inline svector& svector::operator=(const std::vector& rhs) { m_allocator = std::allocator_traits::select_on_container_copy_construction( rhs.get_allocator() ); assign(rhs.begin(), rhs.end()); return *this; } template inline svector& svector::operator=(std::initializer_list il) { return operator=(self_type(il)); } template template inline svector& svector::operator=(const svector& rhs) { m_allocator = std::allocator_traits::select_on_container_copy_construction( rhs.get_allocator() ); assign(rhs.begin(), rhs.end()); return *this; } template inline svector::svector(const svector& rhs) : m_allocator( std::allocator_traits::select_on_container_copy_construction(rhs.get_allocator()) ) { assign(rhs.begin(), rhs.end()); } template inline svector::svector(svector&& rhs ) noexcept(std::is_nothrow_move_constructible::value) { this->swap(rhs); } template inline void svector::assign(size_type n, const value_type& v) { if (n > N && n > capacity()) { grow(n); } m_end = m_begin + n; std::fill(begin(), end(), v); } template template inline void svector::assign(std::initializer_list il) { assign(il.begin(), il.end()); } template template inline void svector::assign(IT other_begin, IT other_end) { std::size_t size = static_cast(other_end - other_begin); if (size > N && size > capacity()) { grow(size); } std::uninitialized_copy(other_begin, other_end, m_begin); m_end = m_begin + size; } template inline auto svector::operator[](size_type idx) -> reference { return m_begin[idx]; } template inline auto svector::operator[](size_type idx) const -> const_reference { return m_begin[idx]; } template inline auto svector::at(size_type idx) -> reference { if (idx >= size()) { XTENSOR_THROW(std::out_of_range, "Out of range in svector access"); } return this->operator[](idx); } template inline auto svector::at(size_type idx) const -> const_reference { if (idx >= size()) { XTENSOR_THROW(std::out_of_range, "Out of range in svector access"); } return this->operator[](idx); } template inline auto svector::data() -> pointer { return m_begin; } template inline auto svector::data() const -> const_pointer { return m_begin; } template void svector::resize(size_type n) { if (n > N && n > capacity()) { grow(n); } size_type old_size = size(); m_end = m_begin + n; if (Init && old_size < size()) { std::fill(begin() + old_size, end(), T()); } } template inline auto svector::max_size() const noexcept -> size_type { return m_allocator.max_size(); } template inline auto svector::capacity() const -> size_type { return static_cast(m_capacity - m_begin); } template inline void svector::reserve(size_type n) { if (n > N && n > capacity()) { grow(n); } } template inline void svector::shrink_to_fit() { // No op for now } template inline void svector::clear() { resize(size_type(0)); } template void svector::push_back(const T& elt) { if (m_end >= m_capacity) { grow(); } *(m_end++) = elt; } template void svector::push_back(T&& elt) { if (m_end >= m_capacity) { grow(); } *(m_end++) = std::move(elt); } template void svector::pop_back() { --m_end; } template inline auto svector::begin() -> iterator { return m_begin; } template inline auto svector::begin() const -> const_iterator { return m_begin; } template inline auto svector::cbegin() const -> const_iterator { return m_begin; } template inline auto svector::end() -> iterator { return m_end; } template inline auto svector::end() const -> const_iterator { return m_end; } template inline auto svector::cend() const -> const_iterator { return m_end; } template inline auto svector::rbegin() -> reverse_iterator { return reverse_iterator(m_end); } template inline auto svector::rbegin() const -> const_reverse_iterator { return const_reverse_iterator(m_end); } template inline auto svector::crbegin() const -> const_reverse_iterator { return const_reverse_iterator(m_end); } template inline auto svector::rend() -> reverse_iterator { return reverse_iterator(m_begin); } template inline auto svector::rend() const -> const_reverse_iterator { return const_reverse_iterator(m_begin); } template inline auto svector::crend() const -> const_reverse_iterator { return const_reverse_iterator(m_begin); } template inline auto svector::size() const -> size_type { return static_cast(m_end - m_begin); } template inline auto svector::empty() const -> bool { return m_begin == m_end; } template inline auto svector::front() -> reference { XTENSOR_ASSERT(!empty()); return m_begin[0]; } template inline auto svector::front() const -> const_reference { XTENSOR_ASSERT(!empty()); return m_begin[0]; } template inline auto svector::back() -> reference { XTENSOR_ASSERT(!empty()); return m_end[-1]; } template inline auto svector::back() const -> const_reference { XTENSOR_ASSERT(!empty()); return m_end[-1]; } template inline auto svector::on_stack() -> bool { return m_begin == &m_data[0]; } template inline auto svector::get_allocator() const noexcept -> allocator_type { return m_allocator; } template inline auto svector::erase(const_iterator cit) -> iterator { auto it = const_cast(cit); iterator ret_val = it; std::move(it + 1, m_end, it); --m_end; return ret_val; } template inline auto svector::erase(const_iterator cfirst, const_iterator clast) -> iterator { auto first = const_cast(cfirst); auto last = const_cast(clast); if (last == m_end) { m_end = first; return first; } iterator new_end = std::move(last, m_end, first); m_end = new_end; return first; } template inline auto svector::insert(const_iterator cit, const T& elt) -> iterator { auto it = const_cast(cit); if (it == m_end) { push_back(elt); return m_end - 1; } if (m_end >= m_capacity) { std::ptrdiff_t elt_no = it - m_begin; grow(); it = m_begin + elt_no; } (*m_end) = back(); std::move_backward(it, m_end - 1, m_end); ++m_end; // Update ref if element moved const T* elt_ptr = &elt; bool cond = it <= elt_ptr && elt_ptr < m_end; // More complicated than incrementing elt_ptr, but this avoids // false positive array-bounds warning on GCC 10 const T* src_ptr = cond ? it + (elt_ptr - it) + std::ptrdiff_t(1) : elt_ptr; *it = *src_ptr; return it; } template template inline auto svector::insert(const_iterator pos, It first, It last) -> iterator { auto it = const_cast(pos); difference_type n = std::distance(first, last); if (n > 0) { if (n > m_capacity - m_end) { std::ptrdiff_t elt_no = it - m_begin; grow(static_cast((m_capacity - m_begin) + n)); it = m_begin + elt_no; } std::move_backward(it, m_end, m_end + n); m_end += n; std::copy(first, last, it); } return it; } template inline auto svector::insert(const_iterator pos, std::initializer_list l) -> iterator { return insert(pos, l.begin(), l.end()); } template inline void svector::destroy_range(T* begin, T* end) { if (!xtrivially_default_constructible::value) { while (begin != end) { --end; end->~T(); } } } template template inline void svector::swap(svector& rhs) { using std::swap; if (this == &rhs) { return; } // We can only avoid copying elements if neither vector is small. if (!this->on_stack() && !rhs.on_stack()) { swap(this->m_begin, rhs.m_begin); swap(this->m_end, rhs.m_end); swap(this->m_capacity, rhs.m_capacity); return; } size_type rhs_old_size = rhs.size(); size_type old_size = this->size(); if (rhs_old_size > old_size) { this->resize(rhs_old_size); } else if (old_size > rhs_old_size) { rhs.resize(old_size); } // Swap the shared elements. size_type min_size = (std::min)(old_size, rhs_old_size); for (size_type i = 0; i < min_size; ++i) { swap((*this)[i], rhs[i]); } // Copy over the extra elts. if (old_size > rhs_old_size) { std::copy(this->begin() + min_size, this->end(), rhs.begin() + min_size); this->destroy_range(this->begin() + min_size, this->end()); this->m_end = this->begin() + min_size; } else if (rhs_old_size > old_size) { std::copy(rhs.begin() + min_size, rhs.end(), this->begin() + min_size); this->destroy_range(rhs.begin() + min_size, rhs.end()); rhs.m_end = rhs.begin() + min_size; } } template inline void svector::grow(size_type min_capacity) { size_type current_size = size(); size_type new_capacity = 2 * current_size + 1; // Always grow. if (new_capacity < min_capacity) { new_capacity = min_capacity; } T* new_alloc; // is data stack allocated? if (m_begin == &m_data[0]) { new_alloc = m_allocator.allocate(new_capacity); std::uninitialized_copy(m_begin, m_end, new_alloc); } else { // If this wasn't grown from the inline copy, grow the allocated space. new_alloc = m_allocator.allocate(new_capacity); std::uninitialized_copy(m_begin, m_end, new_alloc); m_allocator.deallocate(m_begin, std::size_t(m_capacity - m_begin)); } XTENSOR_ASSERT(new_alloc); m_end = new_alloc + current_size; m_begin = new_alloc; m_capacity = new_alloc + new_capacity; } template inline bool operator==(const std::vector& lhs, const svector& rhs) { return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); } template inline bool operator==(const svector& lhs, const std::vector& rhs) { return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); } template inline bool operator==(const svector& lhs, const svector& rhs) { return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); } template inline bool operator!=(const svector& lhs, const svector& rhs) { return !(lhs == rhs); } template inline bool operator<(const svector& lhs, const svector& rhs) { return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } template inline bool operator<=(const svector& lhs, const svector& rhs) { return !(lhs > rhs); } template inline bool operator>(const svector& lhs, const svector& rhs) { return rhs < lhs; } template inline bool operator>=(const svector& lhs, const svector& rhs) { return !(lhs < rhs); } template inline void swap(svector& lhs, svector& rhs) noexcept { lhs.swap(rhs); } template struct rebind_container> { using traits = std::allocator_traits; using allocator = typename traits::template rebind_alloc; using type = svector; }; /** * This array class is modeled after ``std::array`` but adds optional alignment through a template * parameter. * * To be moved to xtl, along with the rest of xstorage.hpp */ template class alignas(Align) aligned_array : public std::array { public: // Note: this is for alignment detection. The allocator serves no other purpose than // that of a trait here. using allocator_type = std::conditional_t, std::allocator>; }; #if defined(_MSC_VER) #define XTENSOR_CONST #else #define XTENSOR_CONST const #endif #if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__) #define GCC4_FALLBACK namespace const_array_detail { template struct array_traits { using storage_type = T[N]; static constexpr T& ref(const storage_type& t, std::size_t n) noexcept { return const_cast(t[n]); } static constexpr T* ptr(const storage_type& t) noexcept { return const_cast(t); } }; template struct array_traits { struct empty { }; using storage_type = empty; static constexpr T& ref(const storage_type& /*t*/, std::size_t /*n*/) noexcept { return *static_cast(nullptr); } static constexpr T* ptr(const storage_type& /*t*/) noexcept { return nullptr; } }; } #endif /** * A std::array like class with all member function (except reverse iterators) * as constexpr. The data is immutable once set. */ template struct const_array { using size_type = std::size_t; using value_type = T; using pointer = value_type*; using const_pointer = const value_type*; using reference = value_type&; using const_reference = const value_type&; using difference_type = std::ptrdiff_t; using iterator = pointer; using const_iterator = const_pointer; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; constexpr const_reference operator[](std::size_t idx) const { #ifdef GCC4_FALLBACK return const_array_detail::array_traits::ref(m_data, idx); #else return m_data[idx]; #endif } constexpr const_iterator begin() const noexcept { return cbegin(); } constexpr const_iterator end() const noexcept { return cend(); } constexpr const_iterator cbegin() const noexcept { return data(); } constexpr const_iterator cend() const noexcept { return data() + N; } // TODO make constexpr once C++17 arrives reverse_iterator rbegin() const noexcept { return crbegin(); } reverse_iterator rend() const noexcept { return crend(); } const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(end()); } const_reverse_iterator crend() const noexcept { return const_reverse_iterator(begin()); } constexpr const_pointer data() const noexcept { #ifdef GCC4_FALLBACK return const_array_detail::array_traits::ptr(m_data); #else return m_data; #endif } constexpr const_reference front() const noexcept { #ifdef GCC4_FALLBACK return const_array_detail::array_traits::ref(m_data, 0); #else return m_data[0]; #endif } constexpr const_reference back() const noexcept { #ifdef GCC4_FALLBACK return N ? const_array_detail::array_traits::ref(m_data, N - 1) : const_array_detail::array_traits::ref(m_data, 0); #else return m_data[size() - 1]; #endif } constexpr bool empty() const noexcept { return size() == size_type(0); } constexpr size_type size() const noexcept { return N; } #ifdef GCC4_FALLBACK XTENSOR_CONST typename const_array_detail::array_traits::storage_type m_data; #else XTENSOR_CONST T m_data[N > 0 ? N : 1]; #endif }; #undef GCC4_FALLBACK template inline bool operator==(const const_array& lhs, const const_array& rhs) { return std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin()); } template inline bool operator!=(const const_array& lhs, const const_array& rhs) { return !(lhs == rhs); } template inline bool operator<(const const_array& lhs, const const_array& rhs) { return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } template inline bool operator<=(const const_array& lhs, const const_array& rhs) { return !(lhs > rhs); } template inline bool operator>(const const_array& lhs, const const_array& rhs) { return rhs < lhs; } template inline bool operator>=(const const_array& lhs, const const_array& rhs) { return !(lhs < rhs); } // Workaround for rebind_container problems on GCC 8 with C++17 enabled #if defined(__GNUC__) && __GNUC__ > 6 && !defined(__clang__) && __cplusplus >= 201703L template struct rebind_container> { using type = aligned_array; }; template struct rebind_container> { using type = const_array; }; #endif /** * @class fixed_shape * Fixed shape implementation for compile time defined arrays. * @sa xshape */ template class fixed_shape { public: #if defined(_MSC_VER) using cast_type = std::array; #define XTENSOR_FIXED_SHAPE_CONSTEXPR inline #else using cast_type = const_array; #define XTENSOR_FIXED_SHAPE_CONSTEXPR constexpr #endif using value_type = std::size_t; using size_type = std::size_t; using const_iterator = typename cast_type::const_iterator; static constexpr std::size_t size() { return sizeof...(X); } template static constexpr auto get() { using tmp_cast_type = std::array; return std::get(tmp_cast_type{X...}); } XTENSOR_FIXED_SHAPE_CONSTEXPR operator cast_type() const { return cast_type({X...}); } XTENSOR_FIXED_SHAPE_CONSTEXPR auto begin() const { return m_array.begin(); } XTENSOR_FIXED_SHAPE_CONSTEXPR auto end() const { return m_array.end(); } auto rbegin() const { return m_array.rbegin(); } auto rend() const { return m_array.rend(); } XTENSOR_FIXED_SHAPE_CONSTEXPR auto cbegin() const { return m_array.cbegin(); } XTENSOR_FIXED_SHAPE_CONSTEXPR auto cend() const { return m_array.cend(); } XTENSOR_FIXED_SHAPE_CONSTEXPR std::size_t operator[](std::size_t idx) const { return m_array[idx]; } XTENSOR_FIXED_SHAPE_CONSTEXPR bool empty() const { return sizeof...(X) == 0; } private: XTENSOR_CONSTEXPR_ENHANCED_STATIC cast_type m_array = cast_type({X...}); }; #ifdef XTENSOR_HAS_CONSTEXPR_ENHANCED template constexpr typename fixed_shape::cast_type fixed_shape::m_array; #endif #undef XTENSOR_FIXED_SHAPE_CONSTEXPR template class sequence_view { public: using value_type = typename E::value_type; using reference = typename E::reference; using const_reference = typename E::const_reference; using pointer = typename E::pointer; using const_pointer = typename E::const_pointer; using size_type = typename E::size_type; using difference_type = typename E::difference_type; using iterator = typename E::iterator; using const_iterator = typename E::const_iterator; using reverse_iterator = typename E::reverse_iterator; using const_reverse_iterator = typename E::const_reverse_iterator; explicit sequence_view(const E& container); template explicit sequence_view(const sequence_view& other); template ().begin())> operator T() const; bool empty() const; size_type size() const; const_reference operator[](std::size_t idx) const; const_iterator end() const; const_iterator begin() const; const_iterator cend() const; const_iterator cbegin() const; const_reverse_iterator rend() const; const_reverse_iterator rbegin() const; const_reverse_iterator crend() const; const_reverse_iterator crbegin() const; const_reference front() const; const_reference back() const; const E& storage() const; private: const E& m_sequence; }; template sequence_view::sequence_view(const E& container) : m_sequence(container) { } template template sequence_view::sequence_view(const sequence_view& other) : m_sequence(other.storage()) { } template template sequence_view::operator T() const { T ret = xtl::make_sequence(this->size()); std::copy(this->cbegin(), this->cend(), ret.begin()); return ret; } template bool sequence_view::empty() const { return size() == size_type(0); } template auto sequence_view::size() const -> size_type { if (End == -1) { return m_sequence.size() - static_cast(Start); } else { return static_cast(End - Start); } } template auto sequence_view::operator[](std::size_t idx) const -> const_reference { return m_sequence[idx + static_cast(Start)]; } template auto sequence_view::end() const -> const_iterator { if (End != -1) { return m_sequence.begin() + End; } else { return m_sequence.end(); } } template auto sequence_view::begin() const -> const_iterator { return m_sequence.begin() + Start; } template auto sequence_view::cend() const -> const_iterator { return end(); } template auto sequence_view::cbegin() const -> const_iterator { return begin(); } template auto sequence_view::rend() const -> const_reverse_iterator { return const_reverse_iterator(begin()); } template auto sequence_view::rbegin() const -> const_reverse_iterator { return const_reverse_iterator(end()); } template auto sequence_view::crend() const -> const_reverse_iterator { return rend(); } template auto sequence_view::crbegin() const -> const_reverse_iterator { return rbegin(); } template auto sequence_view::front() const -> const_reference { return *(m_sequence.begin() + Start); } template auto sequence_view::back() const -> const_reference { if (End == -1) { return m_sequence.back(); } else { return m_sequence[static_cast(End - 1)]; } } template const E& sequence_view::storage() const { return m_sequence; } template inline bool operator==(const sequence_view& lhs, const sequence_view& rhs) { return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); } template inline bool operator!=(const sequence_view& lhs, const sequence_view& rhs) { return !(lhs == rhs); } } /****************************** * std::tuple_size extensions * ******************************/ // The C++ standard defines tuple_size as a class, however // G++ 8 C++ library does define it as a struct hence we get // clang warnings here // Do not remove space between "#" and "pragma". This is required for CRAN checks. // clang-format off #if defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wmismatched-tags" #endif // clang-format on namespace std { template class tuple_size> : public integral_constant { }; template class tuple_size> : public integral_constant { }; template class tuple_size> : public integral_constant { }; // Undefine tuple size for not-known sequence view size template class tuple_size>; } // Do not remove space between "#" and "pragma". This is required for CRAN checks. // clang-format off #if defined(__clang__) # pragma clang diagnostic pop #endif // clang-format on #undef XTENSOR_CONST #endif