Implements the tests for layout_stride and for the features of the other two layouts that depend on layout_stride.
libstdc++/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> --- .../mdspan/layouts/class_mandate_neg.cc | 1 + .../23_containers/mdspan/layouts/ctors.cc | 79 ++++ .../23_containers/mdspan/layouts/mapping.cc | 42 +- .../23_containers/mdspan/layouts/stride.cc | 359 ++++++++++++++++++ 4 files changed, 480 insertions(+), 1 deletion(-) create mode 100644 libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride.cc 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 fdebda8bd06..f9fa6212d4d 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 @@ -18,5 +18,6 @@ template<typename Layout> 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" } // { dg-prune-output "not 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 5c54b440083..fc479ae596e 100644 --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc @@ -202,12 +202,91 @@ namespace from_left_or_right } } +// ctor: mapping(layout_stride::mapping<OExtents>) +namespace from_stride +{ + template<typename Layout> + constexpr void + test_constructible() + { + static_assert(!std::is_constructible_v< + typename Layout::mapping<std::extents<int, 1>>, + std::layout_stride::mapping<std::extents<int>>>); + + static_assert(!std::is_constructible_v< + typename Layout::mapping<std::extents<int, 2>>, + std::layout_stride::mapping<std::extents<int, 1>>>); + + static_assert(std::is_constructible_v< + typename Layout::mapping<std::extents<int, 1, 2>>, + std::layout_stride::mapping<std::extents<int, 1, 2>>>); + } + + template<typename Layout> + constexpr void + test_convertible() + { + static_assert(!std::is_convertible_v< + std::layout_stride::mapping<std::extents<int, 1>>, + typename Layout::mapping<std::extents<int, 1>>>); + + static_assert(std::is_convertible_v< + std::layout_stride::mapping<std::extents<int>>, + typename Layout::mapping<std::extents<int>>>); + } + + template<typename Mapping> + constexpr std::array<typename Mapping::index_type, + Mapping::extents_type::rank()> + strides(Mapping m) + { + std::array<typename Mapping::index_type, Mapping::extents_type::rank()> s; + if constexpr (Mapping::extents_type::rank() > 0) + for(size_t i = 0; i < Mapping::extents_type::rank(); ++i) + s[i] = m.stride(i); + return s; + } + + template<typename Layout, typename Extents> + constexpr bool + test_ctor_from_stride(Extents exts) + { + typename Layout::mapping<Extents> m(exts); + std::layout_stride::mapping<Extents> m1(exts, strides(m)); + typename Layout::mapping<Extents> m2(m1); + VERIFY(m1.extents() == m2.extents()); + return true; + } + + template<typename Layout> + constexpr bool + test_ctor_from_stride_all() + { + test_ctor_from_stride<Layout>(std::extents<int>{}); + test_ctor_from_stride<Layout>(std::extents<int, 1>{}); + test_ctor_from_stride<Layout>(std::extents<int, dyn, dyn, dyn>{3, 5, 7}); + return true; + } + + template<typename Layout> + constexpr void + test_all() + { + test_constructible<Layout>(); + test_convertible<Layout>(); + + test_ctor_from_stride_all<Layout>(); + static_assert(test_ctor_from_stride_all<Layout>()); + } +} + template<typename Layout> constexpr void test_all() { 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/mapping.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc index f59dd75d01f..affa1be56d0 100644 --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc @@ -11,6 +11,7 @@ template<typename Layout, typename Extent> { using M = typename Layout::mapping<Extent>; 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>); @@ -28,6 +29,8 @@ template<typename Layout, typename Extent> 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; } @@ -94,6 +97,20 @@ template<typename Layout> { return Mapping(exts); } }; +template<> + struct Mapping3dFactory<std::layout_stride> + { + using Mapping = std::layout_stride::mapping<std::dextents<size_t, 3>>; + + static constexpr Mapping + create(std::dextents<size_t, 3> exts) + { + size_t n = exts.extent(0); + size_t m = exts.extent(1); + return Mapping(exts, std::array<size_t, 3>{2*m, 1, 11*m*n}); + } + }; + template<typename Layout> constexpr void test_linear_index_3d() @@ -248,6 +265,16 @@ 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]); + } + template<typename Layout> constexpr void test_stride_3d(); @@ -272,6 +299,18 @@ 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]); + } + template<typename Layout> constexpr bool test_stride_all() @@ -292,7 +331,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>; static_assert(has_stride<Mapping> == expected); } @@ -387,5 +426,6 @@ main() { test_all<std::layout_left>(); test_all<std::layout_right>(); + test_all<std::layout_stride>(); 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..158c9bea954 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride.cc @@ -0,0 +1,359 @@ +// { 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, 5, 7>>; + test_ctor_default_stride<M1>(); + + using M2 = std::layout_stride::mapping<std::extents<int>>; + test_ctor_default_stride<M2>(); + 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_constructible_v< + std::layout_stride::mapping<E>, E_arg, std::span<T, N>> == Expected); + static_assert(std::is_constructible_v< + std::layout_stride::mapping<E>, E_arg, std::array<T, N>> == Expected); +} + +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, 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; + }; + + +struct ExtentLike +{ + using index_type = int; + + static constexpr size_t + rank() { return 1; } +}; + + +constexpr void +test_mapping_like_constructible() +{ + using E1 = std::extents<int, 2>; + using E2 = std::extents<int, 3>; + using E3 = ExtentLike; + using M = std::layout_stride::mapping<E1>; + + 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<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>>); + static_assert(!std::is_constructible_v<M, MappingLike<E2, TT, TT, TT>>); +} + +constexpr void +test_mapping_like_convertible() +{ + using E1 = std::extents<int, 2>; + using E2 = std::extents<short, 2>; + using E3 = std::extents<int, dyn>; + using M = std::layout_stride::mapping<E1>; + constexpr auto TT = std::array{true, true}; + + static_assert(!std::is_convertible_v<MappingLike<E1, TT, TT, TT>, M>); + static_assert(std::is_convertible_v<std::layout_stride::mapping<E2>, M>); + static_assert(!std::is_convertible_v<std::layout_stride::mapping<E3>, M>); + static_assert(std::is_convertible_v<std::layout_left::mapping<E2>, M>); + static_assert(!std::is_convertible_v<std::layout_left::mapping<E3>, M>); + static_assert(std::is_convertible_v<std::layout_right::mapping<E2>, M>); + static_assert(!std::is_convertible_v<std::layout_right::mapping<E3>, M>); +} + +template<class OtherMapping> +constexpr void +test_ctor_stride_like(OtherMapping other) +{ + using E = typename OtherMapping::extents_type; + + auto strided = std::layout_stride::mapping<E>(other); + VERIFY(strided == other); +} + +constexpr void +test_ctor_stride_like_all() +{ + using E = std::extents<int, 3, 5, 7>; + auto strides = std::array<int, 3>{5, 1, 15}; + + test_ctor_stride_like(std::layout_left::mapping<E>{}); + test_ctor_stride_like(std::layout_right::mapping<E>{}); + test_ctor_stride_like(std::layout_stride::mapping<E>(E{}, strides)); +} + +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); + } + +constexpr void +test_is_exhaustive_zero() +{ + std::extents<int, 3, 0, 7> extents; + + // This is exhaustive, and the current implementation recognizes it as such, + // but the standard requires returning `false`. + test_is_exhaustive(extents, std::array{1, 1, 1}, true); + + // Unabiguously, exhaustive. + test_is_exhaustive(extents, std::array{0, 1, 0}, true); + test_is_exhaustive(extents, std::array{1, 3, 0}, true); + 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_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); + + // Shuffled variations of the above + test_is_exhaustive(extents, std::array{3, 1, 3*5}, false); + test_is_exhaustive(extents, std::array{5*7, 5, 1}, false); + test_is_exhaustive(extents, std::array{1, 7, 3*7}, 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(); + test_is_exhaustive_ones(); + test_is_exhaustive_3d(); + return true; +} + +template<int Offset> +using OffsetMapping = MappingLike<std::extents<int, 1, 2>, {true, true}, + {true, true}, {true, false}, Offset>; + +constexpr bool +test_eq() +{ + using Extents = std::extents<int, 1, 2>; + std::layout_stride::mapping<Extents> m1; + std::layout_left::mapping<Extents> m2; + std::layout_right::mapping<std::dextents<int, 2>> m3(m1.extents()); + std::layout_stride::mapping<Extents> m4(Extents{}, std::array{1, 1}); + std::layout_stride::mapping<Extents> m5(Extents{}, std::array{2, 1}); + OffsetMapping<0> m6{Extents{}, std::array{2, 1}}; + OffsetMapping<1> m7{Extents{}, std::array{2, 1}}; + + VERIFY(m1 != m2); + VERIFY(m2 != m1); + VERIFY(m1 == m3); + VERIFY(m1 != m4); + VERIFY(m1 == m5); + VERIFY(m4 == m2); + VERIFY(m4 != m3); + VERIFY(m4 != m6); + VERIFY(m1 == m6); + VERIFY(m1 != m7); + return true; +} + +constexpr bool +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); + return true; +} + +template<typename M1, typename M2> + concept has_op_eq = requires (M1 m1, M2 m2) + { + { m1 == m2 } -> std::same_as<bool>; + { m1 != m2 } -> std::same_as<bool>; + }; + +constexpr void +test_has_op_eq() +{ + using E1 = 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>>); +} + +int +main() +{ + test_ctor_strides_all(); + static_assert(test_ctor_strides_all()); + test_mapping_like_convertible(); + test_mapping_like_constructible(); + test_stride_constructible_all(); + + test_is_exhaustive_all(); + static_assert(test_is_exhaustive_all()); + test_eq(); + static_assert(test_eq()); + test_eq_zero(); + static_assert(test_eq_zero()); + test_has_op_eq(); + + return 0; +} -- 2.49.0