diff --git a/README.md b/README.md index 7d96dec..c687673 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# GSL: Guidelines Support Library [![Build Status](https://travis-ci.org/Microsoft/GSL.svg?branch=master)](https://travis-ci.org/Microsoft/GSL) +# GSL: Guidelines Support Library [![Build Status](https://travis-ci.org/Microsoft/GSL.svg?branch=master)](https://travis-ci.org/Microsoft/GSL) [![Build status](https://ci.appveyor.com/api/projects/status/github/Microsoft/GSL?svg=true)](https://ci.appveyor.com/project/neilmacintosh/GSL) The Guidelines Support Library (GSL) contains functions and types that are suggested for use by the [C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines) maintained by the [Standard C++ Foundation](https://isocpp.org). diff --git a/include/array_view.h b/include/array_view.h index 4d5d46a..6d4fde2 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -30,6 +30,7 @@ #include #include #include +#include #include "fail_fast.h" #ifdef _MSC_VER @@ -68,24 +69,18 @@ namespace gsl { */ namespace details { - template - class arrow_proxy + template + struct SizeTypeTraits { - public: - explicit arrow_proxy(T t) - : val(t) - {} - const T operator*() const noexcept - { - return val; - } - const T* operator->() const noexcept - { - return &val; - } - private: - T val; + static const SizeType max_value = std::numeric_limits::max(); }; + + + template + class are_integral : public std::integral_constant {}; + + template + class are_integral : public std::integral_constant::value && are_integral::value> {}; } template @@ -111,14 +106,12 @@ public: std::copy(values, values + Rank, elems); } - // 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); - } + template::value, typename Dummy = std::enable_if_t> + constexpr index(Ts... ds) noexcept : elems{ static_cast(ds)... } + {} constexpr index(const index& other) noexcept = default; + constexpr index& operator=(const index& rhs) noexcept = default; // Preconditions: component_idx < rank @@ -218,144 +211,46 @@ private: value_type elems[Rank] = {}; }; -template<> -class index<1> +#ifndef _MSC_VER + +struct static_bounds_dynamic_range_t { -public: - static const size_t rank = 1; - using size_type = std::ptrdiff_t; - using value_type = std::ptrdiff_t; - using reference = std::add_lvalue_reference_t; - using const_reference = const std::ptrdiff_t&;//std::add_const_t>; - - template - friend class index; - - constexpr index() noexcept : value(0) - {} - - constexpr index(value_type e) noexcept : value(e) - {} - - constexpr index(const value_type(&values)[1]) noexcept : index(values[0]) - {} - - constexpr index(const index &) noexcept = default; - - // Preconditions: component_idx < rank - constexpr reference operator[](size_type component_idx) noexcept + template ::value>> + constexpr operator T() const noexcept { - fail_fast_assert(component_idx == 0, "Component index must be less than rank"); - return value; + return static_cast(-1); } - // Preconditions: component_idx < rank - constexpr const_reference operator[](size_type component_idx) const noexcept + template ::value>> + constexpr bool operator ==(T other) const noexcept { - fail_fast_assert(component_idx == 0, "Component index must be less than rank"); - return value; + return static_cast(-1) == other; } - constexpr bool operator==(const index& rhs) const noexcept + template ::value>> + constexpr bool operator !=(T other) const noexcept { - return value == rhs.value; + return static_cast(-1) != other; } - constexpr bool operator!=(const index& rhs) const noexcept - { - return !(*this == rhs); - } - - constexpr index operator+() const noexcept - { - return *this; - } - - constexpr index operator-() const noexcept - { - return index(-value); - } - - constexpr index operator+(const index& rhs) const noexcept - { - return index(value + rhs.value); - } - - constexpr index operator-(const index& rhs) const noexcept - { - return index(value - rhs.value); - } - - constexpr index& operator+=(const index& rhs) noexcept - { - value += rhs.value; - return *this; - } - - constexpr index& operator-=(const index& rhs) noexcept - { - value -= rhs.value; - return *this; - } - - constexpr index& operator++() noexcept - { - ++value; - return *this; - } - - constexpr index operator++(int) noexcept - { - index ret = *this; - ++(*this); - return ret; - } - - constexpr index& operator--() noexcept - { - --value; - return *this; - } - - constexpr index operator--(int) noexcept - { - index ret = *this; - --(*this); - return ret; - } - - constexpr index operator*(value_type v) const noexcept - { - return index(value * v); - } - - constexpr index operator/(value_type v) const noexcept - { - return index(value / v); - } - - constexpr index& operator*=(value_type v) noexcept - { - value *= v; - return *this; - } - - constexpr index& operator/=(value_type v) noexcept - { - value /= v; - return *this; - } - - friend constexpr index operator*(value_type v, const index& rhs) noexcept - { - return{ rhs * v }; - } - -private: - value_type value; }; +template ::value>> +constexpr bool operator ==(T left, static_bounds_dynamic_range_t right) noexcept +{ + return right == left; +} + +template ::value>> +constexpr bool operator !=(T left, static_bounds_dynamic_range_t right) noexcept +{ + return right != left; +} + +constexpr static_bounds_dynamic_range_t dynamic_range{}; +#else const std::ptrdiff_t dynamic_range = -1; +#endif struct generalized_mapping_tag {}; struct contiguous_mapping_tag : generalized_mapping_tag {}; @@ -652,22 +547,23 @@ public: using size_type = std::ptrdiff_t; using index_type = index; - using iterator = bounds_iterator; - using const_iterator = bounds_iterator; - using difference_type = std::ptrdiff_t; + using const_index_type = std::add_const_t; + using iterator = bounds_iterator; + using const_iterator = bounds_iterator; + using difference_type = std::ptrdiff_t; using sliced_type = static_bounds; using mapping_type = contiguous_mapping_tag; -public: + constexpr static_bounds(const static_bounds&) = default; template , details::BoundsRanges >::value>> constexpr static_bounds(const static_bounds& other) : m_ranges(other.m_ranges) {} - + constexpr static_bounds(std::initializer_list il) : m_ranges(il.begin()) { - fail_fast_assert(MyRanges::DynamicNum == il.size(), "Size of the initializer list must match the rank of the array"); + fail_fast_assert((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) || MyRanges::DynamicNum == il.size(), "Size of the initializer list must match the rank of the array"); fail_fast_assert(m_ranges.totalSize() <= PTRDIFF_MAX, "Size of the range is larger than the max element of the size type"); } @@ -742,7 +638,7 @@ public: constexpr const_iterator begin() const noexcept { - return const_iterator(*this); + return const_iterator(*this, index_type{}); } constexpr const_iterator end() const noexcept @@ -765,9 +661,10 @@ public: using size_type = value_type; using difference_type = value_type; using index_type = index; - using iterator = bounds_iterator; - using const_iterator = bounds_iterator; - static const size_t dynamic_rank = rank; + using const_index_type = std::add_const_t; + using iterator = bounds_iterator; + using const_iterator = bounds_iterator; + static const value_type dynamic_rank = rank; static const value_type static_size = dynamic_range; using sliced_type = std::conditional_t, void>; using mapping_type = generalized_mapping_tag; @@ -850,13 +747,12 @@ public: { return m_extents; } - - const_iterator begin() const noexcept + constexpr const_iterator begin() const noexcept { - return const_iterator{ *this }; + return const_iterator{ *this, index_type{} }; } - const_iterator end() const noexcept + constexpr const_iterator end() const noexcept { return const_iterator{ *this, index_bounds() }; } @@ -874,15 +770,11 @@ template struct is_bounds> : std::integral_constant {}; template -class bounds_iterator - : public std::iterator, - const IndexType> +class bounds_iterator: public std::iterator { private: - using Base = std::iterator , const IndexType>; + using Base = std::iterator ; + public: static const size_t rank = IndexType::rank; using typename Base::reference; @@ -892,79 +784,88 @@ public: using index_type = value_type; using index_size_type = typename IndexType::value_type; template - explicit bounds_iterator(const Bounds& bnd, value_type curr = value_type{}) noexcept - : boundary(bnd.index_bounds()) - , curr(std::move(curr)) + explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept + : boundary(bnd.index_bounds()), curr(std::move(curr)) { static_assert(is_bounds::value, "Bounds type must be provided"); } - reference operator*() const noexcept + + constexpr reference operator*() const noexcept { return curr; } - pointer operator->() const noexcept + + constexpr pointer operator->() const noexcept { - return details::arrow_proxy{ curr }; + return &curr; } - bounds_iterator& operator++() noexcept + + constexpr bounds_iterator& operator++() noexcept { for (size_t i = rank; i-- > 0;) { - if (++curr[i] < boundary[i]) + if (curr[i] < boundary[i] - 1) { + curr[i]++; return *this; } - else - { - curr[i] = 0; - } + curr[i] = 0; } // If we're here we've wrapped over - set to past-the-end. - for (size_t i = 0; i < rank; ++i) - { - curr[i] = boundary[i]; - } + curr = boundary; return *this; } - bounds_iterator operator++(int) noexcept + + constexpr bounds_iterator operator++(int) noexcept { auto ret = *this; ++(*this); return ret; } - bounds_iterator& operator--() noexcept + + constexpr bounds_iterator& operator--() noexcept { - for (size_t i = rank; i-- > 0;) + if (!less(curr, boundary)) { - if (curr[i]-- > 0) - { - return *this; - } - else + // if at the past-the-end, set to last element + for (size_t i = 0; i < rank; ++i) { curr[i] = boundary[i] - 1; } + return *this; + } + for (size_t i = rank; i-- > 0;) + { + if (curr[i] >= 1) + { + curr[i]--; + return *this; + } + curr[i] = boundary[i] - 1; } // If we're here the preconditions were violated // "pre: there exists s such that r == ++s" fail_fast_assert(false); return *this; } - bounds_iterator operator--(int) noexcept + + constexpr bounds_iterator operator--(int) noexcept { auto ret = *this; --(*this); return ret; } - bounds_iterator operator+(difference_type n) const noexcept + + constexpr bounds_iterator operator+(difference_type n) const noexcept { bounds_iterator ret{ *this }; return ret += n; } - bounds_iterator& operator+=(difference_type n) noexcept + + constexpr bounds_iterator& operator+=(difference_type n) noexcept { auto linear_idx = linearize(curr) + n; - value_type stride; + std::remove_const_t stride; stride[rank - 1] = 1; for (size_t i = rank - 1; i-- > 0;) { @@ -975,76 +876,84 @@ public: curr[i] = linear_idx / stride[i]; linear_idx = linear_idx % stride[i]; } + fail_fast_assert(!less(curr, index_type{}) && !less(boundary, curr), "index is out of bounds of the array"); return *this; } - bounds_iterator operator-(difference_type n) const noexcept + + constexpr bounds_iterator operator-(difference_type n) const noexcept { bounds_iterator ret{ *this }; return ret -= n; } - bounds_iterator& operator-=(difference_type n) noexcept + + constexpr bounds_iterator& operator-=(difference_type n) noexcept { return *this += -n; } - difference_type operator-(const bounds_iterator& rhs) const noexcept + + constexpr difference_type operator-(const bounds_iterator& rhs) const noexcept { return linearize(curr) - linearize(rhs.curr); } - reference operator[](difference_type n) const noexcept + + constexpr value_type operator[](difference_type n) const noexcept { return *(*this + n); } - bool operator==(const bounds_iterator& rhs) const noexcept + + constexpr bool operator==(const bounds_iterator& rhs) const noexcept { return curr == rhs.curr; } - bool operator!=(const bounds_iterator& rhs) const noexcept + + constexpr bool operator!=(const bounds_iterator& rhs) const noexcept { return !(*this == rhs); } - bool operator<(const bounds_iterator& rhs) const noexcept + + constexpr bool operator<(const bounds_iterator& rhs) const noexcept { - for (size_t i = 0; i < rank; ++i) - { - if (curr[i] < rhs.curr[i]) - return true; - } - return false; + return less(curr, rhs.curr); } - bool operator<=(const bounds_iterator& rhs) const noexcept + + constexpr bool operator<=(const bounds_iterator& rhs) const noexcept { return !(rhs < *this); } - bool operator>(const bounds_iterator& rhs) const noexcept + + constexpr bool operator>(const bounds_iterator& rhs) const noexcept { return rhs < *this; } - bool operator>=(const bounds_iterator& rhs) const noexcept + + constexpr bool operator>=(const bounds_iterator& rhs) const noexcept { return !(rhs > *this); } + void swap(bounds_iterator& rhs) noexcept { std::swap(boundary, rhs.boundary); std::swap(curr, rhs.curr); } private: - index_size_type linearize(const value_type& idx) const noexcept + constexpr bool less(index_type& one, index_type& other) const noexcept + { + for (size_t i = 0; i < rank; ++i) + { + if (one[i] < other[i]) + return true; + } + return false; + } + + constexpr index_size_type linearize(const value_type& idx) const noexcept { // TODO: Smarter impl. // Check if past-the-end - bool pte = true; - for (size_t i = 0; i < rank; ++i) - { - if (idx[i] != boundary[i]) - { - pte = false; - break; - } - } index_size_type multiplier = 1; index_size_type res = 0; - if (pte) + if (!less(idx, boundary)) { res = 1; for (size_t i = rank; i-- > 0;) @@ -1063,119 +972,9 @@ private: } return res; } + value_type boundary; - value_type curr; -}; - -template <> -class bounds_iterator> - : public std::iterator, - ptrdiff_t, - const details::arrow_proxy>, - const index<1>> -{ - using Base = std::iterator, std::ptrdiff_t, const details::arrow_proxy>, const index<1>>; - -public: - using Base::reference; - using Base::pointer; - using Base::difference_type; - using Base::value_type; - using index_type = value_type; - using index_size_type = index_type::size_type; - - template - explicit bounds_iterator(const Bounds&, value_type curr = value_type{}) noexcept - : curr( std::move(curr) ) - {} - reference operator*() const noexcept - { - return curr; - } - pointer operator->() const noexcept - { - return details::arrow_proxy{ curr }; - } - bounds_iterator& operator++() noexcept - { - ++curr; - return *this; - } - bounds_iterator operator++(int) noexcept - { - auto ret = *this; - ++(*this); - return ret; - } - bounds_iterator& operator--() noexcept - { - curr--; - return *this; - } - bounds_iterator operator--(int) noexcept - { - auto ret = *this; - --(*this); - return ret; - } - bounds_iterator operator+(difference_type n) const noexcept - { - bounds_iterator ret{ *this }; - return ret += n; - } - bounds_iterator& operator+=(difference_type n) noexcept - { - curr += n; - return *this; - } - bounds_iterator operator-(difference_type n) const noexcept - { - bounds_iterator ret{ *this }; - return ret -= n; - } - bounds_iterator& operator-=(difference_type n) noexcept - { - return *this += -n; - } - difference_type operator-(const bounds_iterator& rhs) const noexcept - { - return curr[0] - rhs.curr[0]; - } - reference operator[](difference_type n) const noexcept - { - return curr + n; - } - bool operator==(const bounds_iterator& rhs) const noexcept - { - return curr == rhs.curr; - } - bool operator!=(const bounds_iterator& rhs) const noexcept - { - return !(*this == rhs); - } - bool operator<(const bounds_iterator& rhs) const noexcept - { - return curr[0] < rhs.curr[0]; - } - bool operator<=(const bounds_iterator& rhs) const noexcept - { - return !(rhs < *this); - } - bool operator>(const bounds_iterator& rhs) const noexcept - { - return rhs < *this; - } - bool operator>=(const bounds_iterator& rhs) const noexcept - { - return !(rhs > *this); - } - void swap(bounds_iterator& rhs) noexcept - { - std::swap(curr, rhs.curr); - } -private: - value_type curr; + std::remove_const_t curr; }; template @@ -1237,10 +1036,11 @@ public: using size_type = typename bounds_type::size_type; using index_type = typename bounds_type::index_type; using value_type = ValueType; + using const_value_type = std::add_const_t; using pointer = ValueType*; using reference = ValueType&; using iterator = std::conditional_t::value, contiguous_array_view_iterator, general_array_view_iterator>; - using const_iterator = std::conditional_t::value, contiguous_array_view_iterator>, general_array_view_iterator>>; + using const_iterator = std::conditional_t::value, contiguous_array_view_iterator>, general_array_view_iterator>>; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; using sliced_type = std::conditional_t>; @@ -1293,7 +1093,7 @@ public: } constexpr iterator end() const { - return iterator {this}; + return iterator {this, false}; } constexpr const_iterator cbegin() const { @@ -1301,7 +1101,7 @@ public: } constexpr const_iterator cend() const { - return const_iterator {reinterpret_cast *>(this)}; + return const_iterator {reinterpret_cast *>(this), false}; } constexpr reverse_iterator rbegin() const @@ -1557,9 +1357,9 @@ public: // from n-dimensions static array with size template , - typename Dummy = std::enable_if_t::value> + typename = std::enable_if_t::value> > - constexpr array_view(T(&arr)[N], size_type size) : Base(arr, typename Helper::bounds_type{ size }) + constexpr array_view(T(&arr)[N], size_type size) : Base(arr, Helper::bounds_type{size}) { fail_fast_assert(size <= N); } @@ -1608,10 +1408,9 @@ public: {} // reshape - template + template 0)>> constexpr array_view as_array_view(Dimensions2... dims) { - static_assert(sizeof...(Dimensions2) > 0, "the target array_view must have at least one dimension."); using BoundsType = typename array_view::bounds_type; auto tobounds = details::static_as_array_view_helper(dims..., details::Sep{}); details::verifyBoundsReshape(this->bounds(), tobounds); @@ -1635,7 +1434,7 @@ public: // from bytes array template::value, typename = std::enable_if_t> - constexpr auto as_array_view() const noexcept -> array_view(Base::bounds_type::static_size) / sizeof(U) : dynamic_range)> + constexpr auto as_array_view() const noexcept -> array_view(Base::bounds_type::static_size) / sizeof(U) : dynamic_range)> { static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % static_cast(sizeof(U)) == 0), "Target type must be standard layout and its size must match the byte array size"); @@ -1643,8 +1442,8 @@ public: return { reinterpret_cast(this->data()), this->bytes() / static_cast(sizeof(U)) }; } - template::value, typename Dummy = std::enable_if_t> - constexpr auto as_array_view() const noexcept -> array_view(Base::bounds_type::static_size) / sizeof(U) : dynamic_range)> + template::value, typename = std::enable_if_t> + constexpr auto as_array_view() const noexcept -> array_view(Base::bounds_type::static_size) / sizeof(U) : dynamic_range)> { static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % static_cast(sizeof(U)) == 0), "Target type must be standard layout and its size must match the byte array size"); @@ -1925,10 +1724,8 @@ private: { fail_fast_assert(m_pdata >= m_validator->m_pdata && m_pdata < m_validator->m_pdata + m_validator->size(), "iterator is out of range of the array"); } - - contiguous_array_view_iterator (const ArrayView *container, bool isbegin = false) : - m_pdata(isbegin ? container->m_pdata : container->m_pdata + container->size()), m_validator(container) { } - + contiguous_array_view_iterator (const ArrayView *container, bool isbegin) : + m_pdata(isbegin ? container->m_pdata : container->m_pdata + container->size()), m_validator(container) {} public: reference operator*() const noexcept { @@ -2044,17 +1841,15 @@ private: const ArrayView * m_container; typename ArrayView::bounds_type::iterator m_itr; - - general_array_view_iterator(const ArrayView *container, bool isbegin = false) : - m_container(container), m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) - {} - + general_array_view_iterator(const ArrayView *container, bool isbegin) : + m_container(container), m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) + {} public: - reference operator*() const noexcept + reference operator*() noexcept { return (*m_container)[*m_itr]; } - pointer operator->() const noexcept + pointer operator->() noexcept { return &(*m_container)[*m_itr]; } diff --git a/include/gsl.h b/include/gsl.h index 519682b..ec75723 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -197,6 +197,19 @@ private: } // namespace gsl +namespace std +{ + template + struct hash> + { + size_t operator()(const gsl::not_null & value) const + { + return hash{}(value); + } + }; + +} // namespace std + #ifdef _MSC_VER #undef constexpr diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp index 1e072d4..8937388 100644 --- a/tests/array_view_tests.cpp +++ b/tests/array_view_tests.cpp @@ -16,16 +16,11 @@ #include #include -#include -#include -#include + #include #include #include #include -#include -#include - using namespace std; using namespace gsl; @@ -408,71 +403,71 @@ SUITE(array_view_tests) array_view av2{ av }; CHECK(av2[1] == 5); - static_assert(std::is_convertible, array_view>::value, "ctor is not implicit!"); - - const strided_array_view src{ arr, {2, 1} }; - strided_array_view sav{ src }; - CHECK(sav.bounds().index_bounds() == index<1>{ 2 }); - CHECK(sav.bounds().stride() == 1); - CHECK(sav[1] == 5); - - static_assert(std::is_convertible, strided_array_view>::value, "ctor is not implicit!"); - } - - // Check copy constructor - { - int arr1[2] = { 3, 4 }; - const strided_array_view src1{ arr1, {2, 1} }; - strided_array_view sav1{ src1 }; - - CHECK(sav1.bounds().index_bounds() == index<1>{ 2 }); - CHECK(sav1.bounds().stride() == 1); - CHECK(sav1[0] == 3); - - int arr2[6] = { 1, 2, 3, 4, 5, 6 }; - const strided_array_view src2{ arr2, {{ 3, 2 }, { 2, 1 }} }; - strided_array_view sav2{ src2 }; - CHECK((sav2.bounds().index_bounds() == index<2>{ 3, 2 })); - CHECK((sav2.bounds().strides() == index<2>{ 2, 1 })); - CHECK((sav2[{0, 0}] == 1 && sav2[{2, 0}] == 5)); - } - - // Check const-casting assignment operator - { - int arr1[2] = { 1, 2 }; - int arr2[6] = { 3, 4, 5, 6, 7, 8 }; - - const strided_array_view src{ arr1, {{2}, {1}} }; - strided_array_view sav{ arr2, {{3}, {2}} }; - strided_array_view& sav_ref = (sav = src); - CHECK(sav.bounds().index_bounds() == index<1>{ 2 }); - CHECK(sav.bounds().strides() == index<1>{ 1 }); - CHECK(sav[0] == 1); - CHECK(&sav_ref == &sav); - } - - // Check copy assignment operator - { - int arr1[2] = { 3, 4 }; - int arr1b[1] = { 0 }; - const strided_array_view src1{ arr1, {2, 1} }; - strided_array_view sav1{ arr1b, {1, 1} }; - strided_array_view& sav1_ref = (sav1 = src1); - CHECK(sav1.bounds().index_bounds() == index<1>{ 2 }); - CHECK(sav1.bounds().strides() == index<1>{ 1 }); - CHECK(sav1[0] == 3); - CHECK(&sav1_ref == &sav1); - - const int arr2[6] = { 1, 2, 3, 4, 5, 6 }; - const int arr2b[1] = { 0 }; - const strided_array_view src2{ arr2, {{ 3, 2 },{ 2, 1 }} }; - strided_array_view sav2{ arr2b, {{ 1, 1 },{ 1, 1 }} }; - strided_array_view& sav2_ref = (sav2 = src2); - CHECK((sav2.bounds().index_bounds() == index<2>{ 3, 2 })); - CHECK((sav2.bounds().strides() == index<2>{ 2, 1 })); - CHECK((sav2[{0, 0}] == 1 && sav2[{2, 0}] == 5)); - CHECK(&sav2_ref == &sav2); + // static_assert(std::is_convertible, array_view>::value, "ctor is not implicit!"); + // + // const strided_array_view src{ arr, {2, 1} }; + // strided_array_view sav{ src }; + // CHECK(sav.bounds().index_bounds() == index<1>{ 2 }); + // CHECK(sav.bounds().stride() == 1); + // CHECK(sav[1] == 5); + // + // static_assert(std::is_convertible, strided_array_view>::value, "ctor is not implicit!"); } +// +// // Check copy constructor +// { +// int arr1[2] = { 3, 4 }; +// const strided_array_view src1{ arr1, {2, 1} }; +// strided_array_view sav1{ src1 }; +// +// CHECK(sav1.bounds().index_bounds() == index<1>{ 2 }); +// CHECK(sav1.bounds().stride() == 1); +// CHECK(sav1[0] == 3); +// +// int arr2[6] = { 1, 2, 3, 4, 5, 6 }; +// const strided_array_view src2{ arr2, {{ 3, 2 }, { 2, 1 }} }; +// strided_array_view sav2{ src2 }; +// CHECK((sav2.bounds().index_bounds() == index<2>{ 3, 2 })); +// CHECK((sav2.bounds().strides() == index<2>{ 2, 1 })); +// CHECK((sav2[{0, 0}] == 1 && sav2[{2, 0}] == 5)); +// } +// +// // Check const-casting assignment operator +// { +// int arr1[2] = { 1, 2 }; +// int arr2[6] = { 3, 4, 5, 6, 7, 8 }; +// +// const strided_array_view src{ arr1, {{2}, {1}} }; +// strided_array_view sav{ arr2, {{3}, {2}} }; +// strided_array_view& sav_ref = (sav = src); +// CHECK(sav.bounds().index_bounds() == index<1>{ 2 }); +// CHECK(sav.bounds().strides() == index<1>{ 1 }); +// CHECK(sav[0] == 1); +// CHECK(&sav_ref == &sav); +// } +// +// // Check copy assignment operator +// { +// int arr1[2] = { 3, 4 }; +// int arr1b[1] = { 0 }; +// const strided_array_view src1{ arr1, {2, 1} }; +// strided_array_view sav1{ arr1b, {1, 1} }; +// strided_array_view& sav1_ref = (sav1 = src1); +// CHECK(sav1.bounds().index_bounds() == index<1>{ 2 }); +// CHECK(sav1.bounds().strides() == index<1>{ 1 }); +// CHECK(sav1[0] == 3); +// CHECK(&sav1_ref == &sav1); +// +// const int arr2[6] = { 1, 2, 3, 4, 5, 6 }; +// const int arr2b[1] = { 0 }; +// const strided_array_view src2{ arr2, {{ 3, 2 },{ 2, 1 }} }; +// strided_array_view sav2{ arr2b, {{ 1, 1 },{ 1, 1 }} }; +// strided_array_view& sav2_ref = (sav2 = src2); +// CHECK((sav2.bounds().index_bounds() == index<2>{ 3, 2 })); +// CHECK((sav2.bounds().strides() == index<2>{ 2, 1 })); +// CHECK((sav2[{0, 0}] == 1 && sav2[{2, 0}] == 5)); +// CHECK(&sav2_ref == &sav2); +// } } TEST(strided_array_view_slice) @@ -640,23 +635,14 @@ SUITE(array_view_tests) index<1> index{ 0, 1 }; strided_array_view sav8{ arr,{ 1,{ 1,1 } } }; -#ifdef _MSC_VER strided_array_view sav9{ arr,{ { 1,1 },{ 1,1 } } }; -#endif strided_array_view sav10{ av,{ 1,{ 1,1 } } }; -#ifdef _MSC_VER strided_array_view sav11{ av,{ { 1,1 },{ 1,1 } } }; -#endif + strided_array_view sav12{ av.as_array_view(dim<2>(), dim<2>()),{ { 1 },{ 1 } } }; + strided_array_view sav13{ av.as_array_view(dim<2>(), dim<2>()),{ { 1 },{ 1,1,1 } } }; + strided_array_view sav14{ av.as_array_view(dim<2>(), dim<2>()),{ { 1,1,1 },{ 1 } } }; } #endif - - { - CHECK_THROW((strided_array_view{ av.as_array_view(dim<2>(), dim<2>()), {{1}, {1}} }), fail_fast); - CHECK_THROW((strided_array_view{ av.as_array_view(dim<2>(), dim<2>()), {{1}, {1,1,1}} }), fail_fast); -#ifdef _MSC_VER - CHECK_THROW((strided_array_view{ av.as_array_view(dim<2>(), dim<2>()), {{1,1,1}, {1}} }), fail_fast); -#endif - } } TEST(strided_array_view_type_conversion) @@ -840,6 +826,78 @@ SUITE(array_view_tests) delete[] arr; } + TEST(index_constructors) + { + { + // components of the same type + index<3> i1(0, 1, 2); + CHECK(i1[0] == 0); + + // components of different types + size_t c0 = 0; + size_t c1 = 1; + index<3> i2(c0, c1, 2); + CHECK(i2[0] == 0); + + // from array + index<3> i3 = { 0,1,2 }; + CHECK(i3[0] == 0); + + // from other index of the same size type + index<3> i4 = i3; + CHECK(i4[0] == 0); + + // default + index<3> i7; + CHECK(i7[0] == 0); + + // default + index<3> i9 = {}; + CHECK(i9[0] == 0); + } + + { + // components of the same type + index<1> i1(0); + CHECK(i1[0] == 0); + + // components of different types + size_t c0 = 0; + index<1> i2(c0); + CHECK(i2[0] == 0); + + // from array + index<1> i3 = { 0 }; + CHECK(i3[0] == 0); + + // from int + index<1> i4 = 0; + CHECK(i4[0] == 0); + + // from other index of the same size type + index<1> i5 = i3; + CHECK(i5[0] == 0); + + // default + index<1> i8; + CHECK(i8[0] == 0); + + // default + index<1> i9 = {}; + CHECK(i9[0] == 0); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + index<3> i1(0, 1); + index<3> i2(0, 1, 2, 3); + index<3> i3 = { 0 }; + index<3> i4 = { 0, 1, 2, 3 }; + index<1> i5 = { 0,1 }; + } +#endif + } + TEST(index_operations) { ptrdiff_t a[3] = { 0, 1, 2 }; @@ -926,11 +984,35 @@ SUITE(array_view_tests) } } - ptrdiff_t idx = 0; - for (auto num : section) + size_t check_sum = 0; + for (auto i = 0; i < length; ++i) { - CHECK(num == av[idx][1]); - idx++; + check_sum += av[i][1]; + } + + { + auto idx = 0; + size_t sum = 0; + for (auto num : section) + { + CHECK(num == av[idx][1]); + sum += num; + idx++; + } + + CHECK(sum == check_sum); + } + { + size_t idx = length - 1; + size_t sum = 0; + for (auto iter = section.rbegin(); iter != section.rend(); ++iter) + { + CHECK(*iter == av[idx][1]); + sum += *iter; + idx--; + } + + CHECK(sum == check_sum); } } @@ -955,7 +1037,7 @@ SUITE(array_view_tests) } // both bounds are dynamic { - array_view av(arr, 4); + array_view av = arr; iterate_second_column(av); } } @@ -1543,7 +1625,7 @@ SUITE(array_view_tests) CHECK_THROW(f(), fail_fast); } - TEST(AsWriteableBytes) + TEST(AsWriteableBytes) { int a[] = { 1, 2, 3, 4 }; @@ -1569,7 +1651,36 @@ SUITE(array_view_tests) CHECK(wav.data() == (byte*)&a[0]); CHECK(wav.length() == sizeof(a)); } + } + TEST(NonConstIterator) + { + int a[] = { 1, 2, 3, 4 }; + + { + array_view av = a; + auto wav = av.as_writeable_bytes(); + for (auto& b : wav) + { + b = byte(0); + } + for (size_t i = 0; i < 4; ++i) + { + CHECK(a[i] == 0); + } + } + + { + array_view av = a; + for (auto& n : av) + { + n = 1; + } + for (size_t i = 0; i < 4; ++i) + { + CHECK(a[i] == 1); + } + } } TEST(ArrayViewComparison)