From 546f8cc1306ec69cc3a8f292785658da8caf157a Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Mon, 5 Oct 2015 21:04:56 -0700 Subject: [PATCH] Added tests for index size_type conversions --- include/array_view.h | 106 +++++++++---------- tests/array_view_tests.cpp | 209 +++++++++++++++++++++++++++++++++---- 2 files changed, 243 insertions(+), 72 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index aa1b4e5..98bbadf 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -70,7 +70,7 @@ namespace details template struct SizeTypeTraits { - static const size_t max_value = std::is_signed::value ? static_cast::type>(-1) / 2 : static_cast(-1); + static const SizeType max_value = std::is_signed::value ? static_cast::type>(-1) / 2 : static_cast(-1); }; template @@ -108,6 +108,9 @@ public: using reference = std::add_lvalue_reference_t; using const_reference = std::add_lvalue_reference_t>; + constexpr index() noexcept + {} + constexpr index(const value_type(&values)[Rank]) noexcept { std::copy(values, values + Rank, elems); @@ -120,36 +123,29 @@ public: std::copy(begin(il), end(il), elems); } - constexpr index(const index& other) noexcept = default; + 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 + template ::max_value <= details::SizeTypeTraits::max_value), + typename Other = std::enable_if_t>> + constexpr index(const index& 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 + template ::max_value > details::SizeTypeTraits::max_value), + typename Other = std::enable_if_t>> + constexpr index(const index& other, void* ptr = 0) 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]); - } - } + bool ok = std::accumulate(other.elems, other.elems + Rank, true, + [&](bool b, OtherValueType val) { return b && (val <= static_cast(details::SizeTypeTraits::max_value)); } + ); - 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); + fail_fast_assert(ok, "other value must fit in the new domain"); + std::transform(other.elems, other.elems + rank, elems, [&](OtherValueType val) { return static_cast(val); }); } constexpr index& operator=(const index& rhs) noexcept = default; @@ -230,7 +226,7 @@ public: return ret; } - friend static constexpr index operator*(value_type v, const index& rhs) noexcept + friend constexpr index operator*(value_type v, const index& rhs) noexcept { return rhs * v; } @@ -246,6 +242,7 @@ public: std::transform(elems, elems + rank, elems, [v](value_type x) { return std::divides{}(x, v); }); return *this; } + private: value_type elems[Rank] = {}; }; @@ -262,44 +259,34 @@ public: using reference = std::add_lvalue_reference_t; using const_reference = std::add_lvalue_reference_t>; - constexpr index(value_type e0) noexcept : value(e0) + constexpr index() noexcept : value(0) {} - constexpr index(const value_type(&values)[1]) noexcept : index(values[0]) + constexpr index(value_type e) noexcept : value(e) {} - // Preconditions: il.size() == rank - 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 value_type(&values)[1]) noexcept : index(values[0]) + {} constexpr index(const index &) noexcept = default; - template - constexpr index(typename std::enable_if_t<(details::SizeTypeTraits::max_value <= details::SizeTypeTraits::max_value), const index<1, OtherValueType>>::type& other) noexcept + template ::max_value <= details::SizeTypeTraits::max_value), + typename Other = std::enable_if_t>> + constexpr index(const index<1, OtherValueType>& other) noexcept { value = static_cast(other.value); } - template - constexpr index(typename std::enable_if_t<(details::SizeTypeTraits::max_value > details::SizeTypeTraits::max_value), const index<1, OtherValueType>>::type& other) noexcept + template ::max_value > details::SizeTypeTraits::max_value), + typename Other = std::enable_if_t>> + constexpr index(const index<1, OtherValueType>& other, void* ptr=0) noexcept { - fail_fast_assert(other.value <= static_cast(SizeTypeTraits::max_value)); + fail_fast_assert(other.value <= static_cast(details::SizeTypeTraits::max_value)); value = static_cast(other.value); } - constexpr static index shift_left(const index<2, value_type>& other) noexcept - { - return other.elems[1]; - } - - constexpr static index zero() noexcept - { - return 0; - } - // Preconditions: component_idx < 1 constexpr reference operator[](value_type component_idx) noexcept { @@ -388,7 +375,7 @@ public: value /= v; return *this; } - friend static constexpr index operator*(value_type v, const index& rhs) noexcept + friend constexpr index operator*(value_type v, const index& rhs) noexcept { return{ rhs * v }; } @@ -822,7 +809,7 @@ public: constexpr const_iterator begin() const noexcept { - return const_iterator(*this, index_type::zero()); + return const_iterator(*this); } constexpr const_iterator end() const noexcept @@ -908,7 +895,7 @@ public: template 1), typename Ret = std::enable_if_t> constexpr sliced_type slice() const { - return{ sliced_type::index_type::shift_left(m_extents), sliced_type::index_type::shift_left(m_strides) }; + return{ details::shift_left(m_extents), details::shift_left(m_strides) }; } template constexpr size_type extent() const noexcept @@ -922,7 +909,7 @@ public: } const_iterator begin() const noexcept { - return const_iterator{ *this, index_type::zero() }; + return const_iterator{ *this }; } const_iterator end() const noexcept { @@ -959,9 +946,9 @@ public: using index_type = value_type; using index_size_type = typename IndexType::value_type; template - explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept + explicit bounds_iterator(const Bounds& bnd, value_type curr = value_type{}) noexcept : boundary(bnd.index_bounds()) - , curr( std::move(curr) ) + , curr(std::move(curr)) { static_assert(is_bounds::value, "Bounds type must be provided"); } @@ -1270,13 +1257,24 @@ namespace details Bounds::size_type stride[Bounds::rank]; stride[Bounds::rank - 1] = 1; - for (size_t i = Bounds::rank - 1; Bounds::rank > 1 && i > 0; --i) + for (size_t i = 1; i < Bounds::rank; ++i) { - stride[i - 1] = stride[i] * extents[i]; + stride[Bounds::rank - i - 1] = stride[Bounds::rank - i] * extents[Bounds::rank - i]; } return{ stride }; } + template 1), typename Ret = std::enable_if_t>> + constexpr Ret shift_left(const index& other) noexcept + { + Ret ret; + for (size_t i = 0; i < Rank - 1; ++i) + { + ret[i] = other[i + 1]; + } + return ret; + } + template void verifyBoundsReshape(const BoundsSrc &src, const BoundsDest &dest) { diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp index ec10bbd..a56d0f2 100644 --- a/tests/array_view_tests.cpp +++ b/tests/array_view_tests.cpp @@ -552,6 +552,26 @@ 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 } }; @@ -607,9 +627,6 @@ 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 } }; @@ -618,27 +635,26 @@ SUITE(array_view_tests) strided_array_view sav5{ av.as_array_view(dim<2>(), dim<2>()), { 1 } }; strided_array_view sav6{ av.as_array_view(dim<2>(), dim<2>()), { 1,1,1 } }; strided_array_view sav7{ av.as_array_view(dim<2>(), dim<2>()), { { 1,1 },{ 1,1 },{ 1,1 } } }; + + 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 } #endif - + { - // stride initializer list size should match the rank of the array - CHECK_THROW((index<1>{ 0,1 }), fail_fast); - CHECK_THROW((strided_array_view{ arr, {1, {1,1}} }), fail_fast); -#ifdef _MSC_VER - CHECK_THROW((strided_array_view{ arr, {{1,1 }, {1,1}} }), fail_fast); -#endif - CHECK_THROW((strided_array_view{ av, {1, {1,1}} }), fail_fast); -#ifdef _MSC_VER - CHECK_THROW((strided_array_view{ av, {{1,1 }, {1,1}} }), fail_fast); -#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) @@ -856,7 +872,18 @@ SUITE(array_view_tests) } { - index<2> k = index<2>::shift_left(i); + index<3> k = 3 * i; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 0); + CHECK(k[1] == 3); + CHECK(k[2] == 6); + } + + { + index<2> k = details::shift_left(i); CHECK(i[0] == 0); CHECK(i[1] == 1); @@ -972,7 +999,7 @@ SUITE(array_view_tests) auto bounds = strided_bounds<1>({ length }, { 2 }); #else auto bounds = strided_bounds<1>(index<1>{ length }, index<1>{ 2 }); -#endif +#endif strided_array_view strided(&av.data()[1], av.size() - 1, bounds); CHECK(strided.size() == length); @@ -1033,7 +1060,7 @@ SUITE(array_view_tests) for (unsigned int k = 0; k < section.extent<2>(); ++k) { auto idx = index<3>{ i,j,k }; // avoid braces in the CHECK macro - CHECK(section[idx] == expected[2 * i + 2 * j + k]); + CHECK(section[idx] == expected[2 * i + 2 * j + k]); } } @@ -1151,6 +1178,152 @@ SUITE(array_view_tests) } + template + index Convert(index index) + { + return{ index }; + } + + TEST(DomainConverters) + { + // to smaller + { + index<2, int> int_index{ 0,1 }; + index<2, short> short_index{ int_index }; + + CHECK(short_index[0] == 0); + CHECK(short_index[1] == 1); + } + + // to smaller (failure) + { + index<2, int> big_int_index{ INT_MAX, 1 }; + CHECK_THROW((Convert<2,int, short int>(big_int_index)), fail_fast); + } + + // to same, sign mismatch + { + index<2, int> int_index{ 0,1 }; + index<2, unsigned int> uint_index{ int_index }; + + CHECK(uint_index[0] == 0); + CHECK(uint_index[1] == 1); + } + + // to same, sign mismatch, reversed + { + index<2, unsigned int> uint_index{ 0,1 }; + index<2, int> int_index{ uint_index }; + + CHECK(int_index[0] == 0); + CHECK(int_index[1] == 1); + } + + // to smaller, sign mismatch + { + index<2, int> int_index{ 0,1 }; + index<2, unsigned short> ushort_index{ int_index }; + + CHECK(ushort_index[0] == 0); + CHECK(ushort_index[1] == 1); + } + + // to bigger + { + index<2, int> int_index{ 0,1 }; + index<2, long long> longlong_index{ int_index }; + + CHECK(longlong_index[0] == 0); + CHECK(longlong_index[1] == 1); + } + + // to bigger with max index + { + index<2, int> big_int_index{ INT_MAX, 1 }; + index<2, long long> longlong_index{ big_int_index }; + + CHECK(longlong_index[0] == INT_MAX); + CHECK(longlong_index[1] == 1); + } + + // to bigger, sign mismatch + { + index<2, int> int_index{ 0,1 }; + index<2, unsigned long long> ulonglong_index{ int_index }; + + CHECK(ulonglong_index[0] == 0); + CHECK(ulonglong_index[1] == 1); + } + + } + + TEST(DomainConvertersRank1) + { + // to smaller + { + index<1, int> int_index{ 0 }; + index<1, short> short_index{ int_index }; + + CHECK(short_index[0] == 0); + } + + // to smaller (failure) + { + index<1, int> big_int_index{ INT_MAX }; + + CHECK_THROW((Convert<1, int, short int>(big_int_index)), fail_fast); + } + + // to same, sign mismatch + { + index<1, int> int_index{ 0 }; + index<1, unsigned int> uint_index{ int_index }; + + CHECK(uint_index[0] == 0); + } + + // to same, sign mismatch, reversed + { + index<1, unsigned int> uint_index{ 0 }; + index<1, int> int_index{ uint_index }; + + CHECK(int_index[0] == 0); + } + + // to smaller, sign mismatch + { + index<1, int> int_index{ 0 }; + index<1, unsigned short> ushort_index{ int_index }; + + CHECK(ushort_index[0] == 0); + } + + // to bigger + { + index<1, int> int_index{ 0 }; + index<1, long long> longlong_index{ int_index }; + + CHECK(longlong_index[0] == 0); + } + + // to bigger with max index + { + index<1, int> big_int_index{ INT_MAX }; + index<1, long long> longlong_index{ big_int_index }; + + CHECK(longlong_index[0] == INT_MAX); + } + + // to bigger, sign mismatch + { + index<1, int> int_index{ 0 }; + index<1, unsigned long long> ulonglong_index{ int_index }; + + CHECK(ulonglong_index[0] == 0); + } + + } + TEST(constructors) { array_view av(nullptr);