On Thu, May 29, 2025 at 9:49 AM Tomasz Kaminski <tkami...@redhat.com> wrote:
> > > On Mon, May 26, 2025 at 4:25 PM Luc Grosheintz <luc.groshei...@gmail.com> > wrote: > >> Implements the tests for layout_stride and for the features of the other >> two layouts that depend on layout_stride. >> >> libstdc++-v3/ChangeLog: >> >> * testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc: Add >> tests for layout_stride. >> * testsuite/23_containers/mdspan/layouts/ctors.cc: Add test for >> layout_stride and the interaction with other layouts. >> * testsuite/23_containers/mdspan/layouts/mapping.cc: Ditto. >> * testsuite/23_containers/mdspan/layouts/stride.cc: New test. >> >> Signed-off-by: Luc Grosheintz <luc.groshei...@gmail.com> >> --- >> > LGTM to me, few suggestions > - there are some whitespace changes that should got to previous commit > - removal of the comments regarding differentiation from standard from > is_exhaustive, after we have issue > - one or two additional test cases. > > >> libstdc++-v3/include/std/mdspan | 5 +- >> .../mdspan/layouts/class_mandate_neg.cc | 19 + >> .../23_containers/mdspan/layouts/ctors.cc | 114 ++++ >> .../23_containers/mdspan/layouts/empty.cc | 9 +- >> .../23_containers/mdspan/layouts/mapping.cc | 75 ++- >> .../23_containers/mdspan/layouts/stride.cc | 500 ++++++++++++++++++ >> 6 files changed, 718 insertions(+), 4 deletions(-) >> create mode 100644 >> libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride.cc >> >> diff --git a/libstdc++-v3/include/std/mdspan >> b/libstdc++-v3/include/std/mdspan >> index d5f613a19fd..33ad5070a37 100644 >> --- a/libstdc++-v3/include/std/mdspan >> +++ b/libstdc++-v3/include/std/mdspan >> > I think these changes should go to the previous commit. > >> @@ -792,7 +792,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> else >> { >> auto __impl = [&__m]<size_t... >> _Counts>(index_sequence<_Counts...>) >> - { return __m(((void) _Counts, _IndexType(0))...); }; >> + { return __m(((void) _Counts, _IndexType(0))...); }; >> return __impl(make_index_sequence<__rank>()); >> } >> } >> @@ -890,7 +890,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> extents() const noexcept { return _M_extents; } >> >> constexpr array<index_type, extents_type::rank()> >> - strides() const noexcept { >> + strides() const noexcept >> + { >> array<index_type, extents_type::rank()> __ret; >> for (size_t __i = 0; __i < extents_type::rank(); ++__i) >> __ret[__i] = _M_strides[__i]; >> diff --git >> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc >> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc >> index a41bad988d2..0e39bd3aab0 100644 >> --- >> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc >> +++ >> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc >> @@ -17,7 +17,26 @@ template<typename Layout> >> typename Layout::mapping<extents_type> m3; // { dg-error "required >> from" } >> }; >> >> +template<size_t Count, typename Layout, typename OLayout> >> + struct B // { dg-error "expansion of" } >> + { >> + using Extents = std::extents<uint8_t, dyn, dyn, Count>; >> + using OExtents = std::extents<uint16_t, n, 4, Count>; >> + >> + using Mapping = typename Layout::mapping<Extents>; >> + using OMapping = typename Layout::mapping<OExtents>; >> + >> + Mapping m{OMapping{}}; >> + }; >> + >> A<std::layout_left> a_left; // { dg-error "required >> from" } >> A<std::layout_right> a_right; // { dg-error "required >> from" } >> +A<std::layout_stride> a_stride; // { dg-error "required >> from" } >> + >> +B<1, std::layout_left, std::layout_right> blr; // { dg-error >> "required here" } >> > This check looks a bit suspicious for me, as layout_left cannot be constructed from layout_right for 3 dimensional layouts, so we should not get "must be representable as index_type" errors here. Did you mean checking layout_stride from layout_left? Also, in the layout_stride_cosntructor from any UniqueStridedLayout, we could add message to the static assert: __glibcxx_assert(!cmp_less(numeric_limits<index_type>::max(), __other.required_span_size())) > +B<2, std::layout_left, std::layout_stride> bls; // { dg-error >> "required here" } >> + >> +B<3, std::layout_right, std::layout_left> brl; // { dg-error >> "required here" } >> +B<4, std::layout_right, std::layout_stride> brs; // { dg-error >> "required here" } >> >> // { dg-prune-output "must be representable as index_type" } >> diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc >> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc >> index cc719dfee10..2507eeaf7a1 100644 >> --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc >> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc >> @@ -80,6 +80,20 @@ namespace default_ctor >> VERIFY(m.extents().extent(i) == 0); >> } >> >> + template<typename Mapping> >> + constexpr void >> + verify_default_stride(Mapping m, size_t i) >> + { >> + using Layout = typename Mapping::layout_type; >> + using Extents = typename Mapping::extents_type; >> + >> + if constexpr (std::is_same_v<Layout, std::layout_stride>) >> + { >> + std::layout_right::mapping<Extents> mr; >> + VERIFY(m.stride(i) == mr.stride(i)); >> + } >> + } >> + >> template<typename Layout, typename Extents> >> constexpr void >> test_default_ctor() >> @@ -91,6 +105,7 @@ namespace default_ctor >> for(size_t i = 0; i < Extents::rank(); ++i) >> { >> verify_default_extent(m, i); >> + verify_default_stride(m, i); >> } >> } >> >> @@ -329,6 +344,104 @@ namespace from_left_or_right >> } >> } >> >> +// ctor: mapping(layout_stride::mapping<OExtents>) >> +namespace from_stride >> +{ >> + template<typename Mapping> >> + constexpr auto >> + strides(Mapping m) >> + { >> + constexpr auto rank = Mapping::extents_type::rank(); >> + std::array<typename Mapping::index_type, rank> s; >> + >> + if constexpr (rank > 0) >> + for(size_t i = 0; i < rank; ++i) >> + s[i] = m.stride(i); >> + return s; >> + } >> + >> + template<typename Layout, typename Extents, typename OExtents> >> + constexpr void >> + verify_convertible(OExtents oexts) >> + { >> + using Mapping = typename Layout::mapping<Extents>; >> + using OMapping = std::layout_stride::mapping<OExtents>; >> + >> + constexpr auto other = OMapping(oexts, >> strides(Mapping(Extents(oexts)))); >> + if constexpr (std::is_same_v<Layout, std::layout_right>) >> + ::verify_nothrow_convertible<Mapping>(other); >> + else >> + ::verify_convertible<Mapping>(other); >> + } >> + >> + template<typename Layout, typename Extents, typename OExtents> >> + constexpr void >> + verify_constructible(OExtents oexts) >> + { >> + using Mapping = typename Layout::mapping<Extents>; >> + using OMapping = std::layout_stride::mapping<OExtents>; >> + >> + constexpr auto other = OMapping(oexts, >> strides(Mapping(Extents(oexts)))); >> + if constexpr (std::is_same_v<Layout, std::layout_right>) >> + ::verify_nothrow_constructible<Mapping>(other); >> + else >> + ::verify_constructible<Mapping>(other); >> + } >> + >> + template<typename Layout> >> + constexpr bool >> + test_ctor() >> + { >> + assert_not_constructible< >> + typename Layout::mapping<std::extents<int>>, >> + std::layout_stride::mapping<std::extents<int, 1>>>(); >> + >> + assert_not_constructible< >> + typename Layout::mapping<std::extents<int, 1>>, >> + std::layout_stride::mapping<std::extents<int>>>(); >> + >> + assert_not_constructible< >> + typename Layout::mapping<std::extents<int, 2>>, >> + std::layout_stride::mapping<std::extents<int, 1>>>(); >> + >> + verify_convertible<Layout, std::extents<int>>(std::extents<int>{}); >> + >> + verify_convertible<Layout, std::extents<unsigned int>>( >> + std::extents<int>{}); >> + >> + // Rank == 0 doesn't check IndexType for convertibility. >> + verify_convertible<Layout, std::extents<int>>( >> + std::extents<unsigned int>{}); >> + >> + verify_constructible<Layout, std::extents<int, 3>>( >> + std::extents<int, 3>{}); >> + >> + verify_constructible<Layout, std::extents<unsigned int, 3>>( >> + std::extents<int, 3>{}); >> + >> + verify_constructible<Layout, std::extents<int, 3>>( >> + std::extents<unsigned int, 3>{}); >> + >> + verify_constructible<Layout, std::extents<int, 3, 5>>( >> + std::extents<int, 3, 5>{}); >> + >> + verify_constructible<Layout, std::extents<unsigned int, 3, 5>>( >> + std::extents<int, 3, 5>{}); >> + >> + verify_constructible<Layout, std::extents<int, 3, 5>>( >> + std::extents<unsigned int, 3, 5>{}); >> + return true; >> + } >> + >> + template<typename Layout> >> + constexpr void >> + test_all() >> + { >> + test_ctor<Layout>(); >> + static_assert(test_ctor<Layout>()); >> + } >> +} >> + >> template<typename Layout> >> constexpr void >> test_all() >> @@ -336,6 +449,7 @@ template<typename Layout> >> default_ctor::test_all<Layout>(); >> from_extents::test_all<Layout>(); >> from_same_layout::test_all<Layout>(); >> + from_stride::test_all<Layout>(); >> > } >> >> int >> > > >> diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc >> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc >> index e95eacd80b6..d2fd5bb859e 100644 >> --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc >> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc >> @@ -14,7 +14,13 @@ template<typename Mapping> >> constexpr size_t rank = Mapping::extents_type::rank(); >> for(size_t i = 0; i < rank; ++i) >> { >> - m.stride(i); >> + if constexpr (std::same_as<Layout, std::layout_stride>) >> + { >> + auto mr = std::layout_right::mapping(m.extents()); >> + VERIFY(m.stride(i) == mr.stride(i)); >> + } >> + else >> + m.stride(i); >> > I think I would keep m.stride(i) here. The layouts are empty anyway, so > the stride values are not that meaningful. > And keeping the test simpler seems valuable. I would test that in > test_ctor_default_stride_all in stride.cc > >> } >> } >> >> @@ -67,5 +73,6 @@ main() >> { >> static_assert(test_all<std::layout_left>()); >> static_assert(test_all<std::layout_right>()); >> + static_assert(test_all<std::layout_stride>()); >> return 0; >> } >> diff --git >> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc >> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc >> index 40a0c828cc4..c5f8ef3a18d 100644 >> --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc >> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc >> @@ -12,6 +12,7 @@ template<typename Layout, typename Extents> >> { >> using M = typename Layout::mapping<Extents>; >> static_assert(std::__mdspan::__is_extents<typename M::extents_type>); >> + static_assert(std::__mdspan::__mapping_like<M>); >> static_assert(std::copyable<M>); >> static_assert(std::is_nothrow_move_constructible_v<M>); >> static_assert(std::is_nothrow_move_assignable_v<M>); >> @@ -30,6 +31,8 @@ template<typename Layout, typename Extents> >> >> static_assert(M::is_always_unique() && M::is_unique()); >> static_assert(M::is_always_strided() && M::is_strided()); >> + if constexpr (!std::is_same_v<Layout, std::layout_stride>) >> + static_assert(M::is_always_exhaustive() && M::is_exhaustive()); >> return true; >> } >> >> @@ -105,6 +108,39 @@ template<typename Layout> >> { return exts; } >> }; >> >> +template<> >> + struct MappingFactory<std::layout_stride> >> + { >> + template<typename Extents> >> + static constexpr std::layout_stride::mapping<Extents> >> + create(Extents exts) >> + { >> + if constexpr (Extents::rank() == 0) >> + { >> + auto strides = std::array<size_t, 0>{}; >> + return std::layout_stride::mapping(exts, strides); >> + } >> + else if constexpr (Extents::rank() == 1) >> + { >> + auto strides = std::array<size_t, 1>{2}; >> + return std::layout_stride::mapping(exts, strides); >> + } >> + else if constexpr (Extents::rank() == 2) >> + { >> + size_t m = exts.extent(1); >> + auto strides = std::array<size_t, 2>{3*m, 2}; >> + return std::layout_stride::mapping(exts, strides); >> + } >> + else if constexpr (Extents::rank() == 3) >> + { >> + size_t n = exts.extent(0); >> + size_t m = exts.extent(1); >> + auto strides = std::array<size_t, 3>{3*m, 2, 11*m*n}; >> + return std::layout_stride::mapping(exts, strides); >> + } >> + } >> + }; >> + >> template<typename Layout> >> constexpr void >> test_linear_index_3d() >> @@ -280,6 +316,16 @@ template<typename Layout> >> VERIFY(m.stride(0) == 1); >> } >> >> +template<> >> + constexpr void >> + test_stride_1d<std::layout_stride>() >> + { >> + std::array<int, 1> strides{13}; >> + std::layout_stride::mapping m(std::extents<int, 3>{}, strides); >> + VERIFY(m.stride(0) == strides[0]); >> + VERIFY(m.strides() == strides); >> + } >> + >> template<typename Layout> >> constexpr void >> test_stride_2d(); >> @@ -302,6 +348,17 @@ template<> >> VERIFY(m.stride(1) == 1); >> } >> >> +template<> >> + constexpr void >> + test_stride_2d<std::layout_stride>() >> + { >> + std::array<int, 2> strides{13, 2}; >> + std::layout_stride::mapping m(std::extents<int, 3, 5>{}, strides); >> + VERIFY(m.stride(0) == strides[0]); >> + VERIFY(m.stride(1) == strides[1]); >> + VERIFY(m.strides() == strides); >> + } >> + >> template<typename Layout> >> constexpr void >> test_stride_3d(); >> @@ -326,6 +383,19 @@ template<> >> VERIFY(m.stride(2) == 1); >> } >> >> +template<> >> + constexpr void >> + test_stride_3d<std::layout_stride>() >> + { >> + std::dextents<int, 3> exts(3, 5, 7); >> + std::array<int, 3> strides{11, 2, 41}; >> + std::layout_stride::mapping<std::dextents<int, 3>> m(exts, strides); >> + VERIFY(m.stride(0) == strides[0]); >> + VERIFY(m.stride(1) == strides[1]); >> + VERIFY(m.stride(2) == strides[2]); >> + VERIFY(m.strides() == strides); >> + } >> + >> template<typename Layout> >> constexpr bool >> test_stride_all() >> @@ -347,7 +417,7 @@ template<typename Layout> >> test_has_stride_0d() >> { >> using Mapping = typename Layout::mapping<std::extents<int>>; >> - constexpr bool expected = false; >> + constexpr bool expected = std::is_same_v<Layout, std::layout_stride>; >> > I see now why we have "expected" here. > >> static_assert(has_stride<Mapping> == expected); >> } >> >> @@ -488,8 +558,11 @@ main() >> { >> test_all<std::layout_left>(); >> test_all<std::layout_right>(); >> + test_all<std::layout_stride>(); >> >> test_has_op_eq<std::layout_right, std::layout_left, false>(); >> + test_has_op_eq<std::layout_right, std::layout_stride, true>(); >> + test_has_op_eq<std::layout_left, std::layout_stride, true>(); >> test_has_op_eq_peculiar(); >> return 0; >> } >> diff --git >> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride.cc >> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride.cc >> new file mode 100644 >> index 00000000000..03f5f95593c >> --- /dev/null >> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride.cc >> @@ -0,0 +1,500 @@ >> +// { dg-do run { target c++23 } } >> +#include <mdspan> >> + >> +#include <testsuite_hooks.h> >> + >> +constexpr size_t dyn = std::dynamic_extent; >> + >> +template<typename MappingStride> >> + constexpr void >> + test_ctor_default_stride() >> + { >> + using Extents = typename MappingStride::extents_type; >> + MappingStride actual; >> + typename std::layout_right::mapping<Extents> expected; >> + >> + constexpr auto rank = MappingStride::extents_type::rank(); >> + if constexpr (rank > 0) >> + for(size_t i = 0; i < rank; ++i) >> + VERIFY(actual.stride(i) == expected.stride(i)); >> + } >> + >> +constexpr bool >> +test_ctor_default_stride_all() >> +{ >> + using M1 = std::layout_stride::mapping<std::extents<int, 3>>; >> + test_ctor_default_stride<M1>(); >> + >> + using M2 = std::layout_stride::mapping<std::extents<int, 3, 5, 7>>; >> + test_ctor_default_stride<M2>(); >> + >> + using M3 = std::layout_stride::mapping<std::dextents<int, 3>>; >> + test_ctor_default_stride<M3>(); >> + return true; >> +} >> + >> +struct IntLikeA >> +{ >> + operator int() >> + { return 0; } >> +}; >> + >> +struct IntLikeB >> +{ >> + operator int() noexcept >> + { return 0; } >> +}; >> + >> +struct NotIntLike >> +{ }; >> + >> +template<typename E, typename E_arg, typename T, size_t N, bool Expected> >> +constexpr void >> +test_stride_constructible() >> +{ >> + static_assert(std::is_nothrow_constructible_v< >> + std::layout_stride::mapping<E>, E_arg, std::span<T, N>> == >> Expected); >> + static_assert(std::is_nothrow_constructible_v< >> + std::layout_stride::mapping<E>, E_arg, std::array<T, N>> == >> Expected); >> + static_assert(!std::is_constructible_v<std::layout_stride::mapping<E>, >> + E_arg>); >> +} >> + >> +constexpr void >> +test_stride_constructible_all() >> +{ >> + using E0 = std::extents<int>; >> + using E1 = std::extents<int, 2>; >> + using E2 = std::extents<int, dyn>; >> + >> + test_stride_constructible<E0, E0, int, 0, true>(); >> + test_stride_constructible<E0, E0, IntLikeA, 0, false>(); >> + test_stride_constructible<E0, E0, IntLikeB, 0, true>(); >> + test_stride_constructible<E0, E0, NotIntLike, 0, false>(); >> + test_stride_constructible<E1, E1, int, 1, true>(); >> + test_stride_constructible<E2, E1, int, 1, true>(); >> + test_stride_constructible<E1, E1, int, 2, false>(); >> + test_stride_constructible<E1, E0, int, 1, false>(); >> +} >> + >> +template<typename Extents, typename Shape> >> + constexpr void >> + test_ctor_shape_strides(Extents exts, Shape strides) >> + { >> + using M = std::layout_stride::mapping<Extents>; >> + M m(exts, strides); >> + >> + if constexpr (Extents::rank() > 0) >> + for(size_t i = 0; i < exts.rank(); ++i) >> + { >> + VERIFY(m.stride(i) == strides[i]); >> + VERIFY(m.extents().extent(i) == exts.extent(i)); >> + } >> + } >> + >> +constexpr bool >> +test_ctor_shape_stride_all() >> +{ >> + test_ctor_shape_strides(std::extents<int>{}, std::array<int, 0>{}); >> + test_ctor_shape_strides(std::extents<int, 2>{}, std::array<int, 1>{3}); >> + test_ctor_shape_strides(std::extents<int, 2, 4, 6>{}, >> + std::array<int, 3>{20, 5, 45}); >> + return true; >> +} >> + >> +template<typename Extents, std::array<bool, 2> Strided, >> + std::array<bool, 2> Unique, std::array<bool, 2> Exhautive, >> + typename Extents::index_type Offset = 0> >> + struct MappingLike >> + { >> + using extents_type = Extents; >> + using index_type = typename Extents::index_type; >> + >> + static constexpr bool >> + is_always_strided() requires (Strided[0]) >> + { return Strided[1]; } >> + >> + static constexpr bool >> + is_always_unique() requires (Unique[0]) >> + { return Unique[1]; } >> + >> + static constexpr bool >> + is_always_exhaustive() requires (Exhautive[0]) >> + { return Exhautive[1]; } >> + >> + constexpr Extents >> + extents() const { return _extents; } >> + >> + constexpr index_type >> + stride(size_t i) const { return _strides[i]; } >> + >> + template<typename... Indices> >> + constexpr index_type >> + operator()(Indices... indices) const >> + { >> + std::array<index_type, Extents::rank()> ind_arr{indices...}; >> + index_type ret = Offset; >> + for(size_t i = 0; i < Extents::rank(); ++i) >> + ret += ind_arr[i]*_strides[i]; >> + return ret; >> + } >> + >> + Extents _extents; >> + std::array<index_type, Extents::rank()> _strides; >> + }; >> + >> + >> +template<size_t Rank> >> +struct ExtentLike >> +{ >> + using index_type = int; >> + >> + static constexpr size_t >> + rank() { return Rank; } >> +}; >> > + >> + >> +template<typename E1> >> +constexpr void >> +test_mapping_like_constructible() >> +{ >> + using M = std::layout_stride::mapping<E1>; >> + using E2 = std::dextents<typename E1::index_type, E1::rank()>; >> + using E3 = std::dextents<typename E1::index_type, E1::rank() + 1>; >> + using E4 = ExtentLike<E1::rank()>; >> + >> + constexpr auto TT = std::array{true, true}; >> + constexpr auto FT = std::array{false, true}; >> + constexpr auto TF = std::array{true, false}; >> + >> + static_assert(std::is_constructible_v<M, MappingLike<E1, TT, TT, TT>>); >> + static_assert(std::is_constructible_v<M, MappingLike<E2, TT, TT, TT>>); >> + static_assert(!std::is_constructible_v<M, MappingLike<E3, TT, TT, >> TT>>); >> + static_assert(!std::is_constructible_v<M, MappingLike<E1, FT, TT, >> TT>>); >> + static_assert(!std::is_constructible_v<M, MappingLike<E1, TF, TT, >> TT>>); >> + static_assert(!std::is_constructible_v<M, MappingLike<E1, TT, FT, >> TT>>); >> + static_assert(!std::is_constructible_v<M, MappingLike<E1, TT, TF, >> TT>>); >> + static_assert(!std::is_constructible_v<M, MappingLike<E1, TT, TT, >> FT>>); >> > Could you add: > static_assert(!std::is_constructible_v<M, MappingLike<E1, TT, TT, TF>>); > >> + static_assert(!std::is_constructible_v<M, MappingLike<E4, TT, TT, >> TT>>); >> +} >> + >> +constexpr void >> +test_mapping_like_constructible_all() >> +{ >> + test_mapping_like_constructible<std::extents<int>>(); >> + test_mapping_like_constructible<std::extents<int, 2>>(); >> + test_mapping_like_constructible<std::extents<int, 2, 3>>(); >> +} >> + >> +template<typename E1, typename E2> >> +constexpr void >> +test_mapping_like_convertible() >> +{ >> + using M1 = std::layout_stride::mapping<E1>; >> + using M2 = std::layout_stride::mapping<E2>; >> + constexpr auto TT = std::array{true, true}; >> + >> + static_assert(!std::is_convertible_v<MappingLike<E1, TT, TT, TT>, M1>); >> + static_assert(!std::is_convertible_v<MappingLike<E2, TT, TT, TT>, M1>); >> + static_assert(!std::is_convertible_v<MappingLike<E1, TT, TT, TT>, M2>); >> + >> + static_assert(std::is_convertible_v<std::layout_stride::mapping<E2>, >> M1>); >> + static_assert(std::is_convertible_v<std::layout_left::mapping<E2>, >> M1>); >> + static_assert(std::is_convertible_v<std::layout_right::mapping<E2>, >> M1>); >> + >> + static_assert(!std::is_convertible_v<std::layout_stride::mapping<E1>, >> M2>); >> + static_assert(!std::is_convertible_v<std::layout_left::mapping<E1>, >> M2>); >> + static_assert(!std::is_convertible_v<std::layout_right::mapping<E1>, >> M2>); >> +} >> + >> +constexpr void >> +test_mapping_like_convertible_all() >> +{ >> + test_mapping_like_convertible<std::extents<unsigned int>, >> + std::extents<int>>(); >> + test_mapping_like_convertible<std::extents<unsigned int, 2>, >> + std::extents<int, 2>>(); >> + test_mapping_like_convertible<std::extents<int, dyn, 3>, >> + std::extents<int, 2, 3>>(); >> +} >> + >> +template<typename Extents> >> +constexpr void >> +test_ctor_stride_like(Extents exts, std::array<int, Extents::rank()> >> strides) >> +{ >> + auto other_right = std::layout_right::mapping(exts); >> + auto other_left = std::layout_left::mapping(exts); >> + auto other_stride = std::layout_stride::mapping(exts, strides); >> + >> + VERIFY(std::layout_stride::mapping<Extents>(other_right) == >> other_right); >> + VERIFY(std::layout_stride::mapping<Extents>(other_left) == other_left); >> + VERIFY(std::layout_stride::mapping<Extents>(other_stride) == >> other_stride); >> +} >> + >> +constexpr void >> +test_ctor_stride_like_all() >> +{ >> + using E1 = std::extents<int>; >> + auto s1 = std::array<int, 0>{}; >> + test_ctor_stride_like(E1{}, s1); >> + >> + using E2 = std::extents<int, 3>; >> + auto s2 = std::array<int, 1>{2}; >> + test_ctor_stride_like(E2{}, s2); >> + >> + using E3 = std::extents<int, 3, 5, 7>; >> + auto s3 = std::array<int, 3>{5, 1, 15}; >> + test_ctor_stride_like(E3{}, s3); >> +} >> + >> +constexpr bool >> +test_ctor_strides_all() >> +{ >> + test_ctor_default_stride_all(); >> + test_ctor_shape_stride_all(); >> + test_ctor_stride_like_all(); >> + return true; >> +} >> + >> +// Check is_exhaustive. >> +template<typename Extents, typename Strides> >> + constexpr void >> + test_is_exhaustive(Extents extents, Strides strides, bool expected) >> + { >> + std::layout_stride::mapping<Extents> m(extents, strides); >> + VERIFY(m.is_exhaustive() == expected); >> + >> + bool always_exhaustive = m.required_span_size() == 0; >> + VERIFY(m.is_always_exhaustive() == always_exhaustive); >> + } >> + >> +constexpr void >> +test_is_exhaustive_zero_1d() >> +{ >> + std::extents<int, 0> extents; >> + test_is_exhaustive(extents, std::array{1}, true); >> + >> + // Another case of exhaustive, but the formula prescribed by the >> standard >> + // doesn't allow recognizing it as exhaustive. (However, the >> implementation >> + // does.) >> > Again remove the comment. > >> + test_is_exhaustive(extents, std::array{2}, true); >> +} >> + >> +constexpr void >> +test_is_exhaustive_zero_3d() >> +{ >> + std::extents<int, 3, 0, 7> extents; >> + >> + // This is exhaustive, and the current implementation recognizes it as >> such, >> + // but the standard requires returning `false`. >> > Again remove them. > >> + test_is_exhaustive(extents, std::array{1, 1, 1}, true); >> + test_is_exhaustive(extents, std::array{1, 2*21, 2*3}, true); >> + test_is_exhaustive(extents, std::array{7, 2*21, 1}, true); >> + >> + // Technically invalid, but the same mapping object can be constructed >> via >> + // the default ctor (which uses layout_right to create the zeros in the >> + // strides). This case is unabiguously exhaustive. >> + test_is_exhaustive(extents, std::array{0, 1, 1}, true); >> > I would remove this test, because if we all preconditions it will start > failing. > >> + >> + // Unabiguously exhaustive. >> + test_is_exhaustive(extents, std::array{1, 21, 3}, true); >> + test_is_exhaustive(extents, std::array{7, 21, 1}, true); >> +} >> + >> +constexpr void >> +test_is_exhaustive_0d() >> +{ >> + std::extents<int> extents; >> + test_is_exhaustive(extents, std::array<int, 0>{}, true); >> +} >> + >> +constexpr void >> +test_is_exhaustive_1d() >> +{ >> + std::extents<int, 3> extents; >> + test_is_exhaustive(extents, std::array{1}, true); >> + test_is_exhaustive(extents, std::array{3}, false); >> +} >> + >> + >> +constexpr void >> +test_is_exhaustive_3d() >> +{ >> + std::extents<int, 3, dyn, 7> extents(5); >> + >> + test_is_exhaustive(extents, std::array{1, 3, 3*5}, true); >> + test_is_exhaustive(extents, std::array{5*7, 1, 5}, true); >> + test_is_exhaustive(extents, std::array{7, 3*7, 1}, true); >> + >> + test_is_exhaustive(extents, std::array{1, 3, 2*3*5}, false); >> + test_is_exhaustive(extents, std::array{2*5*7, 1, 2*5}, false); >> + test_is_exhaustive(extents, std::array{2*7, 2*3*7, 2}, false); >> +} >> + >> +constexpr void >> +test_is_exhaustive_ones() >> +{ >> + std::extents<int, 1, 1, 3, 1> extents; >> + test_is_exhaustive(extents, std::array{1, 1, 1, 1}, true); >> + test_is_exhaustive(extents, std::array{1, 1, 1, 3}, true); >> + test_is_exhaustive(extents, std::array{3, 3, 1, 3}, true); >> + test_is_exhaustive(extents, std::array{3, 1, 1, 3}, true); >> +} >> + >> +constexpr bool >> +test_is_exhaustive_all() >> +{ >> + test_is_exhaustive_zero_1d(); >> + test_is_exhaustive_zero_3d(); >> + test_is_exhaustive_ones(); >> + test_is_exhaustive_0d(); >> + test_is_exhaustive_1d(); >> + test_is_exhaustive_3d(); >> + return true; >> +} >> + >> +template<typename Extents, int Offset> >> + using OffsetMapping = MappingLike<Extents, {true, true}, {true, true}, >> + {true, false}, Offset>; >> + >> +template<typename Extents> >> + constexpr void >> + test_eq(Extents exts, >> + std::array<typename Extents::index_type, Extents::rank()> >> left_strides, >> + std::array<typename Extents::index_type, Extents::rank()> >> right_strides, >> + std::array<typename Extents::index_type, Extents::rank()> >> padded_strides) >> + { >> + using DExtents = std::dextents<int, Extents::rank()>; >> + >> + std::layout_left::mapping<Extents> ml; >> + std::layout_right::mapping<DExtents> mr(exts); >> + >> + std::layout_stride::mapping<Extents> msd; >> + std::layout_stride::mapping<Extents> msl(exts, left_strides); >> + std::layout_stride::mapping<Extents> msr(exts, right_strides); >> + std::layout_stride::mapping<Extents> msp(exts, padded_strides); >> + >> + OffsetMapping<Extents, 0> mor{exts, right_strides}; >> + OffsetMapping<Extents, 0> mol{exts, left_strides}; >> + OffsetMapping<Extents, 0> mop{exts, padded_strides}; >> + OffsetMapping<Extents, 1> moo{exts, right_strides}; >> + >> + VERIFY(msd == mr); >> + VERIFY(msd == mor); >> + VERIFY(msd != msp); >> + VERIFY(msd != mop); >> + >> + VERIFY(msl == ml); >> + VERIFY(msl == mol); >> + VERIFY(msd != msp); >> + VERIFY(msl != mop); >> + >> + VERIFY(msp == mop); >> + VERIFY(msp != ml); >> + VERIFY(msp != mr); >> + >> + VERIFY(msd != moo); >> + } >> + >> +constexpr void >> +test_eq_0d() >> +{ >> + using Extents = std::extents<int>; >> + Extents exts; >> + std::layout_left::mapping<Extents> ml; >> + std::layout_right::mapping<Extents> mr; >> + std::layout_stride::mapping<Extents> ms; >> + OffsetMapping<Extents, 0> mor{exts, {}}; >> + OffsetMapping<Extents, 1> moo{exts, {}}; >> + >> + VERIFY(ms == ml); >> + VERIFY(ms == mr); >> + VERIFY(ms == mor); >> + VERIFY(ms != moo); >> +} >> + >> +constexpr void >> +test_eq_1d() >> +{ >> + using Extents = std::extents<int, 2>; >> + auto exhaustive_strides = std::array{1}; >> + auto padded_strides = std::array{2}; >> + >> + test_eq(Extents{}, exhaustive_strides, exhaustive_strides, >> padded_strides); >> +} >> + >> +constexpr void >> +test_eq_2d() >> +{ >> + using Extents = std::extents<int, 1, 2>; >> + auto left_strides = std::array{1, 1}; >> + auto right_strides = std::array{2, 1}; >> + auto padded_strides = std::array{2, 8}; >> + >> + test_eq(Extents{}, left_strides, right_strides, padded_strides); >> +} >> + >> +constexpr void >> +test_eq_zero() >> +{ >> + using Mapping = std::layout_stride::mapping<std::extents<int, 0, 2>>; >> + Mapping m1(std::extents<int, 0, 2>{}, std::array<int, 2>{1, 5}); >> + Mapping m2(std::extents<int, 0, 2>{}, std::array<int, 2>{1, 5}); >> + Mapping m3(std::extents<int, 0, 2>{}, std::array<int, 2>{5, 1}); >> + >> + VERIFY(m1 == m2); >> + VERIFY(m1 != m3); >> +} >> + >> +constexpr bool >> +test_eq_all() >> +{ >> + test_eq_0d(); >> + test_eq_1d(); >> + test_eq_2d(); >> + test_eq_zero(); >> + return true; >> +} >> + >> +template<typename M1, typename M2> >> + concept has_op_eq = requires (M1 m1, M2 m2) >> + { >> + { m1 == m2 } -> std::same_as<bool>; >> + { m2 == m1 } -> std::same_as<bool>; >> + { m1 != m2 } -> std::same_as<bool>; >> + { m2 != m1 } -> std::same_as<bool>; >> + }; >> + >> +constexpr void >> +test_has_op_eq() >> +{ >> + using E1 = std::extents<int>; >> + using E2 = std::extents<int, 2>; >> + using E3 = std::extents<int, 1, 2>; >> + constexpr auto FT = std::array{false, true}; >> + >> + static_assert(!has_op_eq< >> + std::layout_stride::mapping<E1>, MappingLike<E1, FT, FT, FT>>); >> + >> + static_assert(!has_op_eq< >> + std::layout_stride::mapping<E2>, MappingLike<E2, FT, FT, FT>>); >> + >> + static_assert(!has_op_eq< >> + std::layout_stride::mapping<E3>, MappingLike<E3, FT, FT, FT>>); >> +} >> + >> +int >> +main() >> +{ >> + test_ctor_strides_all(); >> + static_assert(test_ctor_strides_all()); >> + test_mapping_like_convertible_all(); >> + test_mapping_like_constructible_all(); >> + test_stride_constructible_all(); >> + test_is_exhaustive_all(); >> + static_assert(test_is_exhaustive_all()); >> + test_eq_all(); >> + static_assert(test_eq_all()); >> + test_has_op_eq(); >> + return 0; >> +} >> -- >> 2.49.0 >> >>