diff --git a/include/array_view.h b/include/array_view.h index 046cbf8..aa1b4e5 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -73,180 +73,6 @@ namespace details static const size_t max_value = std::is_signed::value ? static_cast::type>(-1) / 2 : static_cast(-1); }; - - template - class coordinate_facade - { - static_assert(std::is_integral::value - && sizeof(ValueType) <= sizeof(size_t), "ValueType must be an integral type!"); - static_assert(Rank > 0, "Rank must be greater than 0!"); - - template - friend class coordinate_facade; - public: - using reference = ValueType&; - using const_reference = const ValueType&; - using value_type = ValueType; - static const size_t rank = Rank; - constexpr coordinate_facade() noexcept - { - static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); - } - constexpr coordinate_facade(const value_type(&values)[rank]) noexcept - { - static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); - for (size_t i = 0; i < rank; ++i) - elems[i] = values[i]; - } - constexpr coordinate_facade(value_type e0) noexcept - { - static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); - static_assert(rank == 1, "This constructor can only be used with rank == 1."); - elems[0] = e0; - } - // Preconditions: il.size() == rank - constexpr coordinate_facade(std::initializer_list il) - { - static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); - fail_fast_assert(il.size() == rank, "The size of the initializer list must match the rank of the array"); - for (size_t i = 0; i < rank; ++i) - { - elems[i] = begin(il)[i]; - } - } - - constexpr coordinate_facade(const coordinate_facade & other) = default; - - template - constexpr coordinate_facade(const coordinate_facade & other) - { - for (size_t i = 0; i < rank; ++i) - { - fail_fast_assert(static_cast(other.elems[i]) <= SizeTypeTraits::max_value); - elems[i] = static_cast(other.elems[i]); - } - } - protected: - coordinate_facade& operator=(const coordinate_facade& rhs) = default; - // Preconditions: component_idx < rank - constexpr reference operator[](size_t component_idx) - { - fail_fast_assert(component_idx < rank, "Component index must be less than rank"); - return elems[component_idx]; - } - // Preconditions: component_idx < rank - constexpr const_reference operator[](size_t component_idx) const - { - fail_fast_assert(component_idx < rank, "Component index must be less than rank"); - return elems[component_idx]; - } - constexpr bool operator==(const ConcreteType& rhs) const noexcept - { - return std::equal(elems, elems + rank, rhs.elems); - } - constexpr bool operator!=(const ConcreteType& rhs) const noexcept - { - return !(to_concrete() == rhs); - } - constexpr ConcreteType operator+() const noexcept - { - return to_concrete(); - } - constexpr ConcreteType operator-() const - { - ConcreteType ret = to_concrete(); - std::transform(ret, ret + rank, ret, std::negate{}); - return ret; - } - constexpr ConcreteType operator+(const ConcreteType& rhs) const - { - ConcreteType ret = to_concrete(); - ret += rhs; - return ret; - } - constexpr ConcreteType operator-(const ConcreteType& rhs) const - { - ConcreteType ret = to_concrete(); - ret -= rhs; - return ret; - } - constexpr ConcreteType& operator+=(const ConcreteType& rhs) - { - for (size_t i = 0; i < rank; ++i) - elems[i] += rhs.elems[i]; - return to_concrete(); - } - constexpr ConcreteType& operator-=(const ConcreteType& rhs) - { - for (size_t i = 0; i < rank; ++i) - elems[i] -= rhs.elems[i]; - return to_concrete(); - } - constexpr ConcreteType& operator++() - { - static_assert(rank == 1, "This operator can only be used with rank == 1."); - ++elems[0]; - return to_concrete(); - } - constexpr ConcreteType operator++(int) - { - static_assert(rank == 1, "This operator can only be used with rank == 1."); - ConcreteType ret = to_concrete(); - ++(*this); - return ret; - } - constexpr ConcreteType& operator--() - { - static_assert(rank == 1, "This operator can only be used with rank == 1."); - --elems[0]; - return to_concrete(); - } - constexpr ConcreteType operator--(int) - { - static_assert(rank == 1, "This operator can only be used with rank == 1."); - ConcreteType ret = to_concrete(); - --(*this); - return ret; - } - constexpr ConcreteType operator*(value_type v) const - { - ConcreteType ret = to_concrete(); - ret *= v; - return ret; - } - constexpr ConcreteType operator/(value_type v) const - { - ConcreteType ret = to_concrete(); - ret /= v; - return ret; - } - friend constexpr ConcreteType operator*(value_type v, const ConcreteType& rhs) - { - return rhs * v; - } - constexpr ConcreteType& operator*=(value_type v) - { - for (size_t i = 0; i < rank; ++i) - elems[i] *= v; - return to_concrete(); - } - constexpr ConcreteType& operator/=(value_type v) - { - for (size_t i = 0; i < rank; ++i) - elems[i] /= v; - return to_concrete(); - } - value_type elems[rank] = {}; - private: - constexpr const ConcreteType& to_concrete() const noexcept - { - return static_cast(*this); - } - constexpr ConcreteType& to_concrete() noexcept - { - return static_cast(*this); - } - }; template class arrow_proxy { @@ -268,47 +94,160 @@ namespace details } template -class index : private details::coordinate_facade, ValueType, Rank> +class index final { - using Base = details::coordinate_facade, ValueType, Rank>; - friend Base; + static_assert(std::is_integral::value, "ValueType must be an integral type!"); + static_assert(Rank > 0, "Rank must be greater than 0!"); + template friend class index; + public: - using Base::rank; - using reference = typename Base::reference; - using const_reference = typename Base::const_reference; - using size_type = typename Base::value_type; - using value_type = typename Base::value_type; - constexpr index() noexcept : Base(){} - constexpr index(const value_type (&values)[rank]) noexcept : Base(values) {} - constexpr index(std::initializer_list il) : Base(il) {} + static const size_t rank = Rank; + using value_type = std::remove_reference_t; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_lvalue_reference_t>; - constexpr index(const index &) = default; - - template - constexpr index(const index &other) : Base(other) + constexpr index(const value_type(&values)[Rank]) noexcept { - } - constexpr static index shift_left(const index& other) noexcept - { - value_type (&arr)[rank] = (value_type(&)[rank])(*(other.elems + 1)); - return index(arr); + std::copy(values, values + Rank, elems); } - using Base::operator[]; - using Base::operator==; - using Base::operator!=; - using Base::operator+; - using Base::operator-; - using Base::operator+=; - using Base::operator-=; - using Base::operator++; - using Base::operator--; - using Base::operator*; - using Base::operator/; - using Base::operator*=; - using Base::operator/=; + // Preconditions: il.size() == rank + constexpr index(std::initializer_list il) noexcept + { + fail_fast_assert(il.size() == Rank, "The size of the initializer list must match the rank of the array"); + std::copy(begin(il), end(il), elems); + } + + constexpr index(const index& other) noexcept = default; + + // copy from index over smaller domain + template + constexpr index(typename std::enable_if_t<(details::SizeTypeTraits::max_value <= details::SizeTypeTraits::max_value), const index>::type& other) noexcept + { + std::copy(other.elems, other.elems + Rank, elems); + } + + // copy from index over larger domain + template + constexpr index(typename std::enable_if_t<(details::SizeTypeTraits::max_value > details::SizeTypeTraits::max_value), const index>::type& other) noexcept + { + for (size_t i = 0; i < Rank; ++i) + { + fail_fast_assert(other.elems[i] <= static_cast(SizeTypeTraits::max_value)); + elems[i] = static_cast(other.elems[i]); + } + } + + constexpr static index shift_left(const index& other) noexcept + { + value_type(&arr)[Rank] = (value_type(&)[Rank])(*(other.elems + 1)); + return index(arr); + } + + constexpr static index zero() noexcept + { + value_type zero[Rank] = {}; + return index(zero); + } + + constexpr index& operator=(const index& rhs) noexcept = default; + + // Preconditions: component_idx < rank + constexpr reference operator[](size_t component_idx) + { + fail_fast_assert(component_idx < Rank, "Component index must be less than rank"); + return elems[component_idx]; + } + + // Preconditions: component_idx < rank + constexpr const_reference operator[](size_t component_idx) const noexcept + { + fail_fast_assert(component_idx < Rank, "Component index must be less than rank"); + return elems[component_idx]; + } + + constexpr bool operator==(const index& rhs) const noexcept + { + return std::equal(elems, elems + rank, rhs.elems); + } + + constexpr bool operator!=(const index& rhs) const noexcept + { + return !(this == rhs); + } + + constexpr index operator+() const noexcept + { + return *this; + } + + constexpr index operator-() const noexcept + { + index ret = *this; + std::transform(ret, ret + rank, ret, std::negate{}); + return ret; + } + + constexpr index operator+(const index& rhs) const noexcept + { + index ret = *this; + ret += rhs; + return ret; + } + + constexpr index operator-(const index& rhs) const noexcept + { + index ret = *this; + ret -= rhs; + return ret; + } + + constexpr index& operator+=(const index& rhs) noexcept + { + std::transform(elems, elems + rank, rhs.elems, elems, std::plus{}); + return *this; + } + + constexpr index& operator-=(const index& rhs) noexcept + { + std::transform(elems, elems + rank, rhs.elems, elems, std::minus{}); + return *this; + } + + constexpr index operator*(value_type v) const noexcept + { + index ret = *this; + ret *= v; + return ret; + } + + constexpr index operator/(value_type v) const noexcept + { + index ret = *this; + ret /= v; + return ret; + } + + friend static constexpr index operator*(value_type v, const index& rhs) noexcept + { + return rhs * v; + } + + constexpr index& operator*=(value_type v) noexcept + { + std::transform(elems, elems + rank, elems, [v](value_type x) { return std::multiplies{}(x, v); }); + return *this; + } + + constexpr index& operator/=(value_type v) noexcept + { + std::transform(elems, elems + rank, elems, [v](value_type x) { return std::divides{}(x, v); }); + return *this; + } +private: + value_type elems[Rank] = {}; }; template @@ -316,51 +255,60 @@ class index<1, ValueType> { template friend class index; + public: static const size_t rank = 1; - using reference = ValueType&; - using const_reference = const ValueType&; - using size_type = ValueType; - using value_type = ValueType; + using value_type = std::remove_reference_t; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_lvalue_reference_t>; - constexpr index() noexcept : value(0) - { - } constexpr index(value_type e0) noexcept : value(e0) - { - } + {} + constexpr index(const value_type(&values)[1]) noexcept : index(values[0]) - { - } + {} + // Preconditions: il.size() == rank - constexpr index(std::initializer_list il) + constexpr index(std::initializer_list il) noexcept { fail_fast_assert(il.size() == rank, "Size of the initializer list must match the rank of the array"); value = begin(il)[0]; } - constexpr index(const index &) = default; + constexpr index(const index &) noexcept = default; template - constexpr index(const index<1, OtherValueType> & other) + constexpr index(typename std::enable_if_t<(details::SizeTypeTraits::max_value <= details::SizeTypeTraits::max_value), const index<1, OtherValueType>>::type& other) noexcept { - fail_fast_assert(other.value <= details::SizeTypeTraits::max_value); value = static_cast(other.value); } - constexpr static index shift_left(const index& other) noexcept + template + constexpr index(typename std::enable_if_t<(details::SizeTypeTraits::max_value > details::SizeTypeTraits::max_value), const index<1, OtherValueType>>::type& other) noexcept + { + fail_fast_assert(other.value <= static_cast(SizeTypeTraits::max_value)); + value = static_cast(other.value); + } + + constexpr static index shift_left(const index<2, value_type>& other) noexcept { return other.elems[1]; } - // Preconditions: component_idx < rank - constexpr reference operator[](size_type component_idx) noexcept + + constexpr static index zero() noexcept + { + return 0; + } + + // Preconditions: component_idx < 1 + constexpr reference operator[](value_type component_idx) noexcept { fail_fast_assert(component_idx == 0, "Component index must be less than rank"); (void)(component_idx); return value; } - // Preconditions: component_idx < rank - constexpr const_reference operator[](size_type component_idx) const noexcept + // Preconditions: component_idx < 1 + constexpr const_reference operator[](value_type component_idx) const noexcept { fail_fast_assert(component_idx == 0, "Component index must be less than rank"); (void)(component_idx); @@ -440,9 +388,9 @@ public: value /= v; return *this; } - friend constexpr index operator*(value_type v, const index& rhs) noexcept + friend static constexpr index operator*(value_type v, const index& rhs) noexcept { - return index(rhs * v); + return{ rhs * v }; } private: value_type value; @@ -855,9 +803,9 @@ public: constexpr index_type index_bounds() const noexcept { - index_type extents; + size_type extents[rank]; m_ranges.serialize(extents); - return extents; + return{ extents }; } template @@ -874,32 +822,28 @@ public: constexpr const_iterator begin() const noexcept { - return const_iterator(*this); + return const_iterator(*this, index_type::zero()); } constexpr const_iterator end() const noexcept { - index_type boundary; - m_ranges.serialize(boundary); return const_iterator(*this, this->index_bounds()); } }; template -class strided_bounds : private details::coordinate_facade, SizeType, Rank> +class strided_bounds { - using Base = details::coordinate_facade, SizeType, Rank>; - friend Base; template friend class strided_bounds; public: - using Base::rank; - using reference = typename Base::reference; - using const_reference = typename Base::const_reference; - using size_type = typename Base::value_type; - using difference_type = typename Base::value_type; - using value_type = typename Base::value_type; + static const size_t rank = Rank; + using reference = typename SizeType&; + using const_reference = typename const SizeType&; + using size_type = typename SizeType; + using difference_type = typename SizeType; + using value_type = typename SizeType; using index_type = index; using iterator = bounds_iterator; using const_iterator = bounds_iterator; @@ -907,57 +851,52 @@ public: static const size_t static_size = dynamic_range; using sliced_type = std::conditional_t, void>; using mapping_type = generalized_mapping_tag; - constexpr strided_bounds(const strided_bounds &) = default; + constexpr strided_bounds(const strided_bounds &) noexcept = default; template - constexpr strided_bounds(const strided_bounds &other) - : Base(other), m_strides(other.strides) - { - } - - constexpr strided_bounds(const index_type &extents, const index_type &strides) - : m_strides(strides) - { - for (size_t i = 0; i < rank; i++) - Base::elems[i] = extents[i]; - } - constexpr strided_bounds(const value_type(&values)[rank], index_type strides) - : Base(values), m_strides(std::move(strides)) - { - } + constexpr strided_bounds(const strided_bounds &other) noexcept + : m_extents(other.extents), m_strides(other.strides) + {} + constexpr strided_bounds(const index_type &extents, const index_type &strides) noexcept + : m_extents(extents), m_strides(strides) + {} constexpr index_type strides() const noexcept - { - return m_strides; + { + return m_strides; } constexpr size_type total_size() const noexcept { size_type ret = 0; for (size_t i = 0; i < rank; ++i) - ret += (Base::elems[i] - 1) * m_strides[i]; + { + ret += (m_extents[i] - 1) * m_strides[i]; + } return ret + 1; } constexpr size_type size() const noexcept { size_type ret = 1; for (size_t i = 0; i < rank; ++i) - ret *= Base::elems[i]; + { + ret *= m_extents[i]; + } return ret; } constexpr bool contains(const index_type& idx) const noexcept { for (size_t i = 0; i < rank; ++i) { - if (idx[i] < 0 || idx[i] >= Base::elems[i]) + if (idx[i] < 0 || idx[i] >= m_extents[i]) return false; } return true; } - constexpr size_type linearize(const index_type & idx) const + constexpr size_type linearize(const index_type & idx) const noexcept { size_type ret = 0; for (size_t i = 0; i < rank; i++) { - fail_fast_assert(idx[i] < Base::elems[i], "index is out of bounds of the array"); + fail_fast_assert(idx[i] < m_extents[i], "index is out of bounds of the array"); ret += idx[i] * m_strides[i]; } return ret; @@ -969,27 +908,28 @@ public: template 1), typename Ret = std::enable_if_t> constexpr sliced_type slice() const { - return{ (value_type(&)[rank - 1])Base::elems[1], sliced_type::index_type::shift_left(m_strides) }; + return{ sliced_type::index_type::shift_left(m_extents), sliced_type::index_type::shift_left(m_strides) }; } template constexpr size_type extent() const noexcept { static_assert(Dim < Rank, "dimension should be less than rank (dimension count starts from 0)"); - return Base::elems[Dim]; + return m_extents[Dim]; } constexpr index_type index_bounds() const noexcept { - return index_type(Base::elems); + return m_extents; } const_iterator begin() const noexcept { - return const_iterator{ *this }; + return const_iterator{ *this, index_type::zero() }; } const_iterator end() const noexcept { return const_iterator{ *this, index_bounds() }; } private: + index_type m_extents; index_type m_strides; }; @@ -1017,9 +957,9 @@ public: using typename Base::difference_type; using typename Base::value_type; using index_type = value_type; - using index_size_type = typename IndexType::size_type; + using index_size_type = typename IndexType::value_type; template - explicit bounds_iterator(const Bounds & bnd, value_type curr = value_type{}) noexcept + explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept : boundary(bnd.index_bounds()) , curr( std::move(curr) ) { @@ -1210,7 +1150,7 @@ public: using typename Base::difference_type; using typename Base::value_type; using index_type = value_type; - using index_size_type = typename index_type::size_type; + using index_size_type = typename index_type::value_type; template explicit bounds_iterator(const Bounds &, value_type curr = value_type{}) noexcept @@ -1327,11 +1267,14 @@ namespace details constexpr std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) noexcept { auto extents = bnd.index_bounds(); - typename Bounds::index_type stride; - stride[Bounds::rank - 1] = 1; - for (size_t i = Bounds::rank - 1; Bounds::rank > 1 && i > 0; --i) - stride[i-1] = stride[i] * extents[i]; - return stride; + Bounds::size_type stride[Bounds::rank]; + + stride[Bounds::rank - 1] = 1; + for (size_t i = Bounds::rank - 1; Bounds::rank > 1 && i > 0; --i) + { + stride[i - 1] = stride[i] * extents[i]; + } + return{ stride }; } template diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp index 918df9e..ec10bbd 100644 --- a/tests/array_view_tests.cpp +++ b/tests/array_view_tests.cpp @@ -552,26 +552,6 @@ SUITE(array_view_tests) CHECK_THROW(av.section(5, 5), fail_fast); } - { - // zero stride - strided_array_view sav{ av, {{4}, {}} }; - CHECK(sav[0] == 0); - CHECK(sav[3] == 0); - CHECK_THROW(sav[4], fail_fast); - } - - { - // zero extent - strided_array_view sav{ av,{ {},{1} } }; - CHECK_THROW(sav[0], fail_fast); - } - - { - // zero extent and stride - strided_array_view sav{ av,{ {},{} } }; - CHECK_THROW(sav[0], fail_fast); - } - { // strided array ctor with matching strided bounds strided_array_view sav{ arr,{ 4, 1 } }; @@ -627,6 +607,9 @@ SUITE(array_view_tests) #ifdef CONFIRM_COMPILATION_ERRORS { + strided_array_view sav{ av,{ { 4 },{} } }; + strided_array_view sav{ av,{ {},{ 1 } } }; + strided_array_view sav{ av,{ {},{} } }; strided_array_view sav0{ av.data(), { 3, 2 } }; strided_array_view sav1{ arr, { 1 } }; strided_array_view sav2{ arr, { 1,1,1 } };