diff --git a/include/xtensor/xtiny.hpp b/include/xtensor/xtiny.hpp new file mode 100644 index 000000000..b75d1bf11 --- /dev/null +++ b/include/xtensor/xtiny.hpp @@ -0,0 +1,1925 @@ +/*************************************************************************** +* Copyright (c) 2017, Ullrich Koethe * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_XTINY_HPP +#define XTENSOR_XTINY_HPP + +#include +#include +#include +#include +#include +#include +#include +#include // std::ignore + +#include "xconcepts.hpp" +#include "xexception.hpp" +#include "xutils.hpp" +#include "xbuffer_adaptor.hpp" +#include "xstorage.hpp" + +namespace xt +{ + /*****************/ + /* prerequisites */ + /*****************/ + + using index_t = std::ptrdiff_t; + + constexpr static index_t runtime_size = -1; + + namespace tags + { + struct xtiny_tag {}; + + struct skip_initialization_tag {}; + } + + namespace + { + tags::skip_initialization_tag dont_init; + } + + template + struct xtiny_concept + : public std::integral_constant>::value> + { + }; + + /****************/ + /* declarations */ + /****************/ + + template + class xtiny_impl; + + template + class xtiny; + + /*********/ + /* xtiny */ + /*********/ + + /* Adds common functionality to the respective xtiny_impl */ + template + class xtiny + : public xtiny_impl + { + public: + + using self_type = xtiny; + using base_type = xtiny_impl; + using value_type = typename base_type::value_type; + using const_value_type = typename base_type::const_value_type; + using reference = typename base_type::reference; + using const_reference = typename base_type::const_reference; + using pointer = typename base_type::pointer; + using const_pointer = typename base_type::const_pointer; + using iterator = typename base_type::iterator; + using const_iterator = typename base_type::const_iterator; + using reverse_iterator = typename base_type::reverse_iterator; + using const_reverse_iterator = typename base_type::const_reverse_iterator; + using size_type = typename base_type::size_type; + using difference_type = typename base_type::difference_type; + + using base_type::owns_memory; + using base_type::has_fixed_size; + using base_type::static_size; + + using base_type::base_type; + + xtiny(); + xtiny(xtiny const & rhs); + xtiny(xtiny && rhs); + + template + explicit xtiny(xtiny const & rhs); + + template + explicit xtiny(std::vector const & v); + + template + explicit xtiny(std::array const & v); + + xtiny & operator=(xtiny const & rhs); + xtiny & operator=(xtiny && rhs); + + xtiny & operator=(value_type const & v); + + template + xtiny & operator=(std::vector const & v); + + template + xtiny & operator=(std::array const & v); + + template + xtiny & operator=(xtiny const & rhs); + + using base_type::assign; + + void assign(std::initializer_list v); + + using base_type::data; + + using base_type::operator[]; + reference at(size_type i); + constexpr const_reference at(size_type i) const; + + reference front(); + reference back(); + constexpr const_reference front() const; + constexpr const_reference back() const; + + template + auto subarray(); + template + auto subarray() const; + auto subarray(size_type FROM, size_type TO); + auto subarray(size_type FROM, size_type TO) const; + + auto erase(size_type m) const; + auto pop_front() const; + auto pop_back() const; + + auto insert(size_type m, value_type v) const; + auto push_front(value_type v) const; + auto push_back(value_type v) const; + + using base_type::begin; + constexpr const_iterator cbegin() const; + iterator end(); + constexpr const_iterator end() const; + constexpr const_iterator cend() const; + + using base_type::rbegin; + constexpr const_reverse_iterator crbegin() const; + reverse_iterator rend(); + constexpr const_reverse_iterator rend() const; + constexpr const_reverse_iterator crend() const; + + using base_type::size; + using base_type::max_size; + constexpr bool empty() const; + + using base_type::swap; + }; + + /******************************/ + /* default dynamic xtiny_impl */ + /******************************/ + + template + class xtiny_impl + : public xtiny_impl + { + using base_type = xtiny_impl; + public: + using base_type::base_type; + }; + + /******************************************/ + /* xtiny_impl: dynamic shape, owns memory */ + /******************************************/ + + template + class xtiny_impl + : public tags::xtiny_tag + { + public: + using self_type = xtiny_impl; + using representation_type = VALUETYPE *; + using buffer_type = VALUETYPE[BUFFER_SIZE < 1 ? 1 : BUFFER_SIZE]; + using allocator_type = std::allocator; + + using value_type = VALUETYPE; + using const_value_type = typename std::add_const::type; + using reference = value_type &; + using const_reference = const_value_type &; + using pointer = value_type *; + using const_pointer = const_value_type *; + using iterator = value_type *; + using const_iterator = const_value_type *; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + constexpr static bool owns_memory = true; + constexpr static bool has_fixed_size = false; + constexpr static index_t static_size = runtime_size; + constexpr static index_t buffer_size = BUFFER_SIZE; + + template + using rebind = xtiny; + + template + using rebind_size = xtiny; + + xtiny_impl(); + ~xtiny_impl(); + + explicit xtiny_impl(size_type n); + xtiny_impl(size_type n, const value_type& v); + xtiny_impl(size_type n, tags::skip_initialization_tag); + + template > + xtiny_impl(IT begin, IT end); + + explicit xtiny_impl(std::initializer_list const & v); + + xtiny_impl(xtiny_impl const & v); + xtiny_impl(xtiny_impl && v); + + xtiny_impl & operator=(xtiny_impl const & v); + xtiny_impl & operator=(xtiny_impl && v); + + void assign(size_type n, const value_type& v); + + template > + void assign(IT other_begin, IT other_end); + + reference operator[](size_type i); + constexpr const_reference operator[](size_type i) const; + + pointer data(); + constexpr const_pointer data() const; + + void resize(size_type n); + + size_type capacity() const; + size_type size() const; + size_type max_size() const; + bool on_stack() const; + + iterator begin(); + const_iterator begin() const; + + reverse_iterator rbegin(); + const_reverse_iterator rbegin() const; + + void swap(xtiny_impl & other); + + protected: + constexpr static bool may_use_uninitialized_memory = xtrivially_default_constructible::value; + + /* allocate() assumes that m_size is already set, + but no memory has been allocated yet */ + void allocate(value_type const & v = value_type()); + void allocate(tags::skip_initialization_tag); + template > + void allocate(IT other_begin); + + void deallocate(); + + allocator_type m_allocator; + size_type m_size; + representation_type m_data; + buffer_type m_buffer; + }; + + /**********************************/ + /* default fixed shape xtiny_impl */ + /**********************************/ + + template + class xtiny_impl + : public xtiny_impl> + { + using base_type = xtiny_impl>; + public: + using base_type::base_type; + }; + + /****************************************/ + /* xtiny_impl: fixed shape, owns memory */ + /****************************************/ + + template + class xtiny_impl> + : public std::array + , public tags::xtiny_tag + { + public: + using base_type = std::array; + using self_type = xtiny_impl; + + using value_type = VALUETYPE; + using const_value_type = typename std::add_const::type; + using reference = typename base_type::reference; + using const_reference = typename base_type::const_reference; + using pointer = typename base_type::pointer; + using const_pointer = typename base_type::const_pointer; + using iterator = typename base_type::iterator; + using const_iterator = typename base_type::const_iterator; + using reverse_iterator = typename base_type::reverse_iterator; + using const_reverse_iterator = typename base_type::const_reverse_iterator; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + constexpr static bool owns_memory = true; + constexpr static bool has_fixed_size = true; + constexpr static index_t static_size = N; + + template + using rebind = xtiny; + + template + using rebind_size = xtiny; + + xtiny_impl(); + + explicit xtiny_impl(size_type n); + xtiny_impl(size_type n, const value_type& v); + xtiny_impl(size_type n, tags::skip_initialization_tag); + + template > + explicit xtiny_impl(IT begin); + + template > + xtiny_impl(IT begin, IT end); + + explicit xtiny_impl(std::initializer_list const & v); + + xtiny_impl(xtiny_impl const & v); + xtiny_impl(xtiny_impl && v); + + xtiny_impl & operator=(xtiny_impl const & v); + xtiny_impl & operator=(xtiny_impl && v); + + void assign(size_type n, const value_type& v); + + template > + void assign(IT other_begin, IT other_end); + + using base_type::operator[]; + using base_type::data; + + using base_type::size; + using base_type::max_size; + constexpr size_type capacity() const; + + using base_type::begin; + using base_type::cbegin; + using base_type::rbegin; + using base_type::crbegin; + using base_type::end; + using base_type::cend; + using base_type::rend; + using base_type::crend; + + using base_type::swap; + }; + + /******************************/ + /* representation type traits */ + /******************************/ + + namespace xtiny_detail + { + + template + struct test_value_type + { + static void test(...); + + template + static typename U::value_type test(U *, typename U::value_type * = 0); + + constexpr static bool value = !std::is_same::value; + }; + + template ::value, + bool is_iterator=iterator_concept::value> + struct representation_type_traits; + + template + struct representation_type_traits // T is a container + { + using value_type = typename T::value_type; + using iterator = typename T::iterator; + using const_iterator = typename T::const_iterator; + using reverse_iterator = typename T::reverse_iterator; + using const_reverse_iterator = typename T::const_reverse_iterator; + }; + + template + struct representation_type_traits // T is an iterator + { + using value_type = typename T::value_type; + using iterator = T; + using const_iterator = T; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + }; + + template + struct representation_type_traits + { + using value_type = T; + using iterator = value_type *; + using const_iterator = value_type const *; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + }; + + template + struct representation_type_traits + { + using value_type = T const; + using iterator = value_type *; + using const_iterator = value_type *; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + }; + } + + /********************************************/ + /* xtiny_impl: fixed shape, borrowed memory */ + /********************************************/ + + template + class xtiny_impl + : public tags::xtiny_tag + { + using traits = xtiny_detail::representation_type_traits; + using deduced_value_type = std::remove_const_t; + static_assert(std::is_same, deduced_value_type>::value, + "xtiny_impl: type mismatch between VALUETYPE and REPRESENTATION."); + + public: + using representation_type = REPRESENTATION; + using self_type = xtiny_impl; + + using value_type = VALUETYPE; + using const_value_type = typename std::add_const::type; + using reference = value_type &; + using const_reference = const_value_type &; + using pointer = value_type *; + using const_pointer = const_value_type *; + using iterator = typename traits::iterator; + using const_iterator = typename traits::const_iterator; + using reverse_iterator = typename traits::reverse_iterator; + using const_reverse_iterator = typename traits::const_reverse_iterator; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + constexpr static bool owns_memory = false; + constexpr static bool has_fixed_size = true; + constexpr static index_t static_size = N; + + xtiny_impl(); + + explicit xtiny_impl(representation_type const & begin); + xtiny_impl(representation_type const & begin, representation_type const & end); + + template > + xtiny_impl(IT begin, IT end); + + xtiny_impl(xtiny_impl const & v) = default; + xtiny_impl(xtiny_impl && v) = default; + + xtiny_impl & operator=(xtiny_impl const & v) = default; + xtiny_impl & operator=(xtiny_impl && v) = default; + + void reset(representation_type const & begin); + + void assign(size_type n, const value_type& v); + + template > + void assign(IT other_begin, IT other_end); + + reference operator[](size_type i); + constexpr const_reference operator[](size_type i) const; + + pointer data(); + constexpr const_pointer data() const; + + constexpr size_type size() const; + constexpr size_type max_size() const; + constexpr size_type capacity() const; + + iterator begin(); + constexpr const_iterator begin() const; + + reverse_iterator rbegin(); + constexpr const_reverse_iterator rbegin() const; + + void swap(xtiny_impl &); + + protected: + + representation_type m_data; + }; + + /**********************************************/ + /* xtiny_impl: dynamic shape, borrowed memory */ + /**********************************************/ + + template + class xtiny_impl + : public tags::xtiny_tag + { + using traits = xtiny_detail::representation_type_traits; + using deduced_value_type = std::remove_const_t; + static_assert(std::is_same, deduced_value_type>::value, + "xtiny_impl: type mismatch between VALUETYPE and REPRESENTATION."); + + public: + using representation_type = REPRESENTATION; + using self_type = xtiny_impl; + + using value_type = VALUETYPE; + using const_value_type = typename std::add_const::type; + using reference = value_type &; + using const_reference = const_value_type &; + using pointer = value_type *; + using const_pointer = const_value_type *; + using iterator = typename traits::iterator; + using const_iterator = typename traits::const_iterator; + using reverse_iterator = typename traits::reverse_iterator; + using const_reverse_iterator = typename traits::const_reverse_iterator; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + constexpr static bool owns_memory = false; + constexpr static bool has_fixed_size = false; + constexpr static index_t static_size = runtime_size; + + xtiny_impl(); + + xtiny_impl(representation_type const & begin, representation_type const & end); + + template > + xtiny_impl(IT begin, IT end); + + xtiny_impl(xtiny_impl const & v) = default; + xtiny_impl(xtiny_impl && v) = default; + + xtiny_impl & operator=(xtiny_impl const & v) = default; + xtiny_impl & operator=(xtiny_impl && v) = default; + + void reset(representation_type const & begin, representation_type const & end); + + void assign(size_type n, const value_type& v); + + template > + void assign(IT other_begin, IT other_end); + + reference operator[](size_type i); + constexpr const_reference operator[](size_type i) const; + + pointer data(); + constexpr const_pointer data() const; + + constexpr size_type size() const; + constexpr size_type max_size() const; + constexpr size_type capacity() const; + + iterator begin(); + constexpr const_iterator begin() const; + + reverse_iterator rbegin(); + constexpr const_reverse_iterator rbegin() const; + + void swap(xtiny_impl &); + + protected: + + size_type m_size; + representation_type m_data; + }; + + /**********************************************/ + /* xtiny_impl: dynamic shape, xbuffer_adaptor */ + /**********************************************/ + + template + class xtiny_impl> + : public xbuffer_adaptor + , public tags::xtiny_tag + { + using deduced_value_type = typename xbuffer_adaptor::value_type; + static_assert(std::is_same::value, + "tiny_array_base: type mismatch between VALUETYPE and REPRESENTATION."); + public: + using base_type = xbuffer_adaptor; + using self_type = xtiny_impl; + using value_type = VALUETYPE; + using const_value_type = typename std::add_const::type; + using reference = value_type &; + using const_reference = const_value_type &; + using pointer = value_type *; + using const_pointer = const_value_type *; + using iterator = typename base_type::iterator; + using const_iterator = typename base_type::const_iterator; + using reverse_iterator = typename base_type::reverse_iterator; + using const_reverse_iterator = typename base_type::const_reverse_iterator; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + constexpr static bool owns_memory = false; + constexpr static bool has_fixed_size = false; + constexpr static index_t static_size = runtime_size; + + using base_type::base_type; + + void assign(size_type n, const value_type& v); + + template > + void assign(IT other_begin, IT other_end); + + using base_type::operator[]; + using base_type::data; + using base_type::size; + + constexpr size_type max_size() const; + constexpr size_type capacity() const; + + using base_type::begin; + using base_type::cbegin; + using base_type::rbegin; + using base_type::crbegin; + using base_type::end; + using base_type::cend; + using base_type::rend; + using base_type::crend; + + using base_type::swap; + void swap(xtiny_impl &); + }; + + template + inline void + swap(xtiny & l, xtiny & r) + { + l.swap(r); + } + + /****************/ + /* xtiny output */ + /****************/ + + template + std::ostream & operator<<(std::ostream & o, xtiny const & v) + { + o << "{"; + if(v.size() > 0) + o << promote_type_t(v[0]); + for(decltype(v.size()) i=1; i < v.size(); ++i) + o << ", " << promote_type_t(v[i]); + o << "}"; + return o; + } + + /********************/ + /* xtiny comparison */ + /********************/ + + template + inline bool + operator==(xtiny const & l, + xtiny const & r) + { + return l.size() == r.size() && std::equal(l.cbegin(), l.cend(), r.cbegin()); + } + + template ::value && + std::is_convertible::value> > + inline bool + operator==(xtiny const & l, + V2 const & r) + { + auto i = l.cbegin(); + for(decltype(l.size()) k=0; k < l.size(); ++k, ++i) + { + if(*i != r) + { + return false; + } + } + return true; + } + + template ::value && + std::is_convertible::value> > + inline bool + operator==(V1 const & l, + xtiny const & r) + { + return r == l; + } + + template + inline bool + operator!=(xtiny const & l, + V2 const & r) + { + return !(l == r); + } + + template ::value && + std::is_convertible::value> > + inline bool + operator!=(V1 const & l, + xtiny const & r) + { + return !(r == l); + } + + /************************/ + /* xtiny implementation */ + /************************/ + + template + inline + xtiny::xtiny() + : base_type() + { + } + + template + inline + xtiny::xtiny(xtiny const & v) + : base_type(v) + { + } + + template + inline + xtiny::xtiny(xtiny && v) + : base_type(std::forward(v)) + { + } + + template + template + inline + xtiny::xtiny(xtiny const & v) + : base_type(v.cbegin(), v.cend()) + { + } + + template + template + inline + xtiny::xtiny(std::vector const & v) + : base_type(v.cbegin(), v.cend()) + { + } + + template + template + inline + xtiny::xtiny(std::array const & v) + : base_type(v.cbegin(), v.cend()) + { + } + + template + inline auto + xtiny::operator=(xtiny const & v) -> xtiny & + { + base_type::operator=(v); + return *this; + } + + template + inline auto + xtiny::operator=(xtiny && v) -> xtiny & + { + base_type::operator=(v); + return *this; + } + + template + inline auto + xtiny::operator=(value_type const & v) -> xtiny & + { + base_type::assign(size(), v); + return *this; + } + + template + template + inline auto + xtiny::operator=(std::vector const & v) -> xtiny & + { + base_type::assign(v.cbegin(), v.cend()); + return *this; + } + + template + template + inline auto + xtiny::operator=(std::array const & v) -> xtiny & + { + base_type::assign(v.cbegin(), v.cend()); + return *this; + } + + template + template + inline auto + xtiny::operator=(xtiny const & v) -> xtiny & + { + base_type::assign(v.cbegin(), v.cend()); + return *this; + } + + template + inline void + xtiny::assign(std::initializer_list v) + { + base_type::assign(v.begin(), v.end()); + } + + template + inline auto + xtiny::at(size_type i) -> reference + { + if(i < 0 || i >= size()) + { + throw std::out_of_range("xtiny::at()"); + } + return (*this)[i]; + } + + template + constexpr inline auto + xtiny::at(size_type i) const -> const_reference + { + if(i < 0 || i >= size()) + { + throw std::out_of_range("xtiny::at()"); + } + return (*this)[i]; + } + + template + inline auto + xtiny::front() -> reference + { + return (*this)[0]; + } + + template + inline auto + xtiny::back() -> reference + { + return (*this)[size()-1]; + } + + template + constexpr inline auto + xtiny::front() const -> const_reference + { + return (*this)[0]; + } + + template + constexpr inline auto + xtiny::back() const -> const_reference + { + return (*this)[size()-1]; + } + + template + template + inline auto + xtiny::subarray() + { + static_assert(FROM >= 0 && FROM < TO, + "xtiny::subarray(): range out of bounds."); + XTENSOR_PRECONDITION(TO <= size(), + "xtiny::subarray(): range out of bounds."); + return xtiny(begin()+FROM); + } + + template + template + inline auto + xtiny::subarray() const + { + static_assert(FROM >= 0 && FROM < TO, + "xtiny::subarray(): range out of bounds."); + XTENSOR_PRECONDITION(TO <= size(), + "xtiny::subarray(): range out of bounds."); + return xtiny(begin()+FROM); + } + + template + inline auto + xtiny::subarray(size_type FROM, size_type TO) + { + XTENSOR_PRECONDITION(FROM >= 0 && FROM < TO && TO <= size(), + "xtiny::subarray(): range out of bounds."); + return xtiny(begin()+FROM, begin()+TO); + } + + template + inline auto + xtiny::subarray(size_type FROM, size_type TO) const + { + XTENSOR_PRECONDITION(FROM >= 0 && FROM < TO && TO <= size(), + "xtiny::subarray(): range out of bounds."); + return xtiny(begin()+FROM, begin()+TO); + } + + template + inline auto + xtiny::erase(size_type m) const + { + XTENSOR_PRECONDITION(m >= 0 && m < size(), "xtiny::erase(): " + "Index "+std::to_string(m)+" out of bounds [0, "+std::to_string(size())+")."); + constexpr static index_t res_size = has_fixed_size + ? static_size-1 + : runtime_size; + xtiny res(size()-1, dont_init); + std::copy(cbegin(), cbegin()+m, res.begin()); + std::copy(cbegin()+m+1, cend(), res.begin()+m); + return res; + } + + template + inline auto + xtiny::pop_front() const + { + return erase(0); + } + + template + inline auto + xtiny::pop_back() const + { + return erase(size()-1); + } + + template + inline auto + xtiny::insert(size_type m, value_type v) const + { + XTENSOR_PRECONDITION(m >= 0 && m <= size(), "xtiny::insert(): " + "Index "+std::to_string(m)+" out of bounds [0, "+std::to_string(size())+"]."); + constexpr static index_t res_size = has_fixed_size + ? static_size+1 + : runtime_size; + xtiny res(size()+1, dont_init); + std::copy(cbegin(), cbegin()+m, res.begin()); + res[m] = v; + std::copy(cbegin()+m, cend(), res.begin()+m+1); + return res; + } + + template + inline auto + xtiny::push_front(value_type v) const + { + return insert(0, v); + } + + template + inline auto + xtiny::push_back(value_type v) const + { + return insert(size(), v); + } + + template + constexpr inline auto + xtiny::cbegin() const -> const_iterator + { + return base_type::begin(); + } + + template + inline auto + xtiny::end() -> iterator + { + return begin() + static_cast(size()); + } + + template + constexpr inline auto + xtiny::end() const -> const_iterator + { + return begin() + static_cast(size()); + } + + template + constexpr inline auto + xtiny::cend() const -> const_iterator + { + return cbegin() + static_cast(size()); + } + + template + constexpr inline auto + xtiny::crbegin() const -> const_reverse_iterator + { + return base_type::rbegin(); + } + + template + inline auto + xtiny::rend() -> reverse_iterator + { + return rbegin() + static_cast(size()); + } + + template + constexpr inline auto + xtiny::rend() const -> const_reverse_iterator + { + return rbegin() + static_cast(size()); + } + + template + constexpr inline auto + xtiny::crend() const -> const_reverse_iterator + { + return crbegin() + static_cast(size()); + } + + template + constexpr bool + xtiny::empty() const + { + return size() == 0; + } + + /*******************************************/ + /* xtiny_impl dynamic shape implementation */ + /*******************************************/ + + template + inline + xtiny_impl::xtiny_impl() + : m_size(0) + , m_data(m_buffer) + { + } + + template + inline + xtiny_impl::~xtiny_impl() + { + deallocate(); + } + + template + inline + xtiny_impl::xtiny_impl(size_type n) + : m_size(n) + , m_data(m_buffer) + { + allocate(); + } + + template + inline + xtiny_impl::xtiny_impl(size_type n, const value_type& v) + : m_size(n) + , m_data(m_buffer) + { + allocate(v); + } + + template + inline + xtiny_impl::xtiny_impl(size_type n, tags::skip_initialization_tag) + : m_size(n) + , m_data(m_buffer) + { + allocate(dont_init); + } + + template + template + inline + xtiny_impl::xtiny_impl(IT begin, IT end) + : m_size(0) + , m_data(m_buffer) + { + assign(begin, end); + } + + template + inline + xtiny_impl::xtiny_impl(std::initializer_list const & v) + : m_size(0) + , m_data(m_buffer) + { + assign(v.begin(), v.end()); + } + + template + inline + xtiny_impl::xtiny_impl(xtiny_impl const & v) + : xtiny_impl(v.begin(), v.begin()+v.size()) + { + } + + template + inline + xtiny_impl::xtiny_impl(xtiny_impl && v) + : m_size(0) + , m_data(m_buffer) + { + v.swap(*this); + } + + template + inline auto + xtiny_impl::operator=(xtiny_impl const & v) -> xtiny_impl & + { + if(this != &v) + { + assign(v.begin(), v.begin()+v.size()); + } + return *this; + } + + template + inline auto + xtiny_impl::operator=(xtiny_impl && v) -> xtiny_impl & + { + if(this != &v) + { + assign(v.begin(), v.begin() + v.size()); + } + return *this; + } + + template + inline void + xtiny_impl::assign(size_type n, const value_type& v) + { + if(m_size == n) + { + std::fill(begin(), begin()+size(), v); + } + else + { + deallocate(); + m_size = n; + allocate(v); + } + } + + template + template + inline void + xtiny_impl::assign(IT begin, IT end) + { + size_type n = static_cast(std::distance(begin, end)); + if(m_size == n) + { + for (size_type k = 0; k < m_size; ++k, ++begin) + { + m_data[k] = static_cast(*begin); + } + } + else + { + deallocate(); + m_size = n; + allocate(begin); + } + } + + template + inline auto + xtiny_impl::data() -> pointer + { + return m_data; + } + + template + constexpr inline auto + xtiny_impl::data() const -> const_pointer + { + return m_data; + } + + template + inline auto + xtiny_impl::operator[](size_type i) -> reference + { + return m_data[i]; + } + + template + constexpr inline auto + xtiny_impl::operator[](size_type i) const -> const_reference + { + return m_data[i]; + } + + template + inline void + xtiny_impl::resize(size_type n) + { + if(n != m_size) + { + deallocate(); + m_size = n; + allocate(); + } + } + + template + inline auto + xtiny_impl::capacity() const -> size_type + { + return std::max(m_size, buffer_size); + } + + template + inline auto + xtiny_impl::size() const -> size_type + { + return m_size; + } + + template + inline auto + xtiny_impl::max_size() const -> size_type + { + return m_allocator.max_size(); + } + + template + inline bool + xtiny_impl::on_stack() const + { + return m_data == m_buffer; + } + + template + inline auto + xtiny_impl::begin() -> iterator + { + return m_data; + } + + template + inline auto + xtiny_impl::begin() const -> const_iterator + { + return m_data; + } + + template + inline auto + xtiny_impl::rbegin() -> reverse_iterator + { + return reverse_iterator(m_data + m_size); + } + + template + inline auto + xtiny_impl::rbegin() const -> const_reverse_iterator + { + return const_reverse_iterator(m_data + m_size); + } + + template + inline void + xtiny_impl::swap(xtiny_impl & other) + { + using std::swap; + if(this == &other) + { + return; + } + if(m_size == 0 || m_size > buffer_size) + { + if(other.m_size == 0 || other.m_size > buffer_size) + { + // both use allocated memory (or no memory at all) + swap(m_data, other.m_data); + } + else + { + // self uses allocated memory, other the buffer + for(size_type k=0; k buffer_size) + { + // self uses the buffer, other allocated memory + for(size_type k=0; k + inline void + xtiny_impl::allocate(value_type const & v) + { + if(m_size > buffer_size) + { + m_data = m_allocator.allocate(m_size); + std::uninitialized_fill(m_data, m_data+m_size, v); + } + else + { + std::fill(m_data, m_data+m_size, v); + } + } + + template + inline void + xtiny_impl::allocate(tags::skip_initialization_tag) + { + if(m_size > buffer_size) + { + m_data = m_allocator.allocate(m_size); + if(!may_use_uninitialized_memory) + { + std::uninitialized_fill(m_data, m_data+m_size, value_type()); + } + } + } + + template + template + inline void + xtiny_impl::allocate(IT begin) + { + if(m_size > buffer_size) + { + m_data = m_allocator.allocate(m_size); + for(size_type k=0; k(*begin)); + } + } + else + { + for(size_type k=0; k(*begin); + } + } + } + + template + inline void + xtiny_impl::deallocate() + { + if(m_size > buffer_size) + { + if(!may_use_uninitialized_memory) + { + for(size_type k=0; k + inline + xtiny_impl>::xtiny_impl() + : base_type{} + { + } + + template + inline + xtiny_impl>::xtiny_impl(size_type n) + : xtiny_impl() + { + std::ignore = n; + XTENSOR_ASSERT_MSG(n == size(), "xtiny_impl(n): size mismatch"); + } + + template + inline + xtiny_impl>::xtiny_impl(size_type n, const value_type& v) + { + std::ignore = n; + XTENSOR_ASSERT_MSG(n == size(), "xtiny_impl(n): size mismatch"); + base_type::fill(v); + } + + template + inline + xtiny_impl>::xtiny_impl(size_type n, tags::skip_initialization_tag) + { + std::ignore = n; + XTENSOR_ASSERT_MSG(n == size(), "xtiny_impl(n): size mismatch."); + } + + template + template + inline + xtiny_impl>::xtiny_impl(IT begin) + { + assign(begin, begin+N); + } + + template + template + inline + xtiny_impl>::xtiny_impl(IT begin, IT end) + { + assign(begin, end); + } + + template + inline + xtiny_impl>::xtiny_impl(std::initializer_list const & v) + { + const size_t n = v.size(); + if(n == 1) + { + assign(N, static_cast(*v.begin())); + } + else if(n == N) + { + assign(v.begin(), v.end()); + } + else + { + XTENSOR_ASSERT_MSG(false, "xtiny_impl::xtiny_impl(std::initializer_list): size mismatch."); + } + } + + template + inline + xtiny_impl>::xtiny_impl(xtiny_impl const & v) + : base_type(v) + { + } + + template + inline + xtiny_impl>::xtiny_impl(xtiny_impl && v) + : base_type(std::forward(v)) + { + } + + template + inline auto + xtiny_impl>::operator=(xtiny_impl const & v) -> xtiny_impl & + { + base_type::operator=(v); + return *this; + } + + template + inline auto + xtiny_impl>::operator=(xtiny_impl && v) -> xtiny_impl & + { + base_type::operator=(std::forward(v)); + return *this; + } + + template + inline void + xtiny_impl>::assign(size_type n, const value_type& v) + { + std::ignore = n; + XTENSOR_ASSERT_MSG(n == size(), "xtiny_impl::assign(n, v): size mismatch."); + base_type::fill(v); + } + + template + template + inline void + xtiny_impl>::assign(IT begin, IT end) + { + std::ignore = end; + XTENSOR_ASSERT_MSG(std::distance(begin, end) == static_cast(size()), + "xtiny_impl::assign(begin, end): size mismatch."); + for(size_type k=0; k(*begin); + } + } + + template + constexpr inline auto + xtiny_impl>::capacity() const -> size_type + { + return N; + } + + /**********************************************/ + /* xtiny_impl fixed shape view implementation */ + /**********************************************/ + + template + inline + xtiny_impl::xtiny_impl() + : m_data() + { + } + + template + inline + xtiny_impl::xtiny_impl(representation_type const & begin) + : m_data(begin) + { + } + + template + inline + xtiny_impl::xtiny_impl(representation_type const & begin, representation_type const & end) + : m_data(begin) + { + std::ignore = end; + XTENSOR_ASSERT_MSG(std::distance(begin, end) == static_cast(size()), + "xtiny_impl(begin, end): size mismatch"); + } + + template + template + inline + xtiny_impl::xtiny_impl(IT begin, IT end) + : m_data(const_cast(&*begin)) + { + std::ignore = end; + XTENSOR_ASSERT_MSG(std::distance(begin, end) == static_cast(size()), + "xtiny_impl::assign(begin, end): size mismatch."); + } + + template + inline void + xtiny_impl::reset(representation_type const & begin) + { + m_data = begin; + } + + template + inline void + xtiny_impl::assign(size_type n, const value_type& v) + { + std::ignore = n; + XTENSOR_ASSERT_MSG(n == size(), "xtiny_impl::assign(n, v): size mismatch."); + for(size_type k=0; k + template + inline void + xtiny_impl::assign(IT begin, IT end) + { + std::ignore = end; + XTENSOR_ASSERT_MSG(std::distance(begin, end) == static_cast(size()), + "xtiny_impl::assign(begin, end): size mismatch."); + for(size_type k=0; k(*begin); + } + } + + template + inline auto + xtiny_impl::operator[](size_type i) -> reference + { + return m_data[i]; + } + + template + constexpr inline auto + xtiny_impl::operator[](size_type i) const -> const_reference + { + return m_data[i]; + } + + template + inline auto + xtiny_impl::data() -> pointer + { + return &m_data[0]; + } + + template + constexpr inline auto + xtiny_impl::data() const -> const_pointer + { + return &m_data[0]; + } + + template + constexpr inline auto + xtiny_impl::size() const -> size_type + { + return N; + } + + template + constexpr inline auto + xtiny_impl::max_size() const -> size_type + { + return N; + } + + template + constexpr inline auto + xtiny_impl::capacity() const -> size_type + { + return N; + } + + template + inline auto + xtiny_impl::begin() -> iterator + { + return m_data; + } + + template + constexpr inline auto + xtiny_impl::begin() const -> const_iterator + { + return m_data; + } + + template + inline auto + xtiny_impl::rbegin() -> reverse_iterator + { + return reverse_iterator(m_data+N); + } + + template + constexpr inline auto + xtiny_impl::rbegin() const -> const_reverse_iterator + { + return const_reverse_iterator(m_data+N); + } + + template + inline void + xtiny_impl::swap(xtiny_impl & other) + { + using std::swap; + swap(m_data, other.m_data); + } + + /************************************************/ + /* xtiny_impl dynamic shape view implementation */ + /************************************************/ + + template + inline + xtiny_impl::xtiny_impl() + : m_size(0) + , m_data() + { + } + + template + inline + xtiny_impl::xtiny_impl(representation_type const & begin, representation_type const & end) + : m_size(static_cast(std::distance(begin, end))) + , m_data(begin) + { + } + + template + template + inline + xtiny_impl::xtiny_impl(IT begin, IT end) + : m_size(static_cast(std::distance(begin, end))) + , m_data(const_cast(&*begin)) + { + } + + template + inline void + xtiny_impl::reset(representation_type const & begin, representation_type const & end) + { + m_size = static_cast(std::distance(begin, end)); + m_data = begin; + } + + template + inline void + xtiny_impl::assign(size_type n, const value_type& v) + { + std::ignore = n; + XTENSOR_ASSERT_MSG(n == size(), "xtiny_impl::assign(n, v): size mismatch."); + std::fill(begin(), begin()+size(), v); + } + + template + template + inline void + xtiny_impl::assign(IT begin, IT end) + { + std::ignore = end; + XTENSOR_ASSERT_MSG(std::distance(begin, end) == static_cast(size()), + "xtiny_impl::assign(begin, end): size mismatch."); + for(size_type k=0; k(*begin); + } + } + + template + inline auto + xtiny_impl::operator[](size_type i) -> reference + { + return m_data[i]; + } + + template + constexpr inline auto + xtiny_impl::operator[](size_type i) const -> const_reference + { + return m_data[i]; + } + + template + inline auto + xtiny_impl::data() -> pointer + { + return &m_data[0]; + } + + template + constexpr inline auto + xtiny_impl::data() const -> const_pointer + { + return &m_data[0]; + } + + template + constexpr inline auto + xtiny_impl::size() const -> size_type + { + return m_size; + } + + template + constexpr inline auto + xtiny_impl::max_size() const -> size_type + { + return m_size; + } + + template + constexpr inline auto + xtiny_impl::capacity() const -> size_type + { + return m_size; + } + + template + inline auto + xtiny_impl::begin() -> iterator + { + return m_data; + } + + template + constexpr inline auto + xtiny_impl::begin() const -> const_iterator + { + return m_data; + } + + template + inline auto + xtiny_impl::rbegin() -> reverse_iterator + { + return reverse_iterator(m_data+m_size); + } + + template + constexpr inline auto + xtiny_impl::rbegin() const -> const_reverse_iterator + { + return const_reverse_iterator(m_data+m_size); + } + + template + inline void + xtiny_impl::swap(xtiny_impl & other) + { + using std::swap; + swap(m_size, other.m_size); + swap(m_data, other.m_data); + } + + + /**************************************************/ + /* xtiny_impl xbuffer_adaptor view implementation */ + /**************************************************/ + + template + inline void + xtiny_impl>::assign(size_type n, const value_type& v) + { + std::ignore = n; + XTENSOR_ASSERT_MSG(n == size(), "xtiny_impl::assign(n, v): size mismatch."); + std::fill(begin(), begin()+size(), v); + } + + template + template + inline void + xtiny_impl>::assign(IT begin, IT end) + { + std::ignore = end; + XTENSOR_ASSERT_MSG(std::distance(begin, end) == static_cast(size()), + "xtiny_impl::assign(begin, end): size mismatch."); + for(size_type k=0; k(*begin); + } + } + + template + constexpr inline auto + xtiny_impl>::max_size() const -> size_type + { + return size(); + } + + template + constexpr inline auto + xtiny_impl>::capacity() const -> size_type + { + return size(); + } +} // namespace xt + +#endif // XTENSOR_XTINY_HPP diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b5d2f3a07..760e81742 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -122,6 +122,7 @@ set(XTENSOR_TESTS test_xtensor.cpp test_xtensor_adaptor.cpp test_xtensor_semantic.cpp + test_xtiny.cpp test_xvectorize.cpp test_xview.cpp test_xview_semantic.cpp diff --git a/test/test_xtiny.cpp b/test/test_xtiny.cpp new file mode 100644 index 000000000..0c198670a --- /dev/null +++ b/test/test_xtiny.cpp @@ -0,0 +1,345 @@ +/*************************************************************************** +* Copyright (c) 2017, Ullrich Koethe * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_ENABLE_ASSERT +#define XTENSOR_ENABLE_ASSERT +#endif + +#include +#include +#include + +#include +#include + +namespace xt +{ + static const unsigned SIZE = 3; + + template + struct xtiny_test_data + { + static T data[SIZE]; + }; + + template + T xtiny_test_data::data[SIZE] = {1, 2, 4}; + + template <> + float xtiny_test_data::data[SIZE] = { 1.2f, 2.4f, 4.6f}; + + template + class xtiny_test : public testing::Test + { + }; + + typedef testing::Types, + xtiny, + xtiny, + xtiny, // buffer_size > SIZE + xtiny // buffer_size < SIZE + > xtiny_types; + + TYPED_TEST_CASE(xtiny_test, xtiny_types); + + TYPED_TEST(xtiny_test, construction) + { + using V = TypeParam; + using T = typename V::value_type; + using size_type = typename V::size_type; + const bool fixed = V::has_fixed_size; + + T * data = xtiny_test_data::data; + int * idata = xtiny_test_data::data; + + V v0, + v1(SIZE, 1), + v2(idata, idata+SIZE), + v3(data, data+SIZE); + + EXPECT_EQ(v0.size(), fixed ? SIZE : 0); + EXPECT_EQ(v1.size(), SIZE); + EXPECT_EQ(v3.size(), SIZE); + EXPECT_EQ(v0.empty(), !fixed); + EXPECT_FALSE(v1.empty()); + EXPECT_FALSE(v3.empty()); + + EXPECT_EQ(v3.front(), data[0]); + EXPECT_EQ(v3.back(), data[SIZE-1]); + V const & cv3 = v3; + EXPECT_EQ(cv3.front(), data[0]); + EXPECT_EQ(cv3.back(), data[SIZE-1]); + + auto v3iter = v3.begin(); + auto v3citer = v3.cbegin(); + auto v3riter = v3.rbegin(); + auto v3criter = v3.crbegin(); + for(size_type k=0; k::value) + { + EXPECT_EQ(v3, (V{1, 2, 4})); + } + if(fixed) + { + EXPECT_EQ(v1, (V{1})); + } + else + { + EXPECT_EQ((xtiny(1,1)), (V{1})); + } + + V v; + v.assign(SIZE, 1); + EXPECT_EQ(v1, v); + v.assign({1, 2, 4}); + EXPECT_EQ(v2, v); + + v = 1; + EXPECT_EQ(v, v1); + v = v3; + EXPECT_EQ(v, v3); + + V v4(v1), v5(v3); + swap(v4, v5); + EXPECT_EQ(v3, v4); + EXPECT_EQ(v1, v5); + + // testing move constructor and assignment + v5 = v3.push_back(0).pop_back(); + EXPECT_EQ(v5, v3); + EXPECT_EQ(V(v3.push_back(0).pop_back()), v3); + } + + TYPED_TEST(xtiny_test, subarray) + { + using V = TypeParam; + using A = typename V::template rebind_size; + using T = typename V::value_type; + + T * data = xtiny_test_data::data; + V v3(data, data+SIZE); + V const & cv3 = v3; + + EXPECT_EQ(v3, (v3.template subarray<0, SIZE>())); + EXPECT_EQ(2u, (v3.template subarray<0, 2>().size())); + EXPECT_EQ(v3[0], (v3.template subarray<0, 2>()[0])); + EXPECT_EQ(v3[1], (v3.template subarray<0, 2>()[1])); + EXPECT_EQ(2u, (v3.template subarray<1, 3>().size())); + EXPECT_EQ(v3[1], (v3.template subarray<1, 3>()[0])); + EXPECT_EQ(v3[2], (v3.template subarray<1, 3>()[1])); + EXPECT_EQ(1u, (v3.template subarray<1, 2>().size())); + EXPECT_EQ(v3[1], (v3.template subarray<1, 2>()[0])); + EXPECT_EQ(1u, (v3.subarray(1, 2).size())); + EXPECT_EQ(v3[1], (v3.subarray(1, 2)[0])); + EXPECT_EQ(1u, (cv3.template subarray<1, 2>().size())); + EXPECT_EQ(v3[1], (cv3.template subarray<1, 2>()[0])); + EXPECT_EQ(1u, (cv3.subarray(1, 2).size())); + EXPECT_EQ(v3[1], (cv3.subarray(1, 2)[0])); + + A r{ 2,3,4,5 }; + EXPECT_EQ(r, (A{ 2,3,4,5 })); + EXPECT_EQ(r.subarray(1, 3).size(), 2u); + EXPECT_EQ(r.subarray(1, 3), (A{ 3,4 })); + EXPECT_EQ((r.template subarray<1, 3>().size()), 2u); + EXPECT_EQ((r.template subarray<1, 3>()), (A{ 3,4 })); + } + + TYPED_TEST(xtiny_test, erase_insert) + { + using V = TypeParam; + using V1 = typename V::template rebind_size; + using T = typename V::value_type; + + T * data = xtiny_test_data::data; + V v3(data, data+SIZE); + V1 v10(v3.begin(), v3.begin()+SIZE-1); + + EXPECT_EQ(v10, v3.erase(SIZE - 1)); + EXPECT_EQ(v3, v10.insert(SIZE - 1, v3[SIZE - 1])); + EXPECT_EQ(v10, v3.pop_back()); + EXPECT_EQ(v3, v10.push_back(v3[SIZE - 1])); + V1 v11(v3.begin() + 1, v3.begin() + SIZE); + EXPECT_EQ(v11, v3.erase(0)); + EXPECT_EQ(v3, v11.insert(0, v3[0])); + EXPECT_EQ(v11, v3.pop_front()); + EXPECT_EQ(v3, v11.push_front(v3[0])); + } + + TYPED_TEST(xtiny_test, comparison) + { + using V = TypeParam; + using T = typename V::value_type; + const bool fixed = V::has_fixed_size; + + T * data = xtiny_test_data::data; + + V v1{1}, + v2(SIZE, 1), + v3(data, data+SIZE); + + EXPECT_TRUE(v3 == v3); + EXPECT_EQ(v1 == v2, fixed); + EXPECT_TRUE(v1 == v1); + EXPECT_TRUE(v1 == 1); + EXPECT_TRUE(1 == v1); + EXPECT_TRUE(v1 != v3); + EXPECT_TRUE(v2 != v3); + EXPECT_TRUE(v1 != 0); + EXPECT_TRUE(0 != v1); + EXPECT_TRUE(v2 != 0); + EXPECT_TRUE(0 != v2); + } + + TYPED_TEST(xtiny_test, ostream) + { + using V = TypeParam; + using T = typename V::value_type; + + T * data = xtiny_test_data::data; + V v3(data, data+SIZE); + + std::ostringstream out; + out << v3; + if(std::is_integral::value) + { + std::string expected("{1, 2, 4}"); + EXPECT_EQ(expected, out.str()); + } + } + + TEST(xtiny, conversion) + { + using IV = xtiny; + using FV = xtiny; + IV iv{1,2,3}, + iv1(SIZE, 1); + FV fv{1.1f,2.2f,3.3f}, + fv1{1.0}; + + EXPECT_TRUE(iv1 == fv1); + EXPECT_TRUE(fv1 == iv1); + EXPECT_TRUE(iv != fv); + EXPECT_TRUE(fv != iv); + EXPECT_TRUE(iv == IV(fv)); + iv1 = fv; + EXPECT_TRUE(iv1 == iv); + } + + TEST(xtiny, interoperability) + { + using A = xtiny; + using B = xtiny; + using C = xtiny; + using D = xtiny; + using E = xtiny>; + + EXPECT_TRUE(xtiny_concept::value); + EXPECT_TRUE(xtiny_concept::value); + EXPECT_TRUE(xtiny_concept::value); + EXPECT_TRUE(xtiny_concept::value); + EXPECT_TRUE(xtiny_concept::value); + + static const size_t s = 4; + std::array data{1,2,3,4}; + A a(data.begin()); + B b(a); + C c(a); + D d(a); + E e(data.data(), s); + std::vector v(data.begin(), data.end()); + + EXPECT_EQ(b, a); + EXPECT_EQ(c, a); + EXPECT_EQ(d, a); + EXPECT_EQ(e, a); + + EXPECT_EQ(A(data), a); + EXPECT_EQ(B(data), a); + EXPECT_EQ(C(data), a); + EXPECT_EQ(D(data), a); + + EXPECT_EQ(A(v), a); + EXPECT_EQ(B(v), a); + EXPECT_EQ(C(v), a); + EXPECT_EQ(D(v), a); + + EXPECT_EQ((A{1,2,3,4}), a); + EXPECT_EQ((B{1,2,3,4}), a); + + data[0] = 0; + EXPECT_EQ(e, (B{0,2,3,4})); + EXPECT_EQ((a = e), e); + EXPECT_EQ((b = e), e); + EXPECT_EQ((c = e), e); + EXPECT_EQ((d = e), e); + + EXPECT_EQ((a = 1), A{1}); + EXPECT_EQ((b = 1), A{1}); + EXPECT_EQ((c = 1), A{1}); + EXPECT_EQ((d = 1), A{1}); + EXPECT_EQ((e = 1), A{1}); + + EXPECT_EQ((a = v), (A{1,2,3,4})); + EXPECT_EQ((b = v), a); + EXPECT_EQ((c = v), a); + EXPECT_EQ((d = v), a); + EXPECT_EQ((e = v), a); + + b[s-1] = 5; + EXPECT_EQ(b, (A{1,2,3,5})); + EXPECT_EQ((a = b), b); + EXPECT_EQ((c = b), b); + EXPECT_EQ((d = b), b); + EXPECT_EQ((e = b), b); + + data[0] = 0; + EXPECT_EQ((c = data), (A{0,2,3,5})); + EXPECT_EQ((a = data), c); + EXPECT_EQ((b = data), c); + EXPECT_EQ((d = data), c); + EXPECT_EQ((e = data), c); + + d.assign(v.begin(), v.end()); + EXPECT_EQ(d, (A{1,2,3,4})); + a.assign(v.begin(), v.end()); + EXPECT_EQ(a, d); + b.assign(v.begin(), v.end()); + EXPECT_EQ(b, d); + c.assign(v.begin(), v.end()); + EXPECT_EQ(c, d); + e.assign(v.begin(), v.end()); + EXPECT_EQ(e, d); + } +} // namespace xt \ No newline at end of file