Merge branch 'master' of https://github.com/Microsoft/GSL
Conflicts:
include/array_view.h
diff --git a/.travis.yml b/.travis.yml
index 524f1fb..3c64230 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -12,6 +12,7 @@
packages:
- clang-3.6
- cmake
+ - g++-5
sources: &sources
- ubuntu-toolchain-r-test
- llvm-toolchain-precise-3.6
diff --git a/include/array_view.h b/include/array_view.h
index 1178e43..4211637 100644
--- a/include/array_view.h
+++ b/include/array_view.h
@@ -24,6 +24,7 @@
#include <cstddef>
#include <cstdint>
#include <limits>
+#include <numeric>
#include <type_traits>
#include <utility>
#include <array>
@@ -67,179 +68,6 @@
*/
namespace details
{
- template <typename ConcreteType, size_t Rank>
- class coordinate_facade
- {
- static_assert(Rank > 0, "Rank must be greater than 0!");
-
- template <typename OtherConcreteType, size_t OtherRank>
- friend class coordinate_facade;
- public:
- using reference = std::add_lvalue_reference_t<std::ptrdiff_t>;
- using const_reference = std::add_const_t<reference>;
- using value_type = std::ptrdiff_t;
- static const size_t rank = Rank;
-
- constexpr coordinate_facade() noexcept
- {
- static_assert(std::is_base_of<coordinate_facade, ConcreteType>::value, "ConcreteType must be derived from coordinate_facade.");
- }
-
- constexpr coordinate_facade(const value_type(&values)[rank]) noexcept
- {
- static_assert(std::is_base_of<coordinate_facade, ConcreteType>::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<coordinate_facade, ConcreteType>::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<value_type> il)
- {
- static_assert(std::is_base_of<coordinate_facade, ConcreteType>::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 <typename OtherConcreteType>
- constexpr coordinate_facade(const coordinate_facade<OtherConcreteType, Rank>& other)
- {
- for (size_t i = 0; i < rank; ++i)
- elems[i] = 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<ValueType>{});
- 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<const ConcreteType&>(*this);
- }
- constexpr ConcreteType& to_concrete() noexcept
- {
- return static_cast<ConcreteType&>(*this);
- }
- };
template <typename T>
class arrow_proxy
{
@@ -261,43 +89,156 @@
}
template <size_t Rank>
-class index : private details::coordinate_facade<index<Rank>, Rank>
+class index final
{
- using Base = details::coordinate_facade<index<Rank>, Rank>;
- friend Base;
+ static_assert(Rank > 0, "Rank must be greater than 0!");
+
template <size_t OtherRank>
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<value_type> il) : Base(il) {}
+ static const size_t rank = Rank;
+ using value_type = std::remove_reference_t<ValueType>;
+ using reference = std::add_lvalue_reference_t<value_type>;
+ using const_reference = std::add_lvalue_reference_t<std::add_const_t<value_type>>;
- constexpr index(const index &) = default;
+ constexpr index() noexcept
+ {}
- constexpr static index shift_left(const index<rank+1>& other) noexcept
+ constexpr index(const value_type(&values)[Rank]) 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<value_type> 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 <typename OtherValueType,
+ 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);
+ }
+
+ // copy from index over larger domain
+ template <typename OtherValueType,
+ 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
+ {
+ 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(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); });
+ }
+
+ 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<ValueType>{});
+ 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<ValueType>{});
+ return *this;
+ }
+
+ constexpr index& operator-=(const index& rhs) noexcept
+ {
+ std::transform(elems, elems + rank, rhs.elems, elems, std::minus<ValueType>{});
+ 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 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<ValueType>{}(x, v); });
+ return *this;
+ }
+
+ constexpr index& operator/=(value_type v) noexcept
+ {
+ std::transform(elems, elems + rank, elems, [v](value_type x) { return std::divides<ValueType>{}(x, v); });
+ return *this;
+ }
+
+private:
+ value_type elems[Rank] = {};
};
template<>
@@ -326,12 +267,19 @@
value = begin(il)[0];
}
- constexpr index(const index &) = default;
+ template <size_t, typename OtherValueType>
+ friend class index;
- constexpr static index shift_left(const index<rank + 1>& other) noexcept
- {
- return other.elems[1];
- }
+ 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
@@ -439,7 +387,7 @@
friend constexpr index operator*(value_type v, const index& rhs) noexcept
{
- return index(rhs * v);
+ return{ rhs * v };
}
private:
@@ -696,6 +644,17 @@
{
return TypeListIndexer<TypeChain>(obj);
}
+
+ 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 IndexType>
@@ -799,9 +758,9 @@
constexpr index_type index_bounds() const noexcept
{
- index_type extents;
+ size_type extents[rank] = {};
m_ranges.serialize(extents);
- return extents;
+ return{ extents };
}
template <std::ptrdiff_t... Ranges>
@@ -823,8 +782,6 @@
constexpr const_iterator end() const noexcept
{
- index_type boundary;
- m_ranges.serialize(boundary);
return const_iterator(*this, this->index_bounds());
}
};
@@ -839,20 +796,21 @@
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;
- using index_type = index<rank>;
+ static const size_t rank = Rank;
+ using reference = SizeType&;
+ using const_reference = const SizeType&;
+ using size_type = SizeType;
+ using difference_type = SizeType;
+ using value_type = SizeType;
+ using index_type = index<rank, size_type>;
using iterator = bounds_iterator<index_type>;
using const_iterator = bounds_iterator<index_type>;
- static const int dynamic_rank = rank;
+ static const size_t dynamic_rank = rank;
static const std::ptrdiff_t static_size = dynamic_range;
using sliced_type = std::conditional_t<rank != 0, strided_bounds<rank - 1>, void>;
using mapping_type = generalized_mapping_tag;
- constexpr strided_bounds(const strided_bounds &) = default;
+
+ constexpr strided_bounds(const strided_bounds &) noexcept = default;
constexpr strided_bounds(const index_type& extents, const index_type& strides)
: m_strides(strides)
@@ -865,16 +823,22 @@
: Base(values), m_strides(std::move(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;
}
@@ -882,7 +846,9 @@
{
size_type ret = 1;
for (size_t i = 0; i < rank; ++i)
- ret *= Base::elems[i];
+ {
+ ret *= m_extents[i];
+ }
return ret;
}
@@ -890,18 +856,18 @@
{
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;
@@ -915,19 +881,19 @@
template <bool Enabled = (rank > 1), typename Ret = std::enable_if_t<Enabled, sliced_type>>
constexpr sliced_type slice() const
{
- return{ (value_type(&)[rank - 1])Base::elems[1], sliced_type::index_type::shift_left(m_strides) };
+ return{ details::shift_left(m_extents), details::shift_left(m_strides) };
}
template <size_t Dim = 0>
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
@@ -941,6 +907,7 @@
}
private:
+ index_type m_extents;
index_type m_strides;
};
@@ -968,11 +935,11 @@
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 <typename Bounds>
- explicit bounds_iterator(const Bounds & bnd, value_type curr = value_type{}) 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<Bounds>::value, "Bounds type must be provided");
}
@@ -1278,11 +1245,14 @@
constexpr std::enable_if_t<std::is_same<typename Bounds::mapping_type, contiguous_mapping_tag>::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;
+ typename Bounds::size_type stride[Bounds::rank] = {};
+
+ stride[Bounds::rank - 1] = 1;
+ for (size_t i = 1; i < Bounds::rank; ++i)
+ {
+ stride[Bounds::rank - i - 1] = stride[Bounds::rank - i] * extents[Bounds::rank - i];
+ }
+ return{ stride };
}
template <typename BoundsSrc, typename BoundsDest>
diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp
index 5b06e19..19889c3 100644
--- a/tests/array_view_tests.cpp
+++ b/tests/array_view_tests.cpp
@@ -17,6 +17,7 @@
#include <UnitTest++/UnitTest++.h>
#include <array_view.h>
#include <numeric>
+#include <limits>
#include <array>
#include <string>
#include <vector>
@@ -554,7 +555,7 @@
{
// zero stride
- strided_array_view<int, 1> sav{ av, {{4}, {}} };
+ strided_array_view<int, 1> sav{ av,{ { 4 },{} } };
CHECK(sav[0] == 0);
CHECK(sav[3] == 0);
CHECK_THROW(sav[4], fail_fast);
@@ -562,7 +563,7 @@
{
// zero extent
- strided_array_view<int, 1> sav{ av,{ {},{1} } };
+ strided_array_view<int, 1> sav{ av,{ {},{ 1 } } };
CHECK_THROW(sav[0], fail_fast);
}
@@ -635,27 +636,26 @@
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> 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
-
+
{
- // 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,1,1}} }), fail_fast);
#ifdef _MSC_VER
CHECK_THROW((strided_array_view<int, 2>{ av.as_array_view(dim<2>(), dim<2>()), {{1,1,1}, {1}} }), fail_fast);
#endif
}
-
}
TEST(strided_array_view_type_conversion)
@@ -873,7 +873,18 @@
}
{
- 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);
@@ -989,7 +1000,7 @@
auto bounds = strided_bounds<1>({ length }, { 2 });
#else
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);
CHECK(strided.size() == length);
@@ -1050,7 +1061,7 @@
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]);
}
}
@@ -1168,6 +1179,152 @@
}
+ 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{ std::numeric_limits<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{ std::numeric_limits<int>::max(), 1 };
+ index<2, long long> longlong_index{ big_int_index };
+
+ CHECK(longlong_index[0] == std::numeric_limits<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{ std::numeric_limits<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{ std::numeric_limits<int>::max() };
+ index<1, long long> longlong_index{ big_int_index };
+
+ CHECK(longlong_index[0] == std::numeric_limits<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<int, dynamic_range> av(nullptr);