Added tests for index size_type conversions

This commit is contained in:
Anna Gringauze 2015-10-05 21:04:56 -07:00
parent db38497d05
commit 546f8cc130
2 changed files with 243 additions and 72 deletions

View File

@ -70,7 +70,7 @@ namespace details
template <typename SizeType> template <typename SizeType>
struct SizeTypeTraits struct SizeTypeTraits
{ {
static const size_t max_value = std::is_signed<SizeType>::value ? static_cast<typename std::make_unsigned<SizeType>::type>(-1) / 2 : static_cast<SizeType>(-1); static const SizeType max_value = std::is_signed<SizeType>::value ? static_cast<typename std::make_unsigned<SizeType>::type>(-1) / 2 : static_cast<SizeType>(-1);
}; };
template <typename T> template <typename T>
@ -108,6 +108,9 @@ public:
using reference = std::add_lvalue_reference_t<value_type>; using reference = std::add_lvalue_reference_t<value_type>;
using const_reference = std::add_lvalue_reference_t<std::add_const_t<value_type>>; using const_reference = std::add_lvalue_reference_t<std::add_const_t<value_type>>;
constexpr index() noexcept
{}
constexpr index(const value_type(&values)[Rank]) noexcept constexpr index(const value_type(&values)[Rank]) noexcept
{ {
std::copy(values, values + Rank, elems); std::copy(values, values + Rank, elems);
@ -120,36 +123,29 @@ public:
std::copy(begin(il), end(il), elems); 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 // copy from index over smaller domain
template <typename OtherValueType> template <typename OtherValueType,
constexpr index(typename std::enable_if_t<(details::SizeTypeTraits<OtherValueType>::max_value <= details::SizeTypeTraits<value_type>::max_value), const index<Rank, OtherValueType>>::type& other) noexcept bool Enabled = (details::SizeTypeTraits<OtherValueType>::max_value <= details::SizeTypeTraits<value_type>::max_value),
typename Other = std::enable_if_t<Enabled, index<Rank, OtherValueType>>>
constexpr index(const index<Rank, OtherValueType>& other) noexcept
{ {
std::copy(other.elems, other.elems + Rank, elems); std::copy(other.elems, other.elems + Rank, elems);
} }
// copy from index over larger domain // copy from index over larger domain
template <typename OtherValueType> template <typename OtherValueType,
constexpr index(typename std::enable_if_t<(details::SizeTypeTraits<OtherValueType>::max_value > details::SizeTypeTraits<value_type>::max_value), const index<Rank, OtherValueType>>::type& other) noexcept bool Enabled = (details::SizeTypeTraits<OtherValueType>::max_value > details::SizeTypeTraits<value_type>::max_value),
typename Other = std::enable_if_t<Enabled, index<Rank, OtherValueType>>>
constexpr index(const index<Rank, OtherValueType>& other, void* ptr = 0) noexcept
{ {
for (size_t i = 0; i < Rank; ++i) bool ok = std::accumulate(other.elems, other.elems + Rank, true,
{ [&](bool b, OtherValueType val) { return b && (val <= static_cast<OtherValueType>(details::SizeTypeTraits<value_type>::max_value)); }
fail_fast_assert(other.elems[i] <= static_cast<OtherValueType>(SizeTypeTraits<value_type>::max_value)); );
elems[i] = static_cast<value_type>(other.elems[i]);
}
}
constexpr static index shift_left(const index<Rank + 1, value_type>& other) noexcept 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<value_type>(val); });
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; constexpr index& operator=(const index& rhs) noexcept = default;
@ -230,7 +226,7 @@ public:
return ret; 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; return rhs * v;
} }
@ -246,6 +242,7 @@ public:
std::transform(elems, elems + rank, elems, [v](value_type x) { return std::divides<ValueType>{}(x, v); }); std::transform(elems, elems + rank, elems, [v](value_type x) { return std::divides<ValueType>{}(x, v); });
return *this; return *this;
} }
private: private:
value_type elems[Rank] = {}; value_type elems[Rank] = {};
}; };
@ -262,44 +259,34 @@ public:
using reference = std::add_lvalue_reference_t<value_type>; using reference = std::add_lvalue_reference_t<value_type>;
using const_reference = std::add_lvalue_reference_t<std::add_const_t<value_type>>; using const_reference = std::add_lvalue_reference_t<std::add_const_t<value_type>>;
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(const value_type(&values)[1]) noexcept : index(values[0])
constexpr index(std::initializer_list<value_type> 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 &) noexcept = default; constexpr index(const index &) noexcept = default;
template <typename OtherValueType> template <typename OtherValueType,
constexpr index(typename std::enable_if_t<(details::SizeTypeTraits<OtherValueType>::max_value <= details::SizeTypeTraits<value_type>::max_value), const index<1, OtherValueType>>::type& other) noexcept bool Enabled = (details::SizeTypeTraits<OtherValueType>::max_value <= details::SizeTypeTraits<value_type>::max_value),
typename Other = std::enable_if_t<Enabled, index<1, OtherValueType>>>
constexpr index(const index<1, OtherValueType>& other) noexcept
{ {
value = static_cast<ValueType>(other.value); value = static_cast<ValueType>(other.value);
} }
template <typename OtherValueType> template <typename OtherValueType,
constexpr index(typename std::enable_if_t<(details::SizeTypeTraits<OtherValueType>::max_value > details::SizeTypeTraits<value_type>::max_value), const index<1, OtherValueType>>::type& other) noexcept bool Enabled = (details::SizeTypeTraits<OtherValueType>::max_value > details::SizeTypeTraits<value_type>::max_value),
typename Other = std::enable_if_t<Enabled, index<1, OtherValueType>>>
constexpr index(const index<1, OtherValueType>& other, void* ptr=0) noexcept
{ {
fail_fast_assert(other.value <= static_cast<OtherValueType>(SizeTypeTraits<value_type>::max_value)); fail_fast_assert(other.value <= static_cast<OtherValueType>(details::SizeTypeTraits<value_type>::max_value));
value = static_cast<value_type>(other.value); value = static_cast<value_type>(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 // Preconditions: component_idx < 1
constexpr reference operator[](value_type component_idx) noexcept constexpr reference operator[](value_type component_idx) noexcept
{ {
@ -388,7 +375,7 @@ public:
value /= v; value /= v;
return *this; 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 }; return{ rhs * v };
} }
@ -822,7 +809,7 @@ public:
constexpr const_iterator begin() const noexcept constexpr const_iterator begin() const noexcept
{ {
return const_iterator(*this, index_type::zero()); return const_iterator(*this);
} }
constexpr const_iterator end() const noexcept constexpr const_iterator end() const noexcept
@ -908,7 +895,7 @@ public:
template <bool Enabled = (rank > 1), typename Ret = std::enable_if_t<Enabled, sliced_type>> template <bool Enabled = (rank > 1), typename Ret = std::enable_if_t<Enabled, sliced_type>>
constexpr sliced_type slice() const 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 <size_t Dim = 0> template <size_t Dim = 0>
constexpr size_type extent() const noexcept constexpr size_type extent() const noexcept
@ -922,7 +909,7 @@ public:
} }
const_iterator begin() const noexcept const_iterator begin() const noexcept
{ {
return const_iterator{ *this, index_type::zero() }; return const_iterator{ *this };
} }
const_iterator end() const noexcept const_iterator end() const noexcept
{ {
@ -959,9 +946,9 @@ public:
using index_type = value_type; using index_type = value_type;
using index_size_type = typename IndexType::value_type; using index_size_type = typename IndexType::value_type;
template <typename Bounds> template <typename Bounds>
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()) : boundary(bnd.index_bounds())
, curr( std::move(curr) ) , curr(std::move(curr))
{ {
static_assert(is_bounds<Bounds>::value, "Bounds type must be provided"); static_assert(is_bounds<Bounds>::value, "Bounds type must be provided");
} }
@ -1270,13 +1257,24 @@ namespace details
Bounds::size_type stride[Bounds::rank]; Bounds::size_type stride[Bounds::rank];
stride[Bounds::rank - 1] = 1; 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 }; return{ stride };
} }
template <size_t Rank, typename ValueType, bool Enabled = (Rank > 1), typename Ret = std::enable_if_t<Enabled, index<Rank-1, ValueType>>>
constexpr Ret shift_left(const index<Rank, ValueType>& other) noexcept
{
Ret ret;
for (size_t i = 0; i < Rank - 1; ++i)
{
ret[i] = other[i + 1];
}
return ret;
}
template <typename BoundsSrc, typename BoundsDest> template <typename BoundsSrc, typename BoundsDest>
void verifyBoundsReshape(const BoundsSrc &src, const BoundsDest &dest) void verifyBoundsReshape(const BoundsSrc &src, const BoundsDest &dest)
{ {

View File

@ -552,6 +552,26 @@ SUITE(array_view_tests)
CHECK_THROW(av.section(5, 5), fail_fast); CHECK_THROW(av.section(5, 5), fail_fast);
} }
{
// zero stride
strided_array_view<int, 1> sav{ av,{ { 4 },{} } };
CHECK(sav[0] == 0);
CHECK(sav[3] == 0);
CHECK_THROW(sav[4], fail_fast);
}
{
// zero extent
strided_array_view<int, 1> sav{ av,{ {},{ 1 } } };
CHECK_THROW(sav[0], fail_fast);
}
{
// zero extent and stride
strided_array_view<int, 1> sav{ av,{ {},{} } };
CHECK_THROW(sav[0], fail_fast);
}
{ {
// strided array ctor with matching strided bounds // strided array ctor with matching strided bounds
strided_array_view<int, 1> sav{ arr,{ 4, 1 } }; strided_array_view<int, 1> sav{ arr,{ 4, 1 } };
@ -607,9 +627,6 @@ SUITE(array_view_tests)
#ifdef CONFIRM_COMPILATION_ERRORS #ifdef CONFIRM_COMPILATION_ERRORS
{ {
strided_array_view<int, 1> sav{ av,{ { 4 },{} } };
strided_array_view<int, 1> sav{ av,{ {},{ 1 } } };
strided_array_view<int, 1> sav{ av,{ {},{} } };
strided_array_view<int, 1> sav0{ av.data(), { 3, 2 } }; strided_array_view<int, 1> sav0{ av.data(), { 3, 2 } };
strided_array_view<int, 1> sav1{ arr, { 1 } }; strided_array_view<int, 1> sav1{ arr, { 1 } };
strided_array_view<int, 1> sav2{ arr, { 1,1,1 } }; strided_array_view<int, 1> sav2{ arr, { 1,1,1 } };
@ -618,27 +635,26 @@ SUITE(array_view_tests)
strided_array_view<int, 2> sav5{ av.as_array_view(dim<2>(), dim<2>()), { 1 } }; strided_array_view<int, 2> sav5{ av.as_array_view(dim<2>(), dim<2>()), { 1 } };
strided_array_view<int, 2> sav6{ av.as_array_view(dim<2>(), dim<2>()), { 1,1,1 } }; strided_array_view<int, 2> sav6{ av.as_array_view(dim<2>(), dim<2>()), { 1,1,1 } };
strided_array_view<int, 2> sav7{ av.as_array_view(dim<2>(), dim<2>()), { { 1,1 },{ 1,1 },{ 1,1 } } }; strided_array_view<int, 2> sav7{ av.as_array_view(dim<2>(), dim<2>()), { { 1,1 },{ 1,1 },{ 1,1 } } };
index<1> index{ 0, 1 };
strided_array_view<int, 1> sav8{ arr,{ 1,{ 1,1 } } };
#ifdef _MSC_VER
strided_array_view<int, 1> sav9{ arr,{ { 1,1 },{ 1,1 } } };
#endif
strided_array_view<int, 1> sav10{ av,{ 1,{ 1,1 } } };
#ifdef _MSC_VER
strided_array_view<int, 1> sav11{ av,{ { 1,1 },{ 1,1 } } };
#endif
} }
#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<int, 1>{ arr, {1, {1,1}} }), fail_fast);
#ifdef _MSC_VER
CHECK_THROW((strided_array_view<int, 1>{ arr, {{1,1 }, {1,1}} }), fail_fast);
#endif
CHECK_THROW((strided_array_view<int, 1>{ av, {1, {1,1}} }), fail_fast);
#ifdef _MSC_VER
CHECK_THROW((strided_array_view<int, 1>{ av, {{1,1 }, {1,1}} }), fail_fast);
#endif
CHECK_THROW((strided_array_view<int, 2>{ av.as_array_view(dim<2>(), dim<2>()), {{1}, {1}} }), fail_fast); CHECK_THROW((strided_array_view<int, 2>{ av.as_array_view(dim<2>(), dim<2>()), {{1}, {1}} }), fail_fast);
CHECK_THROW((strided_array_view<int, 2>{ av.as_array_view(dim<2>(), dim<2>()), {{1}, {1,1,1}} }), fail_fast); CHECK_THROW((strided_array_view<int, 2>{ av.as_array_view(dim<2>(), dim<2>()), {{1}, {1,1,1}} }), fail_fast);
#ifdef _MSC_VER #ifdef _MSC_VER
CHECK_THROW((strided_array_view<int, 2>{ av.as_array_view(dim<2>(), dim<2>()), {{1,1,1}, {1}} }), fail_fast); CHECK_THROW((strided_array_view<int, 2>{ av.as_array_view(dim<2>(), dim<2>()), {{1,1,1}, {1}} }), fail_fast);
#endif #endif
} }
} }
TEST(strided_array_view_type_conversion) 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[0] == 0);
CHECK(i[1] == 1); CHECK(i[1] == 1);
@ -972,7 +999,7 @@ SUITE(array_view_tests)
auto bounds = strided_bounds<1>({ length }, { 2 }); auto bounds = strided_bounds<1>({ length }, { 2 });
#else #else
auto bounds = strided_bounds<1>(index<1>{ length }, index<1>{ 2 }); auto bounds = strided_bounds<1>(index<1>{ length }, index<1>{ 2 });
#endif #endif
strided_array_view<int, 1> strided(&av.data()[1], av.size() - 1, bounds); strided_array_view<int, 1> strided(&av.data()[1], av.size() - 1, bounds);
CHECK(strided.size() == length); CHECK(strided.size() == length);
@ -1033,7 +1060,7 @@ SUITE(array_view_tests)
for (unsigned int k = 0; k < section.extent<2>(); ++k) for (unsigned int k = 0; k < section.extent<2>(); ++k)
{ {
auto idx = index<3>{ i,j,k }; // avoid braces in the CHECK macro 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<size_t Rank, typename T1, typename T2>
index<Rank, T2> Convert(index<Rank, T1> 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) TEST(constructors)
{ {
array_view<int, dynamic_range> av(nullptr); array_view<int, dynamic_range> av(nullptr);