[PATCH v4 7/8] libstdc++: Add tests for layout_stride.
Tomasz Kaminski
tkaminsk@redhat.com
Thu May 29 07:49:46 GMT 2025
On Mon, May 26, 2025 at 4:25 PM Luc Grosheintz <luc.grosheintz@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.grosheintz@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" }
> +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
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20250529/8646ebae/attachment-0001.htm>
More information about the Libstdc++
mailing list