On 5/29/25 10:06, Tomasz Kaminski wrote:
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?

Wow, this code is nonsense, sorry! The idea is to check:
  layout_left(layout_{left,stride}),
  layout_right(layout_{right,stride}),

when the extents representable size of the extents shrinks, and
the target extents is valid, e.g. by using a dextents.

The unfortunate issue is that because I've skipped preconditions
for layout_stride, we can't check
  layout_stride(layout_{left,right,stride})

We could add the check that the size of the extents is statically
representable. Which follows the pattern of checking as much as
possible at compile time.


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




Reply via email to