etl::poly_span without etl::dynamic_extent

This commit is contained in:
John Wellbelove 2022-06-30 15:55:02 +01:00
parent 26d0dd9859
commit 9dca59bf40
10 changed files with 996 additions and 274 deletions

View File

@ -1,208 +1,641 @@
#pragma once
///\file
#include "span/detail/poly_span_utils.h"
/******************************************************************************
The MIT License(MIT)
#include <initializer_list>
#include <stdexcept>
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 <typename Type>
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 <typename TType>
using HasSize = etl::is_integral<decltype(etl::declval<TType&>().size())>;
//***************************************************************************
template <typename TType>
using DataType = decltype(etl::declval<TType&>().data());
//***************************************************************************
template <typename TType>
using HasData = etl::is_pointer<DataType<TType>>;
//***************************************************************************
template <typename TFrom, typename TType>
using HasConvertibleData = etl::is_convertible<DataType<TFrom>, TType*>;
//***************************************************************************
template <typename TFrom, typename TTo>
using TransferConst = typename etl::conditional<etl::is_const<TFrom>::value,
typename etl::add_const<TTo>::type,
TTo>::type;
//***************************************************************************
template <typename TType>
class poly_span_iterator;
//***************************************************************************
template <typename TIterator>
struct IsPolySpanItImpl : etl::false_type {};
//***************************************************************************
template <typename TType>
struct IsPolySpanItImpl<poly_span_iterator<TType>> : etl::true_type {};
//***************************************************************************
template <typename TIterator>
using IsPolySpanIterator = IsPolySpanItImpl<TIterator>;
//***************************************************************************
/// Iterator implementation
//***************************************************************************
template <typename TType>
class poly_span_iterator : public etl::iterator<ETL_OR_STD::random_access_iterator_tag, TType>
{
private:
template <typename TOther>
using EnableIfConversionToConst = etl::enable_if_t<etl::is_const<TType>::value && etl::is_same<TType, const TOther>::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 <typename TMutableType, typename = EnableIfConversionToConst<TMutableType>>
poly_span_iterator(const poly_span_iterator<TMutableType>& 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 <typename>
friend class poly_span_iterator;
using void_ptr_t = TransferConst<TType, void>*;
using char_ptr_t = TransferConst<TType, char>*;
//***************************************************************************
char_ptr_t to_char_ptr() const noexcept
{
return static_cast<char_ptr_t>(static_cast<void_ptr_t>(m_ptr));
}
//***************************************************************************
void from_char_ptr(char_ptr_t ptr) noexcept
{
m_ptr = static_cast<TType*>(static_cast<void_ptr_t>(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 <typename TType>
class poly_span
{
private:
//***************************************************************************
template <typename Object>
using EnableIfConvertibleFromObject = detail::enable_if_t<
detail::HasSize<Object>::value && detail::HasData<Object>::value &&
detail::HasConvertibleData<Object, Type>::value>;
using EnableIfConvertibleFromObject = etl::enable_if_t<private_poly_span::HasSize<Object>::value &&
private_poly_span::HasData<Object>::value &&
private_poly_span::HasConvertibleData<Object, TType>::value>;
//***************************************************************************
template <typename OtherType>
using EnableIfConvertibleType = detail::enable_if_t<
std::is_convertible<OtherType*, Type*>::value>;
using EnableIfTConvertibleType = etl::enable_if_t<etl::is_convertible<OtherType*, TType*>::value>;
//***************************************************************************
template <typename Iterator>
using IsContiguousIterator = std::integral_constant<bool,
std::is_base_of<std::random_access_iterator_tag,
typename std::iterator_traits<Iterator>::iterator_category>::value &&
!detail::IsPolySpanIterator<Iterator>::value>;
using IsTContiguousIterator = etl::integral_constant<bool,
etl::is_base_of<ETL_OR_STD::random_access_iterator_tag, typename ETL_OR_STD::iterator_traits<Iterator>::iterator_category>::value// &&
/*!private_poly_span::IsPolySpanIterator<Iterator>::value*/>;
public:
using element_type = Type;
using value_type = typename std::remove_cv<Type>::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<Type>;
using const_iterator = detail::poly_span_iterator<const Type>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
public:
using element_type = TType;
using value_type = typename etl::remove_cv<TType>::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<TType>;
using const_iterator = private_poly_span::poly_span_iterator<const TType>;
using reverse_iterator = etl::reverse_iterator<iterator>;
using const_reverse_iterator = etl::reverse_iterator<const_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 <typename ConvertibleType>
poly_span(const poly_span<ConvertibleType>& 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 <typename ContiguousIterator>
poly_span(ContiguousIterator begin, ContiguousIterator end) noexcept
: poly_span(&*begin, end - begin, nullptr) {
static_assert(IsContiguousIterator<ContiguousIterator>::value, "Wrong iterator");
//***************************************************************************
template <typename TConvertibleType>
poly_span(const poly_span<TConvertibleType>& 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 <typename ContiguousIterator>
poly_span(ContiguousIterator begin, size_t size) noexcept
: poly_span(&*begin, size, nullptr) {
static_assert(IsContiguousIterator<ContiguousIterator>::value, "Wrong iterator");
//***************************************************************************
/// Construct from a range of iterators or pointers.
/// Iterators must Contiguous.
//***************************************************************************
template <typename TContiguousIterator>
poly_span(TContiguousIterator begin, TContiguousIterator end) noexcept
: poly_span(&*begin, end - begin, ETL_NULLPTR)
{
static_assert(IsTContiguousIterator<TContiguousIterator>::value, "Not a contiguous iterator");
}
//***************************************************************************
/// Construct from a range defined as iterator/pointer to begin and size.
/// Iterators must Contiguous.
//***************************************************************************
template <typename TContiguousIterator>
poly_span(TContiguousIterator begin, size_t size) noexcept
: poly_span(&*begin, size, ETL_NULLPTR)
{
static_assert(IsTContiguousIterator<TContiguousIterator>::value, "Not a contiguous iterator");
}
//***************************************************************************
/// Construct from a C-style array.
template <typename ConvertibleType, size_t N>
poly_span(ConvertibleType(&array)[N]) noexcept
: poly_span(array, N, nullptr) {}
//***************************************************************************
template <typename TConvertibleType, size_t N>
poly_span(TConvertibleType(&array)[N]) noexcept
: poly_span(array, N, ETL_NULLPTR)
{
}
/// Construct from initializer_list.
template <typename ConvertibleType,
typename = EnableIfConvertibleType<const ConvertibleType>>
poly_span(std::initializer_list<ConvertibleType> 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 <typename TBufferLikeObj, typename = EnableIfConvertibleFromObject<TBufferLikeObj>>
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 <typename BufferLikeObj,
typename = EnableIfConvertibleFromObject<BufferLikeObj>>
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<Type>!
*/
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<const Type>& 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<TType>!
//***************************************************************************
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<const TType>& 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 <typename ConvertibleType>
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 <typename TConvertibleType>
poly_span(TConvertibleType* dataPtr, size_t size, std::nullptr_t) noexcept
: m_dataPtr(dataPtr)
, m_size(size)
, m_element_size(sizeof(TConvertibleType))
{
}
#else
//***************************************************************************
template <typename TConvertibleType>
poly_span(TConvertibleType* dataPtr, size_t size, void*) noexcept
: m_dataPtr(dataPtr)
, m_size(size)
, m_element_size(sizeof(TConvertibleType))
{
}
#endif
template <typename>
friend class poly_span;
using VoidPtr = detail::TransferConst<Type, void>*;
using CharPtr = detail::TransferConst<Type, char>*;
using void_ptr_t = private_poly_span::TransferConst<TType, void>*;
using char_ptr_t = private_poly_span::TransferConst<TType, char>*;
Type* elementAt(size_t ind) const noexcept {
const auto base = static_cast<CharPtr>(static_cast<VoidPtr>(m_dataPtr));
return static_cast<Type*>(static_cast<VoidPtr>(base + ind * m_element_size));
//***************************************************************************
TType* element_at(size_t ind) const noexcept
{
const auto base = static_cast<char_ptr_t>(static_cast<void_ptr_t>(m_dataPtr));
return static_cast<TType*>(static_cast<void_ptr_t>(base + (ind * m_element_size)));
}
//***************************************************************************
template <typename Value>
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

View File

@ -4,131 +4,10 @@
#include <iterator>
#include <type_traits>
namespace span {
namespace detail {
namespace private_poly_span
{
template <bool Condition, typename Result = void>
using enable_if_t = typename std::enable_if<Condition, Result>::type;
template <typename Type>
using HasSize = std::is_integral<decltype(std::declval<Type&>().size())>;
template <typename Type>
using DataType = decltype(std::declval<Type&>().data());
template <typename Type>
using HasData = std::is_pointer<DataType<Type>>;
template <typename From, typename Type>
using HasConvertibleData = std::is_convertible<DataType<From>, Type*>;
template <typename FromType, typename ToType>
using TransferConst = typename std::conditional<std::is_const<FromType>::value,
typename std::add_const<ToType>::type,
ToType>::type;
template <typename Type>
class poly_span_iterator;
template <typename Iter>
struct IsPolySpanItImpl : std::false_type {};
template <typename Type>
struct IsPolySpanItImpl<poly_span_iterator<Type>> : std::true_type {};
template <typename Iter>
using IsPolySpanIterator = IsPolySpanItImpl<Iter>;
template <typename Type>
class poly_span_iterator : public std::iterator<std::random_access_iterator_tag, Type> {
private:
template <typename Other>
using EnableIfConversionToConst = enable_if_t<
std::is_const<Type>::value && std::is_same<Type, const Other>::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 <typename MutableType,
typename = EnableIfConversionToConst<MutableType>>
poly_span_iterator(const poly_span_iterator<MutableType>& 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 <typename>
friend class poly_span_iterator;
using VoidPtr = TransferConst<Type, void>*;
using CharPtr = TransferConst<Type, char>*;
CharPtr toCharPtr() const noexcept { return static_cast<CharPtr>(static_cast<VoidPtr>(m_ptr)); }
void fromCharPtr(CharPtr ptr) noexcept { m_ptr = static_cast<Type*>(static_cast<VoidPtr>(ptr)); }
Type* m_ptr;
size_t m_element_size;
};
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 <etl/poly_span.h>

365
test/test_poly_span.cpp Normal file
View File

@ -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 <array>
#include <vector>
#include <algorithm>
#include <iterator>
#include <vector>
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 <typename TSpan, typename TSource>
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<Base> s{};
CHECK(s.empty());
CHECK_EQUAL(0, s.size());
CHECK(s.begin() == s.end());
}
//*************************************************************************
TEST(test_poly_span_constructFromVec)
{
std::vector<int> v = { 1, 2, 3 };
etl::poly_span<int> 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<Derived> v;
const auto& cv = v;
etl::poly_span<const Base> s1 = v;
etl::poly_span<const Base>{v};
etl::poly_span<const Base> s2 = cv;
etl::poly_span<const Base>{cv};
etl::poly_span<Derived> s3 = v;
etl::poly_span<Base>{v};
// The line below shouldn't work if uncommented
// etl::poly_span<int>{cv};
}
//*************************************************************************
TEST(test_poly_span_constructFromCArray)
{
int a[] = { 1, 2, 3 };
etl::poly_span<const int> 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<const Base> s1 = ar;
etl::poly_span<const Base>{ar};
etl::poly_span<const Derived> s2 = car;
etl::poly_span<const Derived>{car};
etl::poly_span<Base> s3 = ar;
etl::poly_span<Base>{ar};
// The line below shouldn't work if uncommented
// etl::poly_span<int>{car};
}
//*************************************************************************
TEST(test_poly_span_constructFromArray)
{
std::array<int, 3> a = { 1, 2, 3 };
etl::poly_span<int> 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<Derived, 3> a = { 1, 2, 3 };
etl::poly_span<Derived> s1 = a;
etl::poly_span<Base> s2 = s1;
etl::poly_span<const Base>{s2};
CHECK(equal(s1, s2));
}
//*************************************************************************
TEST(test_poly_span_constructFromIterator)
{
std::vector<int> v = { 1, 2, 3, 4 };
etl::poly_span<int> 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<const int> s2{ v.data() + 1, v.data() + v.size() };
CHECK(equal(s1, s2));
etl::poly_span<const int> 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<const int> 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<Derived> v1 = { 0, 1, 2 };
Derived a1[] = { 0, 1, 2 };
CHECK(sizeof(Derived) > sizeof(Base));
etl::poly_span<const Base> s1 = v1;
CHECK_EQUAL(sizeof(Derived), s1.element_size());
etl::poly_span<Base> 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<MultiDerived> v1 = { 0, 1, 2 };
etl::poly_span<Base> s1 = v1;
CHECK(static_cast<void*>(v1.data()) != static_cast<void*>(&s1.front()));
CHECK(equal(s1, v1));
}
//*************************************************************************
TEST(test_poly_span_typeProperties)
{
CHECK(std::is_trivial<etl::poly_span<int>::iterator>::value);
CHECK(std::is_trivial<etl::poly_span<const Base>::iterator>::value);
CHECK(std::is_trivial<etl::poly_span<int>::const_iterator>::value);
CHECK(bool(std::is_base_of<ETL_OR_STD::random_access_iterator_tag, etl::poly_span<int>::iterator::iterator_category>::value));
CHECK(std::is_trivially_copy_constructible<etl::poly_span<int>>::value);
CHECK(std::is_trivially_copy_assignable<etl::poly_span<int>>::value);
}
//*************************************************************************
//TEST(test_poly_span_iterators)
//{
// std::vector<int> v = { 1, 2, 3 };
// etl::poly_span<int> s = v;
// using iterator = etl::poly_span<int>::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<int>::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<int> 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<const int> 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<const int> 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<const int> 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<double> v = { 1.0, 2.0, 3.0 };
etl::poly_span<double> s1 = v;
etl::poly_span<const double> cs2 = v;
CHECK(!etl::poly_span<const double>{}.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<const int> 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);
}
}
}

View File

@ -2411,6 +2411,7 @@
<ClInclude Include="..\..\include\etl\crc8_maxim.h" />
<ClInclude Include="..\..\include\etl\crc8_rohc.h" />
<ClInclude Include="..\..\include\etl\crc8_wcdma.h" />
<ClInclude Include="..\..\include\etl\poly_span.h" />
<ClInclude Include="..\..\include\etl\pseudo_moving_average.h" />
<ClInclude Include="..\..\include\etl\delegate.h" />
<ClInclude Include="..\..\include\etl\delegate_service.h" />
@ -5312,6 +5313,7 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='MSVC Debug - Appveyor|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug64|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\sanity-check\poly_span.h.t.cpp" />
<ClCompile Include="..\sanity-check\pseudo_moving_average.h.t.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug MSVC - No STL - Built-ins|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug MSVC - No STL - Force Built-ins|Win32'">true</ExcludedFromBuild>
@ -10973,6 +10975,7 @@
<ClCompile Include="..\test_crc8_maxim.cpp" />
<ClCompile Include="..\test_crc8_rohc.cpp" />
<ClCompile Include="..\test_crc8_wcdma.cpp" />
<ClCompile Include="..\test_poly_span.cpp" />
<ClCompile Include="..\test_pseudo_moving_average.cpp" />
<ClCompile Include="..\test_delegate.cpp" />
<ClCompile Include="..\test_delegate_cpp03.cpp" />

View File

@ -1281,6 +1281,9 @@
<ClInclude Include="..\..\include\etl\generators\message_router_generator.h">
<Filter>ETL\Messaging\Generators</Filter>
</ClInclude>
<ClInclude Include="..\..\include\etl\poly_span.h">
<Filter>ETL\Containers</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\test_string_char.cpp">
@ -3260,6 +3263,12 @@
<ClCompile Include="..\test_bit_stream_reader_little_endian.cpp">
<Filter>Tests\Binary</Filter>
</ClCompile>
<ClCompile Include="..\sanity-check\poly_span.h.t.cpp">
<Filter>Tests\Sanity Checks\Source</Filter>
</ClCompile>
<ClCompile Include="..\test_poly_span.cpp">
<Filter>Tests\Containers</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="..\..\library.properties">