From 9dca59bf40e3293670564738195ed5c13b197fc3 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Thu, 30 Jun 2022 15:55:02 +0100 Subject: [PATCH] etl::poly_span without etl::dynamic_extent --- include/etl/poly_span.h | 735 ++++++++++++++++++++----- include/etl/private/poly_span_utils.h | 125 +---- test/sanity-check/c++03/CMakeLists.txt | 1 + test/sanity-check/c++11/CMakeLists.txt | 1 + test/sanity-check/c++14/CMakeLists.txt | 1 + test/sanity-check/c++17/CMakeLists.txt | 1 + test/sanity-check/poly_span.h.t.cpp | 29 + test/test_poly_span.cpp | 365 ++++++++++++ test/vs2019/etl.vcxproj | 3 + test/vs2019/etl.vcxproj.filters | 9 + 10 files changed, 996 insertions(+), 274 deletions(-) create mode 100644 test/sanity-check/poly_span.h.t.cpp create mode 100644 test/test_poly_span.cpp diff --git a/include/etl/poly_span.h b/include/etl/poly_span.h index 8738da11..622c3d16 100644 --- a/include/etl/poly_span.h +++ b/include/etl/poly_span.h @@ -1,208 +1,641 @@ -#pragma once +///\file -#include "span/detail/poly_span_utils.h" +/****************************************************************************** +The MIT License(MIT) -#include -#include +Copyright (c) 2020 Dr. Alexander Bulovyatov +https://github.com/mrshurik/poly_span -namespace span { +Modifications: +Copyright(c) 2022 John Wellbelove -/** - * C++20 std::span-like view with focus on polymorphic aspect of types. - * Provides a base class / interface view. Type can be const or non-const. - * The class behaves like std::span, has O(1) index access and provides RandomAccess iterators. - * - * Hints: - * 1. NEVER assign to elements or apply mutable algorithms to a mutable view to a base class. Object slicing! - * 2. Pay attention to lifetime of the source buffer. poly_span should mostly be used as an argument of functions. - * 3. The view has no data() for reason. Pointer to the first byte of the first object is unknown in general. - */ -template -class poly_span { -private: +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef ETL_POLY_SPAN_INCLUDED +#define ETL_POLY_SPAN_INCLUDED + +#include "platform.h" +#include "type_traits.h" +#include "iterator.h" +#include "exception.h" +#include "error_handler.h" + +#if ETL_CPP11_SUPPORTED +namespace etl +{ + //*************************************************************************** + /// The base class for poly_span exceptions. + //*************************************************************************** + class poly_span_exception : public exception + { + public: + + poly_span_exception(string_type reason_, string_type file_name_, numeric_type line_number_) + : exception(reason_, file_name_, line_number_) + { + } + }; + + //*************************************************************************** + /// The out of range exceptions. + //*************************************************************************** + class poly_span_out_of_range : public poly_span_exception + { + public: + + poly_span_out_of_range(string_type file_name_, numeric_type line_number_) + : poly_span_exception("poly_span:out of range", file_name_, line_number_) + { + } + }; + + namespace private_poly_span + { + //*************************************************************************** + template + using HasSize = etl::is_integral().size())>; + + //*************************************************************************** + template + using DataType = decltype(etl::declval().data()); + + //*************************************************************************** + template + using HasData = etl::is_pointer>; + + //*************************************************************************** + template + using HasConvertibleData = etl::is_convertible, TType*>; + + //*************************************************************************** + template + using TransferConst = typename etl::conditional::value, + typename etl::add_const::type, + TTo>::type; + + //*************************************************************************** + template + class poly_span_iterator; + + //*************************************************************************** + template + struct IsPolySpanItImpl : etl::false_type {}; + + //*************************************************************************** + template + struct IsPolySpanItImpl> : etl::true_type {}; + + //*************************************************************************** + template + using IsPolySpanIterator = IsPolySpanItImpl; + + //*************************************************************************** + /// Iterator implementation + //*************************************************************************** + template + class poly_span_iterator : public etl::iterator + { + private: + + template + using EnableIfConversionToConst = etl::enable_if_t::value && etl::is_same::value>; + + public: + + //*************************************************************************** + poly_span_iterator() = default; + poly_span_iterator(const poly_span_iterator&) = default; + poly_span_iterator& operator=(const poly_span_iterator&) = default; + + //*************************************************************************** + poly_span_iterator(TType* ptr, size_t element_size) noexcept + : m_ptr(ptr), m_element_size(element_size) + { + } + + //*************************************************************************** + template > + poly_span_iterator(const poly_span_iterator& other) noexcept + : m_ptr(other.m_ptr) + , m_element_size(other.m_element_size) + { + } + + //*************************************************************************** + TType* operator ->() const noexcept + { + return m_ptr; + } + + //*************************************************************************** + TType& operator *() const noexcept + { + return *m_ptr; + } + + //*************************************************************************** + poly_span_iterator& operator ++() noexcept + { + from_char_ptr(to_char_ptr() + m_element_size); + return *this; + } + + //*************************************************************************** + poly_span_iterator& operator --() noexcept + { + from_char_ptr(to_char_ptr() - m_element_size); + return *this; + } + + //*************************************************************************** + poly_span_iterator& operator +=(ptrdiff_t n) noexcept + { + from_char_ptr(to_char_ptr() + (n * m_element_size)); + return *this; + } + + //*************************************************************************** + friend ptrdiff_t operator -(poly_span_iterator left, poly_span_iterator right) noexcept + { + return (left.to_char_ptr() - right.to_char_ptr()) / left.m_element_size; + } + + //*************************************************************************** + poly_span_iterator operator +(ptrdiff_t n) const noexcept + { + poly_span_iterator tmp = *this; + tmp += n; + return tmp; + } + + //*************************************************************************** + poly_span_iterator operator ++(int) noexcept + { + poly_span_iterator tmp = *this; + ++(*this); + return tmp; + } + + //*************************************************************************** + poly_span_iterator operator --(int) noexcept + { + poly_span_iterator tmp = *this; + --(*this); + return tmp; + } + + //*************************************************************************** + poly_span_iterator operator-(ptrdiff_t n) const noexcept + { + return *this + (-n); + } + + //*************************************************************************** + poly_span_iterator& operator-=(ptrdiff_t n) noexcept + { + return *this += -n; + } + + //*************************************************************************** + TType& operator[](ptrdiff_t n) const noexcept + { + return *(*this + n); + } + + //*************************************************************************** + friend poly_span_iterator operator+(ptrdiff_t n, poly_span_iterator other) noexcept + { + return other + n; + } + + //*************************************************************************** + friend bool operator==(poly_span_iterator left, poly_span_iterator right) noexcept + { + return left.m_ptr == right.m_ptr; + } + + //*************************************************************************** + friend bool operator!=(poly_span_iterator left, poly_span_iterator right) noexcept + { + return left.m_ptr != right.m_ptr; + } + + //*************************************************************************** + friend bool operator >(poly_span_iterator left, poly_span_iterator right) noexcept + { + return left.m_ptr > right.m_ptr; + } + + //*************************************************************************** + friend bool operator <(poly_span_iterator left, poly_span_iterator right) noexcept + { + return left.m_ptr < right.m_ptr; + } + + //*************************************************************************** + friend bool operator>=(poly_span_iterator left, poly_span_iterator right) noexcept + { + return left.m_ptr >= right.m_ptr; + } + + //*************************************************************************** + friend bool operator<=(poly_span_iterator left, poly_span_iterator right) noexcept + { + return left.m_ptr <= right.m_ptr; + } + + private: + + template + friend class poly_span_iterator; + + using void_ptr_t = TransferConst*; + using char_ptr_t = TransferConst*; + + //*************************************************************************** + char_ptr_t to_char_ptr() const noexcept + { + return static_cast(static_cast(m_ptr)); + } + + //*************************************************************************** + void from_char_ptr(char_ptr_t ptr) noexcept + { + m_ptr = static_cast(static_cast(ptr)); + } + + TType* m_ptr; + size_t m_element_size; + }; + } + + //*************************************************************************** + /// C++11 etl::span-like view with focus on polymorphic aspect of types. + /// Provides a base class / interface view. Type can be const or non-const. + /// The class behaves like etl::span, has O(1) index access and provides RandomAccess iterators. + /// Hints: + /// 1. NEVER assign to elements or apply mutable algorithms to a mutable view to a base class. Object slicing! + /// 2. Pay attention to lifetime of the source buffer. poly_span should mostly be used as an argument of functions. + /// 3. The view has no data() for reason. Pointer to the first byte of the first object is unknown in general. + //*************************************************************************** + template + class poly_span + { + private: + + //*************************************************************************** template - using EnableIfConvertibleFromObject = detail::enable_if_t< - detail::HasSize::value && detail::HasData::value && - detail::HasConvertibleData::value>; + using EnableIfConvertibleFromObject = etl::enable_if_t::value && + private_poly_span::HasData::value && + private_poly_span::HasConvertibleData::value>; + //*************************************************************************** template - using EnableIfConvertibleType = detail::enable_if_t< - std::is_convertible::value>; + using EnableIfTConvertibleType = etl::enable_if_t::value>; + //*************************************************************************** template - using IsContiguousIterator = std::integral_constant::iterator_category>::value && - !detail::IsPolySpanIterator::value>; + using IsTContiguousIterator = etl::integral_constant::iterator_category>::value// && + /*!private_poly_span::IsPolySpanIterator::value*/>; -public: - using element_type = Type; - using value_type = typename std::remove_cv::type; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using pointer = Type*; - using reference = Type&; - using const_pointer = const Type*; - using const_reference = const Type&; - using iterator = detail::poly_span_iterator; - using const_iterator = detail::poly_span_iterator; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; + public: + + using element_type = TType; + using value_type = typename etl::remove_cv::type; + using size_type = size_t; + using difference_type = ptrdiff_t; + using pointer = TType*; + using reference = TType&; + using const_pointer = const TType*; + using const_reference = const TType&; + using iterator = private_poly_span::poly_span_iterator; + using const_iterator = private_poly_span::poly_span_iterator; + using reverse_iterator = etl::reverse_iterator; + using const_reverse_iterator = etl::reverse_iterator; static constexpr size_t npos = ~size_t(0); - /// Creates empty view. + //*************************************************************************** + /// Default constructor + //*************************************************************************** poly_span() = default; + //*************************************************************************** + /// Copy constructor + //*************************************************************************** poly_span(const poly_span&) = default; + + //*************************************************************************** + /// Assignment operator + //*************************************************************************** poly_span& operator=(const poly_span&) = default; + //*************************************************************************** /// Construct from a compatible poly_span: convert mutable to const, derived to base type. - template - poly_span(const poly_span& span) noexcept - : poly_span(span.m_dataPtr, span.m_size, span.m_element_size) {} - - - /// Construct from a range of iterators or pointers. Iterators must Contiguous. - template - poly_span(ContiguousIterator begin, ContiguousIterator end) noexcept - : poly_span(&*begin, end - begin, nullptr) { - static_assert(IsContiguousIterator::value, "Wrong iterator"); + //*************************************************************************** + template + poly_span(const poly_span& span) noexcept + : poly_span(span.m_dataPtr, span.m_size, span.m_element_size) + { } - /// Construct from a range defined as iterator/pointer to begin and size. Iterators must Contiguous. - template - poly_span(ContiguousIterator begin, size_t size) noexcept - : poly_span(&*begin, size, nullptr) { - static_assert(IsContiguousIterator::value, "Wrong iterator"); + //*************************************************************************** + /// Construct from a range of iterators or pointers. + /// Iterators must Contiguous. + //*************************************************************************** + template + poly_span(TContiguousIterator begin, TContiguousIterator end) noexcept + : poly_span(&*begin, end - begin, ETL_NULLPTR) + { + static_assert(IsTContiguousIterator::value, "Not a contiguous iterator"); } + //*************************************************************************** + /// Construct from a range defined as iterator/pointer to begin and size. + /// Iterators must Contiguous. + //*************************************************************************** + template + poly_span(TContiguousIterator begin, size_t size) noexcept + : poly_span(&*begin, size, ETL_NULLPTR) + { + static_assert(IsTContiguousIterator::value, "Not a contiguous iterator"); + } + + //*************************************************************************** /// Construct from a C-style array. - template - poly_span(ConvertibleType(&array)[N]) noexcept - : poly_span(array, N, nullptr) {} + //*************************************************************************** + template + poly_span(TConvertibleType(&array)[N]) noexcept + : poly_span(array, N, ETL_NULLPTR) + { + } - /// Construct from initializer_list. - template > - poly_span(std::initializer_list list) noexcept - : poly_span(list.begin(), list.size(), nullptr) {} + //*************************************************************************** + /// Construct from a buffer objects: etl::vector, etl::array, etl::span or any class with data() and size(). + //*************************************************************************** + template > + poly_span(TBufferLikeObj& buffer) noexcept + : poly_span(buffer.data() + , buffer.size() + , ETL_NULLPTR) + { + } - /// Construct from a buffer objects: std::vector, std::array, std::span or any class with data() and size(). - template > - poly_span(BufferLikeObj& buffer) noexcept - : poly_span(buffer.data(), buffer.size(), nullptr) {} + //*************************************************************************** + /// Get the beginning of the span + //*************************************************************************** + iterator begin() const noexcept + { + return { m_dataPtr, m_element_size }; + } + + //*************************************************************************** + /// Get the end of the span + //*************************************************************************** + iterator end() const noexcept + { + return { element_at(m_size), m_element_size }; + } + + //*************************************************************************** + /// Get the beginning of the span + //*************************************************************************** + const_iterator cbegin() const noexcept + { + return begin(); + } + + //*************************************************************************** + /// Get the end of the span + //*************************************************************************** + const_iterator cend() const noexcept + { + return end(); + } + + //*************************************************************************** + /// Get the reverse beginning of the span + //*************************************************************************** + reverse_iterator rbegin() const noexcept + { + return reverse_iterator(end()); + } - iterator begin() const noexcept { return {m_dataPtr, m_element_size}; } - iterator end() const noexcept { return {elementAt(m_size), m_element_size}; } - const_iterator cbegin() const noexcept { return begin(); } - const_iterator cend() const noexcept { return end(); } - reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } - reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } - const_reverse_iterator crbegin() const noexcept { return rbegin(); } - const_reverse_iterator crend() const noexcept { return rend(); } + //*************************************************************************** + /// Get the end of the span + //*************************************************************************** + reverse_iterator rend() const noexcept + { + return reverse_iterator(begin()); + } + //*************************************************************************** + /// Get the reverse beginning of the span + //*************************************************************************** + const_reverse_iterator crbegin() const noexcept + { + return rbegin(); + } + + //*************************************************************************** + /// Get the end of the span + //*************************************************************************** + const_reverse_iterator crend() const noexcept + { + return rend(); + } + + //*************************************************************************** /// Returns true if the view has no elements. - bool empty() const noexcept { return m_size == 0; } + //*************************************************************************** + bool empty() const noexcept + { + return m_size == 0; + } + //*************************************************************************** /// Number of elements in the view. - size_t size() const noexcept { return m_size; } - - /** - * @brief sizeof(OriginalType) where OriginalType is type of the source buffer. - * It's not sizeof(Type) for poly_span! - */ - size_t element_size() const noexcept { return m_element_size; } - - /** - * @brief Checks that other poly_span provides view to the same source buffer. - * It's not like std::span operator==() which checks for elements equality. - */ - bool equal_view(const poly_span& other) const noexcept { - return (m_dataPtr == other.m_dataPtr) && (m_size == other.m_size) - && (m_element_size == other.m_element_size); + //*************************************************************************** + size_t size() const noexcept + { + return m_size; } + //*************************************************************************** + /// \brief sizeof(OriginalType) where OriginalType is type of the source buffer. + /// It's not sizeof(TType) for poly_span! + //*************************************************************************** + size_t element_size() const noexcept + { + return m_element_size; + } + + //*************************************************************************** + /// \brief Checks that other poly_span provides view to the same source buffer. + /// It's not like etl::span operator==() which checks for elements equality. + //*************************************************************************** + bool equal_view(const poly_span& other) const noexcept + { + return (m_dataPtr == other.m_dataPtr) && + (m_size == other.m_size) && + (m_element_size == other.m_element_size); + } + + //*************************************************************************** /// Reference to the element with given index. No out of bounds check. - reference operator[](size_t index) const noexcept { return *elementAt(index); } - - /** - * @brief Index access with out of bounds checks. - * @param index position of element in the view. - * @throw std::out_of_range if index >= size(). - * @return reference to the element. - */ - reference at(size_t index) const { - if (index >= m_size) throw std::out_of_range("at() ind >= size()"); - return *elementAt(index); + //*************************************************************************** + reference operator[](size_t index) const noexcept + { + return *element_at(index); } + //*************************************************************************** + /// \brief Index access with out of bounds checks. + /// \param index position of element in the view. + /// Asserts etl::poly_span_out_of_range if index >= size(). + /// \return reference to the element. + //*************************************************************************** + reference at(size_t index) const + { + ETL_ASSERT(index < m_size, ETL_ERROR(etl::poly_span_out_of_range)); + + return *element_at(index); + } + + //*************************************************************************** /// Reference to the first element. The view must be non-empty. - reference front() const noexcept { return *m_dataPtr; } + //*************************************************************************** + reference front() const noexcept + { + return *m_dataPtr; + } + //*************************************************************************** /// Reference to the last element. The view must be non-empty. - reference back() const noexcept { return *elementAt(m_size - 1); } - - /** - * @brief Creates a new view as a subrange of this. - * @param pos Starting offset for the new view. - * @param count Number of elements to take into the new view. Default npos is to take all until the end. - * @throw std::out_of_range if pos > size(). - * @return New sub-view. If count > number of elements before the end it's the same as npos, no error. - */ - poly_span subspan(size_t pos, size_t count = npos) const { - if (pos > m_size) throw std::out_of_range("subspan() pos > size()"); - return {elementAt(pos), minImpl(m_size - pos, count), m_element_size}; + //*************************************************************************** + reference back() const noexcept + { + return *element_at(m_size - 1); } - /** - * @brief Creates a new view from exactly N first elements. - * @param count Exact number of elements to take. - * @throw std::out_of_range if count > size(). - * @return New sub-view. - */ - poly_span first(size_t count) const { - if (count > m_size) throw std::out_of_range("first() count > size()"); - return {m_dataPtr, count, m_element_size}; + //*************************************************************************** + /// \brief Creates a new view as a subrange of this. + /// \param pos Starting offset for the new view. + /// \param count Number of elements to take into the new view. Default npos is to take all until the end. + /// Asserts etl::poly_span_out_of_range if pos > size(). + /// \return New sub-view. If count > number of elements before the end it's the same as npos, no error. + //*************************************************************************** + poly_span subspan(size_t pos, size_t count = npos) const + { + ETL_ASSERT(pos <= m_size, ETL_ERROR(etl::poly_span_out_of_range)); + + return { element_at(pos), min_impl(m_size - pos, count), m_element_size }; } - /** - * @brief Creates a new view from exactly N last elements. - * @param count Exact number of elements to take. - * @throw std::out_of_range if count > size(). - * @return New sub-view. - */ - poly_span last(size_t count) const { - if (count > m_size) throw std::out_of_range("last() count > size()"); - return {elementAt(m_size - count), count, m_element_size}; + //*************************************************************************** + /// \brief Creates a new view from exactly N first elements. + /// \param count Exact number of elements to take. + /// Asserts etl::poly_span_out_of_range if count > size(). + /// \return New sub-view. + //*************************************************************************** + poly_span first(size_t count) const + { + ETL_ASSERT(count <= m_size, ETL_ERROR(etl::poly_span_out_of_range)); + + return { m_dataPtr, count, m_element_size }; } -private: - poly_span(Type* dataPtr, size_t size, size_t element_size) noexcept - : m_dataPtr(dataPtr), m_size(size), m_element_size(element_size) {} + //*************************************************************************** + /// \brief Creates a new view from exactly N last elements. + /// \param count Exact number of elements to take. + /// Asserts etl::poly_span_out_of_range if count > size(). + /// \return New sub-view. + //*************************************************************************** + poly_span last(size_t count) const + { + ETL_ASSERT(count <= m_size, ETL_ERROR(etl::poly_span_out_of_range)); + + return { element_at(m_size - count), count, m_element_size }; + } - template - poly_span(ConvertibleType* dataPtr, size_t size, std::nullptr_t) noexcept - : m_dataPtr(dataPtr), m_size(size), m_element_size(sizeof(ConvertibleType)) {} + private: + + //*************************************************************************** + poly_span(TType* dataPtr, size_t size, size_t element_size) noexcept + : m_dataPtr(dataPtr) + , m_size(size) + , m_element_size(element_size) + { + } + +#if ETL_USING_STL + //*************************************************************************** + template + poly_span(TConvertibleType* dataPtr, size_t size, std::nullptr_t) noexcept + : m_dataPtr(dataPtr) + , m_size(size) + , m_element_size(sizeof(TConvertibleType)) + { + } +#else + //*************************************************************************** + template + poly_span(TConvertibleType* dataPtr, size_t size, void*) noexcept + : m_dataPtr(dataPtr) + , m_size(size) + , m_element_size(sizeof(TConvertibleType)) + { + } +#endif template friend class poly_span; - using VoidPtr = detail::TransferConst*; - using CharPtr = detail::TransferConst*; + using void_ptr_t = private_poly_span::TransferConst*; + using char_ptr_t = private_poly_span::TransferConst*; - Type* elementAt(size_t ind) const noexcept { - const auto base = static_cast(static_cast(m_dataPtr)); - return static_cast(static_cast(base + ind * m_element_size)); + //*************************************************************************** + TType* element_at(size_t ind) const noexcept + { + const auto base = static_cast(static_cast(m_dataPtr)); + return static_cast(static_cast(base + (ind * m_element_size))); } + //*************************************************************************** template - static Value minImpl(Value a, Value b) noexcept { return (a < b) ? a : b; } + static Value min_impl(Value a, Value b) noexcept + { + return (a < b) ? a : b; + } - Type* m_dataPtr = nullptr; - size_t m_size = 0; + TType* m_dataPtr = ETL_NULLPTR; + size_t m_size = 0; size_t m_element_size = 0; -}; - + }; } + +#endif + +#endif diff --git a/include/etl/private/poly_span_utils.h b/include/etl/private/poly_span_utils.h index e6dc01f4..eaba3471 100644 --- a/include/etl/private/poly_span_utils.h +++ b/include/etl/private/poly_span_utils.h @@ -4,131 +4,10 @@ #include #include -namespace span { -namespace detail { +namespace private_poly_span +{ -template -using enable_if_t = typename std::enable_if::type; -template -using HasSize = std::is_integral().size())>; -template -using DataType = decltype(std::declval().data()); - -template -using HasData = std::is_pointer>; - -template -using HasConvertibleData = std::is_convertible, Type*>; - -template -using TransferConst = typename std::conditional::value, - typename std::add_const::type, - ToType>::type; - -template -class poly_span_iterator; - -template -struct IsPolySpanItImpl : std::false_type {}; - -template -struct IsPolySpanItImpl> : std::true_type {}; - -template -using IsPolySpanIterator = IsPolySpanItImpl; - -template -class poly_span_iterator : public std::iterator { -private: - template - using EnableIfConversionToConst = enable_if_t< - std::is_const::value && std::is_same::value>; - -public: - poly_span_iterator() = default; - poly_span_iterator(const poly_span_iterator&) = default; - poly_span_iterator& operator=(const poly_span_iterator&) = default; - - poly_span_iterator(Type* ptr, size_t element_size) noexcept - : m_ptr(ptr), m_element_size(element_size) {} - - template > - poly_span_iterator(const poly_span_iterator& other) noexcept - : m_ptr(other.m_ptr), m_element_size(other.m_element_size) {} - - Type* operator->() const noexcept { return m_ptr; } - Type& operator*() const noexcept { return *m_ptr; } - - friend bool operator==(poly_span_iterator left, poly_span_iterator right) noexcept { return left.m_ptr == right.m_ptr; } - friend bool operator!=(poly_span_iterator left, poly_span_iterator right) noexcept { return left.m_ptr != right.m_ptr; } - friend bool operator >(poly_span_iterator left, poly_span_iterator right) noexcept { return left.m_ptr > right.m_ptr; } - friend bool operator <(poly_span_iterator left, poly_span_iterator right) noexcept { return left.m_ptr < right.m_ptr; } - friend bool operator>=(poly_span_iterator left, poly_span_iterator right) noexcept { return left.m_ptr >= right.m_ptr; } - friend bool operator<=(poly_span_iterator left, poly_span_iterator right) noexcept { return left.m_ptr <= right.m_ptr; } - - poly_span_iterator& operator++() noexcept { - fromCharPtr(toCharPtr() + m_element_size); - return *this; - } - - poly_span_iterator& operator--() noexcept { - fromCharPtr(toCharPtr() - m_element_size); - return *this; - } - - poly_span_iterator& operator+=(ptrdiff_t n) noexcept { - fromCharPtr(toCharPtr() + n * m_element_size); - return *this; - } - - friend ptrdiff_t operator-(poly_span_iterator left, poly_span_iterator right) noexcept { - return (left.toCharPtr() - right.toCharPtr()) / left.m_element_size; - } - - poly_span_iterator operator+(ptrdiff_t n) const noexcept { - poly_span_iterator tmp = *this; - tmp += n; - return tmp; - } - - poly_span_iterator operator++(int) noexcept { - poly_span_iterator tmp = *this; - ++(*this); - return tmp; - } - - poly_span_iterator operator--(int) noexcept { - poly_span_iterator tmp = *this; - --(*this); - return tmp; - } - - poly_span_iterator operator-(ptrdiff_t n) const noexcept { return *this + (-n); } - - poly_span_iterator& operator-=(ptrdiff_t n) noexcept { return *this += -n; } - - Type& operator[](ptrdiff_t n) const noexcept { return *(*this + n); } - - friend poly_span_iterator operator+(ptrdiff_t n, poly_span_iterator other) noexcept { - return other + n; - } - -private: - template - friend class poly_span_iterator; - - using VoidPtr = TransferConst*; - using CharPtr = TransferConst*; - - CharPtr toCharPtr() const noexcept { return static_cast(static_cast(m_ptr)); } - void fromCharPtr(CharPtr ptr) noexcept { m_ptr = static_cast(static_cast(ptr)); } - - Type* m_ptr; - size_t m_element_size; -}; } -} diff --git a/test/sanity-check/c++03/CMakeLists.txt b/test/sanity-check/c++03/CMakeLists.txt index d3d27650..8ee27f68 100644 --- a/test/sanity-check/c++03/CMakeLists.txt +++ b/test/sanity-check/c++03/CMakeLists.txt @@ -213,6 +213,7 @@ target_sources(t98 PRIVATE etl_profile.h ../pearson.h.t.cpp ../permutations.h.t.cpp ../placement_new.h.t.cpp + ../poly_span.h.t.cpp ../platform.h.t.cpp ../pool.h.t.cpp ../power.h.t.cpp diff --git a/test/sanity-check/c++11/CMakeLists.txt b/test/sanity-check/c++11/CMakeLists.txt index b8517ee4..8f99d08d 100644 --- a/test/sanity-check/c++11/CMakeLists.txt +++ b/test/sanity-check/c++11/CMakeLists.txt @@ -214,6 +214,7 @@ target_sources(t11 PRIVATE etl_profile.h ../permutations.h.t.cpp ../placement_new.h.t.cpp ../platform.h.t.cpp + ../poly_span.h.t.cpp ../pool.h.t.cpp ../power.h.t.cpp ../priority_queue.h.t.cpp diff --git a/test/sanity-check/c++14/CMakeLists.txt b/test/sanity-check/c++14/CMakeLists.txt index d2072f16..67310a5e 100644 --- a/test/sanity-check/c++14/CMakeLists.txt +++ b/test/sanity-check/c++14/CMakeLists.txt @@ -214,6 +214,7 @@ target_sources(t14 PRIVATE etl_profile.h ../permutations.h.t.cpp ../placement_new.h.t.cpp ../platform.h.t.cpp + ../poly_span.h.t.cpp ../pool.h.t.cpp ../power.h.t.cpp ../priority_queue.h.t.cpp diff --git a/test/sanity-check/c++17/CMakeLists.txt b/test/sanity-check/c++17/CMakeLists.txt index fc5efda8..e1d4da12 100644 --- a/test/sanity-check/c++17/CMakeLists.txt +++ b/test/sanity-check/c++17/CMakeLists.txt @@ -214,6 +214,7 @@ target_sources(t17 PRIVATE etl_profile.h ../permutations.h.t.cpp ../placement_new.h.t.cpp ../platform.h.t.cpp + ../poly_span.h.t.cpp ../pool.h.t.cpp ../power.h.t.cpp ../priority_queue.h.t.cpp diff --git a/test/sanity-check/poly_span.h.t.cpp b/test/sanity-check/poly_span.h.t.cpp new file mode 100644 index 00000000..3aad9e73 --- /dev/null +++ b/test/sanity-check/poly_span.h.t.cpp @@ -0,0 +1,29 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2021 Bo Rydberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include diff --git a/test/test_poly_span.cpp b/test/test_poly_span.cpp new file mode 100644 index 00000000..8d695088 --- /dev/null +++ b/test/test_poly_span.cpp @@ -0,0 +1,365 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2020 John Wellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "unit_test_framework.h" + +#include "etl/poly_span.h" + +#include +#include +#include +#include +#include + +namespace +{ + //*************************************************************************** + struct Base + { + virtual int value() const = 0; + + virtual bool operator==(const Base& o) const { return value() == o.value(); } + }; + + //*************************************************************************** + struct Derived : Base + { + Derived(int v) : m_value(v) {} + + int value() const override { return m_value; } + + int m_value; + }; + + //*************************************************************************** + struct AnotherBase + { + virtual double another_value() const { return v; }; + + double v = 0; + }; + + //*************************************************************************** + struct MultiDerived : AnotherBase, Base + { + MultiDerived(int v) : m_value(v) {} + + int value() const override { return m_value; } + + int m_value; + }; + + //*************************************************************************** + template + bool equal(const TSpan& span, TSource& source) + { + auto sb = std::begin(source); + return (span.size() == (std::end(source) - sb)) && std::equal(span.begin(), span.end(), sb); + } + + SUITE(test_poly_span) + { + //************************************************************************* + TEST(test_poly_span_defaultConstruct) + { + etl::poly_span s{}; + CHECK(s.empty()); + CHECK_EQUAL(0, s.size()); + CHECK(s.begin() == s.end()); + } + + //************************************************************************* + TEST(test_poly_span_constructFromVec) + { + std::vector v = { 1, 2, 3 }; + + etl::poly_span s = v; + CHECK_EQUAL(s[0], v[0]); + CHECK_EQUAL(s[1], v[1]); + CHECK_EQUAL(s[2], v[2]); + CHECK_EQUAL(v.size(), s.size()); + CHECK_EQUAL(sizeof(v[0]), s.element_size()); + CHECK(!s.empty()); + } + + //************************************************************************* + TEST(test_poly_span_constructFromVecRespectConst) + { + std::vector v; + const auto& cv = v; + etl::poly_span s1 = v; + etl::poly_span{v}; + etl::poly_span s2 = cv; + etl::poly_span{cv}; + etl::poly_span s3 = v; + etl::poly_span{v}; + // The line below shouldn't work if uncommented + // etl::poly_span{cv}; + } + + //************************************************************************* + TEST(test_poly_span_constructFromCArray) + { + int a[] = { 1, 2, 3 }; + etl::poly_span s = a; + CHECK_EQUAL(s[0], a[0]); + CHECK_EQUAL(s[1], a[1]); + CHECK_EQUAL(s[2], a[2]); + CHECK_EQUAL(3, s.size()); + CHECK_EQUAL(sizeof(a[0]), s.element_size()); + } + + //************************************************************************* + TEST(test_poly_span_constructFromCArrayRespectConst) + { + Derived ar[2] = { 1, 2 }; + const auto& car = ar; + etl::poly_span s1 = ar; + etl::poly_span{ar}; + etl::poly_span s2 = car; + etl::poly_span{car}; + etl::poly_span s3 = ar; + etl::poly_span{ar}; + // The line below shouldn't work if uncommented + // etl::poly_span{car}; + } + + //************************************************************************* + TEST(test_poly_span_constructFromArray) + { + std::array a = { 1, 2, 3 }; + + etl::poly_span s = a; + CHECK_EQUAL(s[0], a[0]); + CHECK_EQUAL(s[1], a[1]); + CHECK_EQUAL(s[2], a[2]); + } + + //************************************************************************* + TEST(test_poly_span_constructFromPolySpan) + { + std::array a = { 1, 2, 3 }; + etl::poly_span s1 = a; + etl::poly_span s2 = s1; + etl::poly_span{s2}; + CHECK(equal(s1, s2)); + } + + //************************************************************************* + TEST(test_poly_span_constructFromIterator) + { + std::vector v = { 1, 2, 3, 4 }; + + etl::poly_span s1{ v.begin() + 1, v.end() }; + CHECK_EQUAL(v.size() - 1, s1.size()); + CHECK_EQUAL(s1[0], v[1]); + CHECK_EQUAL(s1[1], v[2]); + CHECK_EQUAL(s1[2], v[3]); + + etl::poly_span s2{ v.data() + 1, v.data() + v.size() }; + CHECK(equal(s1, s2)); + + etl::poly_span s3{ v.cbegin(), v.size() }; + CHECK(equal(s3, v)); + } + + //************************************************************************* + TEST(test_poly_span_constructFromInitializerList) + { + const int data[] = { 1, 2, 3, 4, 5 }; + etl::poly_span s(data); + + CHECK_EQUAL(5, s.size()); + + for (int i = 0; i < 5; ++i) + { + //int x = s[i]; + //CHECK_EQUAL(i + 1, s[i]); + } + } + + //************************************************************************* + TEST(test_poly_span_DerivedToBase) + { + std::vector v1 = { 0, 1, 2 }; + + Derived a1[] = { 0, 1, 2 }; + CHECK(sizeof(Derived) > sizeof(Base)); + + etl::poly_span s1 = v1; + CHECK_EQUAL(sizeof(Derived), s1.element_size()); + etl::poly_span s2 = a1; + + auto it1 = s1.begin(); + auto it2 = s2.begin(); + + for (int i = 0; i < 3; ++i, ++it1, ++it2) + { + CHECK_EQUAL(i, s1[i].value()); + CHECK_EQUAL(i, s2[i].value()); + CHECK_EQUAL(i, it1->value()); + CHECK_EQUAL(i, it2->value()); + } + } + + //************************************************************************* + TEST(test_poly_span_MultiDerivedToBase) + { + std::vector v1 = { 0, 1, 2 }; + + etl::poly_span s1 = v1; + CHECK(static_cast(v1.data()) != static_cast(&s1.front())); + CHECK(equal(s1, v1)); + } + + //************************************************************************* + TEST(test_poly_span_typeProperties) + { + CHECK(std::is_trivial::iterator>::value); + CHECK(std::is_trivial::iterator>::value); + CHECK(std::is_trivial::const_iterator>::value); + CHECK(bool(std::is_base_of::iterator::iterator_category>::value)); + CHECK(std::is_trivially_copy_constructible>::value); + CHECK(std::is_trivially_copy_assignable>::value); + } + + //************************************************************************* + //TEST(test_poly_span_iterators) + //{ + // std::vector v = { 1, 2, 3 }; + // etl::poly_span s = v; + // using iterator = etl::poly_span::iterator; + // + // CHECK_EQUAL(s.end() - s.begin(), s.size()); + // CHECK_EQUAL(s.rend() - s.rbegin(), s.size()); + // + // iterator b = s.begin(); + // CHECK(b + s.size() == s.end()); + // CHECK_EQUAL(b[0], 1); + // CHECK(b < s.end()); + // CHECK(b != s.end()); + // CHECK(b + 2 > s.end() - 1); + + // etl::poly_span::const_iterator bc = b; + // CHECK(bc <= s.end()); + // CHECK_EQUAL(s.back(), *s.crbegin()); + // CHECK_EQUAL(s.front(), s.crend()[-1]); + // + // bc += 1; + // bc -= 1; + // CHECK(bc == s.cbegin()); + // CHECK(std::is_sorted(s.cbegin(), s.cend())); + //} + + //************************************************************************* + TEST(test_poly_span_randomAccessIteratorUse) + { + int data[] = { 25, 4, 3, -2, 1 }; + etl::poly_span s = data; + + std::sort(s.begin(), s.end()); + CHECK(std::is_sorted(std::begin(data), std::end(data))); + } + + //************************************************************************* + TEST(test_poly_span_subspan) + { + const int data[] = { 1, 2, 3, 4, 5 }; + etl::poly_span s(data); + + auto s1 = s.subspan(1, 2); + CHECK_EQUAL(2, s1.size()); + CHECK_EQUAL(s[1], s1[0]); + CHECK_EQUAL(s[2], s1[1]); + + auto s2 = s.subspan(2); + CHECK_EQUAL(3, s2.size()); + CHECK_EQUAL(s[2], s2[0]); + CHECK_EQUAL(s[4], s2[2]); + + auto s3 = s.subspan(0, 10); + CHECK_EQUAL(s.size(), s3.size()); + CHECK_THROW(s.subspan(6, 1), etl::poly_span_out_of_range); + } + + //************************************************************************* + TEST(test_poly_span_first) + { + const int data[] = { 1, 2, 3, 4, 5 }; + etl::poly_span s(data); + + CHECK_THROW(s.first(6), etl::poly_span_out_of_range); + CHECK_EQUAL(s.first(5).size(), 5); + CHECK(s.first(0).empty()); + + auto s1 = s.first(3); + CHECK_EQUAL(3, s1.size()); + CHECK_EQUAL(1, s1[0]); + CHECK_EQUAL(3, s1[2]); + } + + //************************************************************************* + TEST(test_poly_span_last) + { + const int data[] = { 1, 2, 3, 4, 5 }; + etl::poly_span s(data); + + CHECK_THROW(s.last(6), etl::poly_span_out_of_range); + CHECK_EQUAL(s.last(5).size(), 5); + + auto s2 = s.last(2); + CHECK_EQUAL(2, s2.size()); + CHECK_EQUAL(4, s2[0]); + CHECK_EQUAL(5, s2[1]); + } + + //************************************************************************* + TEST(test_poly_span_equal_view) + { + std::vector v = { 1.0, 2.0, 3.0 }; + etl::poly_span s1 = v; + etl::poly_span cs2 = v; + + CHECK(!etl::poly_span{}.equal_view(s1)); + CHECK(s1.equal_view(cs2)); + CHECK(s1.last(2).equal_view(cs2.subspan(1))); + } + + //************************************************************************* + TEST(test_poly_span_at) + { + const int data[] = { 1, 2, 3 }; + etl::poly_span s(data); + + CHECK_EQUAL(s.at(0), 1); + CHECK_EQUAL(s.at(2), 3); + CHECK_THROW(s.at(3), etl::poly_span_out_of_range); + CHECK_THROW(s.at(-1), etl::poly_span_out_of_range); + } + } +} diff --git a/test/vs2019/etl.vcxproj b/test/vs2019/etl.vcxproj index 7710cc45..eb377325 100644 --- a/test/vs2019/etl.vcxproj +++ b/test/vs2019/etl.vcxproj @@ -2411,6 +2411,7 @@ + @@ -5312,6 +5313,7 @@ true true + true true @@ -10973,6 +10975,7 @@ + diff --git a/test/vs2019/etl.vcxproj.filters b/test/vs2019/etl.vcxproj.filters index 6aa5aac4..82a0e6dc 100644 --- a/test/vs2019/etl.vcxproj.filters +++ b/test/vs2019/etl.vcxproj.filters @@ -1281,6 +1281,9 @@ ETL\Messaging\Generators + + ETL\Containers + @@ -3260,6 +3263,12 @@ Tests\Binary + + Tests\Sanity Checks\Source + + + Tests\Containers +