Thank you! These are ready too. You're right about reversing
the easy way (all these hard learned, "beautiful", recursive
tricks seem to be all outdated, thanks for persistently pointing
them out).

I kept the reversed function because it returns the revered
array, that's very convenient. It's now implemented using
std::ranges::reverse.

BTW, I spent part of the morning trying to understand if

  constexpr auto [...I] = make_index_sequence<rank>();

can help eliminate the lambda. I failed, I think it's because
index_sequence doesn't play nice with structured bindings. Is
there a reasonably easy way to make it work now? My most
desparate attempt was a hard-coded:

  constexpr auto [...I] = std::tuple{std::cw<0>, ..., std::cw<N>};

which failed, because it can't get the I in a constexpr manner.


On 9/25/25 1:20 PM, Tomasz Kaminski wrote:
On Tue, Sep 23, 2025 at 3:41 PM Luc Grosheintz <[email protected]>
wrote:

This commit adds the right padded layout as described in N5014, with
LWG4372 (dynamic padding value) and LWG4314 (move in operator()).

libstdc++-v3/ChangeLog:

         * include/std/mdspan (_RightPaddedIndices): New class.
         (layout_right_padded): New class.
         * src/c++23/std.cc.in (layout_right_padded): Add.
         * testsuite/23_containers/mdspan/layouts/ctors.cc: Update
         test for right padded layout.
         * testsuite/23_containers/mdspan/layouts/empty.cc: Ditto.
         * testsuite/23_containers/mdspan/layouts/mapping.cc: Ditto.
         * testsuite/23_containers/mdspan/layouts/padded.cc: Ditto.
         * testsuite/23_containers/mdspan/layouts/padded_neg.cc: Ditto.
         * testsuite/23_containers/mdspan/layouts/padded_traits.h: Ditto.

Signed-off-by: Luc Grosheintz <[email protected]>

This also looks good to me, the usage of common storage and the traits
in the test have really paid off.


---
  libstdc++-v3/include/std/mdspan               | 263 ++++++++++++++++++
  libstdc++-v3/src/c++23/std.cc.in              |   5 +-
  .../23_containers/mdspan/layouts/ctors.cc     |   3 +-
  .../23_containers/mdspan/layouts/empty.cc     |   1 +
  .../23_containers/mdspan/layouts/mapping.cc   |   6 +-
  .../23_containers/mdspan/layouts/padded.cc    |   4 +
  .../mdspan/layouts/padded_neg.cc              |  28 ++
  .../mdspan/layouts/padded_traits.h            | 135 ++++++++-
  8 files changed, 437 insertions(+), 8 deletions(-)

diff --git a/libstdc++-v3/include/std/mdspan
b/libstdc++-v3/include/std/mdspan
index 18782bf0a06..f61931ab8e6 100644
--- a/libstdc++-v3/include/std/mdspan
+++ b/libstdc++-v3/include/std/mdspan
@@ -954,6 +954,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
         : mapping(__other.extents(), __mdspan::__internal_ctor{})
         { __glibcxx_assert(*this == __other); }

+#if __glibcxx_padded_layouts
+      template<class _RightPaddedMapping>
+       requires __mdspan::__is_right_padded_mapping<_RightPaddedMapping>
+                && is_constructible_v<extents_type,
+                                      typename
_RightPaddedMapping::extents_type>
+       constexpr
+       explicit(!is_convertible_v<typename
_RightPaddedMapping::extents_type,
+                                  extents_type>)
+       mapping(const _RightPaddedMapping& __other) noexcept
+       : mapping(__other.extents(), __mdspan::__internal_ctor{})
+       {
+         constexpr size_t __rank = extents_type::rank();
+         constexpr size_t __ostride_sta = __mdspan::__get_static_stride<
+           _RightPaddedMapping>();
+
+         if constexpr (__rank > 1

Same comment about extracting (_rank > 1) first, and then doing
__glibcxx_assert in else.

+             && extents_type::static_extent(__rank - 1) != dynamic_extent
+             && __ostride_sta != dynamic_extent)
+           static_assert(extents_type::static_extent(__rank - 1) ==
__ostride_sta);
+
+         if constexpr (__rank > 1)
+           __glibcxx_assert(__other.stride(__rank - 2)
+               == __other.extents().extent(__rank - 1));
+       }
+#endif
+
        constexpr mapping&
        operator=(const mapping&) noexcept = default;

@@ -1350,6 +1376,37 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
         return __res;
        }

+    template<typename _Extents, typename _Stride, typename... _Indices>
+      constexpr typename _Extents::index_type
+      __linear_index_rightpad(const _Extents& __exts, _Stride __stride,
+                            _Indices... __indices)
+      {
+       // i[n-1] + stride*(i[n-2] + extents.extent(n-2])*...)
+       using _IndexType = typename _Extents::index_type;
+       _IndexType __res = 0;
+       if constexpr (sizeof...(__indices) > 0)
+         {
+           _IndexType __mult = 1;
+           array<_IndexType, sizeof...(__indices)>
__ind_arr{__indices...};
+
+           auto __update_rest = [&, __pos = __exts.rank()-1](_IndexType)
mutable
+             {
+               --__pos;
+               __res += __ind_arr[__pos] * __mult;
+               __mult *= __exts.extent(__pos);
+             };
+
+           auto __update = [&](_IndexType, auto... __rest)
+             {
+               __res += __ind_arr[__exts.rank() - 1];
+               __mult = __stride.extent(0);
+               (__update_rest(__rest), ...);
+             };
+           __update(__indices...);
+         }
+       return __res;
+      }
+
      template<size_t _Rank>
        struct _LeftPaddedLayoutTraits
        {
@@ -1384,6 +1441,39 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
           }
        };

+    template<size_t _Rank>
+      struct _RightPaddedLayoutTraits
+      {
+       using _LayoutSame = layout_right;
+       using _LayoutOther = layout_left;
+
+       constexpr static size_t _S_ext_idx = _Rank - 1;
+       constexpr static size_t _S_stride_idx = _Rank - 2;
+       constexpr static size_t _S_unpad_begin = 0;
+       constexpr static size_t _S_unpad_end = _Rank - 1;
+
+       template<typename _Mapping>
+         constexpr static bool _S_is_same_padded_mapping =
+         __is_right_padded_mapping<_Mapping>;
+
+       template<typename _Mapping>
+         constexpr static bool _S_is_other_padded_mapping =
+         __is_left_padded_mapping<_Mapping>;
+
+       template<typename _IndexType, size_t _StaticStride,
size_t..._Extents>
+         constexpr static auto _S_make_padded_extent(
+           extents<_IndexType, _StaticStride> __stride,
+           const extents<_IndexType, _Extents...>& __exts)
+         {
+           auto __impl = [&]<size_t... _Is>(integer_sequence<size_t,
_Is...>)
+           {
+             return extents<_IndexType, (_Extents...[_Is])...,
_StaticStride>{
+               __exts.extent(_Is)..., __stride.extent(0)};
+           };
+           return __impl(make_index_sequence<sizeof...(_Extents) - 1>());
+         }
+      };
+
      template<size_t _PaddingValue, typename _Extents, typename
_LayoutTraits>
        class _PaddedStorage
        {
@@ -1834,6 +1924,179 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
                                            __other.stride(1)));
           }
        };
+
+  template<size_t _PaddingValue>
+    template<typename _Extents>
+      class layout_right_padded<_PaddingValue>::mapping {
+      public:
+       static constexpr size_t padding_value = _PaddingValue;
+       using extents_type = _Extents;
+       using index_type = typename extents_type::index_type;
+       using size_type = typename extents_type::size_type;
+       using rank_type = typename extents_type::rank_type;
+       using layout_type = layout_right_padded<_PaddingValue>;
+
+      private:
+       static constexpr size_t _S_rank = extents_type::rank();
+       using _RightPaddedStorage = __mdspan::_PaddedStorage<_PaddingValue,
+             _Extents, __mdspan::_RightPaddedLayoutTraits<_S_rank>>;

The name will become _PaddedStorage, so we can change __get_static_stride.

+       [[no_unique_address]] _RightPaddedStorage _M_storage;
+
+       static constexpr size_t _S_static_stride =
+         _RightPaddedStorage::_S_static_stride;
+
+       consteval friend size_t
+       __mdspan::__get_static_stride<mapping>();
+
+       constexpr index_type
+       _M_extent(size_t __r) const noexcept
+       { return _M_storage._M_extents.extent(__r); }
+
+       constexpr index_type
+       _M_dynamic_padded_stride() const noexcept
+       { return _M_storage._M_stride.extent(0); }
+
+      public:
+       constexpr
+       mapping() noexcept
+       { }
+
+       constexpr
+       mapping(const mapping&) noexcept = default;
+
+       constexpr
+       mapping(const extents_type& __exts)
+       : _M_storage(__exts)
+       { }
+
+       template<__mdspan::__valid_index_type<index_type> _OIndexType>
+         constexpr mapping(const extents_type& __exts, _OIndexType __opad)
+         : _M_storage(__exts, std::move(__opad))
+         { }
+
+       template<typename _OExtents>
+         requires is_constructible_v<extents_type, _OExtents>
+         constexpr explicit(!is_convertible_v<_OExtents, extents_type>)
+         mapping(const layout_right::mapping<_OExtents>& __other)
+         : _M_storage(__other)
+         { }
+
+       template<typename _OExtents>
+         requires is_constructible_v<_OExtents, extents_type>
+         constexpr explicit(_OExtents::rank() > 0)
+         mapping(const typename layout_stride::mapping<_OExtents>&
__other)
+         : _M_storage(__other)
+         { __glibcxx_assert(*this == __other); }
+
+       template<typename _RightPaddedMapping>
+         requires __mdspan::__is_right_padded_mapping<_RightPaddedMapping>
+             && is_constructible_v<extents_type,
+                                   typename
_RightPaddedMapping::extents_type>
+         constexpr explicit(_S_rank > 1 && (padding_value !=
dynamic_extent
+               || _RightPaddedMapping::padding_value == dynamic_extent))
+         mapping(const _RightPaddedMapping& __other)
+         : _M_storage(__other)

This will pass _M_storage(layout_right{}, _other);l

+         { }
+
+       template<typename _LeftPaddedMapping>
+         requires (__mdspan::__is_left_padded_mapping<_LeftPaddedMapping>
+             || __mdspan::__mapping_of<layout_left, _LeftPaddedMapping>)
+           && (_S_rank <= 1)
+           && is_constructible_v<extents_type,
+                                 typename
_LeftPaddedMapping::extents_type>
+         constexpr explicit(!is_convertible_v<
+             typename _LeftPaddedMapping::extents_type, extents_type>)
+         mapping(const _LeftPaddedMapping& __other) noexcept
+         : _M_storage(__other)

This will pass _M_storage(layout_left{}, _other);l

+         { }
+
+       constexpr mapping& operator=(const mapping&) noexcept = default;
+
+       constexpr const extents_type&
+       extents() const noexcept { return _M_storage._M_extents; }
+
+       constexpr array<index_type, _S_rank>
+       strides() const noexcept
+       {
+         array<index_type, _S_rank> __ret;
+         if constexpr (_S_rank > 0)
+           __ret[_S_rank - 1] = 1;
+         if constexpr (_S_rank > 1)
+           __ret[_S_rank - 2] = _M_dynamic_padded_stride();
+         if constexpr (_S_rank > 2)
+           for(size_t __i = _S_rank - 2; __i > 0; --__i)
+             __ret[__i - 1] = __ret[__i] * _M_extent(__i);
+         return __ret;
+       }
+
+       constexpr index_type
+       required_span_size() const noexcept
+       { return _M_storage._M_required_span_size(); }
+
+       // _GLIBCXX_RESOLVE_LIB_DEFECTS
+       // 4314. Missing move in mdspan layout mapping::operator()
+       template<__mdspan::__valid_index_type<index_type>... _Indices>
+         requires (sizeof...(_Indices) == _S_rank)
+         constexpr index_type
+         operator()(_Indices... __indices) const noexcept
+         {
+           return __mdspan::__linear_index_rightpad(
+             extents(), _M_storage._M_stride,
+             static_cast<index_type>(std::move(__indices))...);
+         }
+
+       static constexpr bool
+       is_always_exhaustive() noexcept
+       { return _RightPaddedStorage::_M_is_always_exhaustive(); }
+
+       constexpr bool
+       is_exhaustive() noexcept
+       { return _M_storage._M_is_exhaustive(); }
+
+       static constexpr bool
+       is_always_unique() noexcept { return true; }
+
+       static constexpr bool
+       is_always_strided() noexcept { return true; }
+
+       static constexpr bool
+       is_unique() noexcept { return true; }
+
+       static constexpr bool
+       is_strided() noexcept { return true; }
+
+       constexpr index_type
+       stride(rank_type __r) const noexcept
+       {
+         __glibcxx_assert(__r < _S_rank);
+         if constexpr (_S_rank <= 1)
+           return 1;
+         else

This can be chained with next if without need for subbraces,
I would do that.

+           {
+             if (__r == _S_rank - 1)
+               return 1;
+             else if (__r == _S_rank - 2)
+               return _M_dynamic_padded_stride();
+             else
+               return static_cast<index_type>(
+                 static_cast<size_t>(_M_dynamic_padded_stride()) *
+                 static_cast<size_t>(__mdspan::__fwd_prod(
+                     extents(), __r + 1, _S_rank - 1)));
+           }
+       }
+
+       template<typename _RightPaddedMapping>
+         requires(__mdspan::__is_right_padded_mapping<_RightPaddedMapping>
+                  && _RightPaddedMapping::extents_type::rank() == _S_rank)
+         friend constexpr bool
+         operator==(const mapping& __self, const _RightPaddedMapping&
__other)
+         noexcept
+         {
+           return __self.extents() == __other.extents()
+             && (_S_rank < 2 || cmp_equal(__self.stride(_S_rank - 2),
+                                          __other.stride(_S_rank - 2)));
+         }
+      };
  #endif

    template<typename _ElementType>
diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/
std.cc.in
index f10bab59e2c..c1b4e4c88b7 100644
--- a/libstdc++-v3/src/c++23/std.cc.in
+++ b/libstdc++-v3/src/c++23/std.cc.in
@@ -1871,9 +1871,10 @@ export namespace std
    using std::mdspan;
  #if __glibcxx_padded_layouts
    using std::layout_left_padded;
+  using std::layout_right_padded;
  #endif
-  // FIXME layout_right_padded, strided_slice, submdspan_mapping_result,
-  // full_extent_t, full_extent, submdspan_extents, mdsubspan
+  // FIXME strided_slice, submdspan_mapping_result, full_extent_t,
full_extent,
+  // submdspan_extents, mdsubspan
  }
  #endif

diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
index 891471467e1..73b161f5979 100644
--- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
@@ -10,7 +10,7 @@ constexpr size_t dyn = std::dynamic_extent;
  #if __glibcxx_padded_layouts
  template<typename Layout>
    constexpr bool
-  is_padded_layout = is_left_padded<Layout>;
+  is_padded_layout = is_left_padded<Layout> || is_right_padded<Layout>;
  #else
  template<typename>
    constexpr bool
@@ -479,6 +479,7 @@ main()
    test_all<std::layout_right>();
  #ifdef __glibcxx_padded_layouts
    test_padded_all<std::layout_left_padded>();
+  test_padded_all<std::layout_right_padded>();
  #endif

    from_left_or_right::test_all<std::layout_left, std::layout_right>();
diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc
b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc
index 236aca8bc2b..c5519afe34f 100644
--- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc
@@ -142,6 +142,7 @@ main()
    static_assert(test_all<std::layout_stride>());
  #ifdef __glibcxx_padded_layouts
    static_assert(test_padded_all<std::layout_left_padded>());
+  static_assert(test_padded_all<std::layout_right_padded>());
  #endif
    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 5bf6bf65d3a..57560a6aae7 100644
--- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
@@ -374,7 +374,7 @@ template<>

  #if __glibcxx_padded_layouts
  template<typename Layout>
-  requires is_left_padded<Layout>
+  requires is_left_padded<Layout> || is_right_padded<Layout>
    struct TestStride2D<Layout>
    {
      static constexpr void
@@ -458,7 +458,7 @@ template<>

  #if __glibcxx_padded_layouts
  template<typename Layout>
-  requires is_left_padded<Layout>
+  requires is_left_padded<Layout> || is_right_padded<Layout>
    struct TestStride3D<Layout>
    {
      static constexpr void
@@ -702,6 +702,7 @@ main()
    test_all<std::layout_stride>();
  #ifdef __glibcxx_padded_layouts
    test_padded_all<std::layout_left_padded>();
+  test_padded_all<std::layout_right_padded>();
  #endif

    test_has_op_eq<std::layout_right, std::layout_left, false>();
@@ -709,6 +710,7 @@ main()
    test_has_op_eq<std::layout_left, std::layout_stride, true>();
  #ifdef __glibcxx_padded_layouts
    test_padded_has_op_eq<std::layout_left_padded>();
+  test_padded_has_op_eq<std::layout_right_padded>();
  #endif

    test_has_op_eq_peculiar();
diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc
b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc
index ea9a8ef3f4b..a607d1ddd02 100644
--- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc
@@ -670,6 +670,10 @@ main()
    test_all<std::layout_left_padded>();
    static_assert(test_all<std::layout_left_padded>());

+  test_all<std::layout_right_padded>();
+  static_assert(test_all<std::layout_right_padded>());
+
    test_from_pad_all<std::layout_left_padded>();
+  test_from_pad_all<std::layout_right_padded>();
    return 0;
  }
diff --git
a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_neg.cc
b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_neg.cc
index 29f12aa4de2..d0ac31c2810 100644
--- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_neg.cc
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_neg.cc
@@ -15,6 +15,7 @@ template<template<size_t> typename Layout>
      return true;
    }
  static_assert(test_from_extens_representable_sta<std::layout_left_padded>());
// { dg-error "from here" }
+static_assert(test_from_extens_representable_sta<std::layout_right_padded>());
// { dg-error "from here" }

  template<template<size_t> typename Layout>
    constexpr bool
@@ -28,6 +29,7 @@ template<template<size_t> typename Layout>
      return true;
    }
  
static_assert(test_from_extents_representable_padded_size<std::layout_left_padded>());
// { dg-error "expansion of" }
+static_assert(test_from_extents_representable_padded_size<std::layout_right_padded>());
// { dg-error "expansion of" }

  template<template<size_t> typename Layout>
    constexpr bool
@@ -40,6 +42,7 @@ template<template<size_t> typename Layout>
      return true;
    }
  
static_assert(test_from_extents_representable_stride<std::layout_left_padded>());
// { dg-error "expansion of" }
+static_assert(test_from_extents_representable_stride<std::layout_right_padded>());
// { dg-error "expansion of" }

  template<template<size_t> typename Layout>
    constexpr bool
@@ -51,6 +54,7 @@ template<template<size_t> typename Layout>
      return true;
    }
  static_assert(test_from_pad_representable_stride<std::layout_left_padded>());
// { dg-error "expansion of" }
+static_assert(test_from_pad_representable_stride<std::layout_right_padded>());
// { dg-error "expansion of" }

  template<template<size_t> typename Layout>
    constexpr bool
@@ -62,6 +66,7 @@ template<template<size_t> typename Layout>
      return true;
    }
  
static_assert(test_from_pad_representable_padded_size<std::layout_left_padded>());
// { dg-error "expansion of" }
+static_assert(test_from_pad_representable_padded_size<std::layout_right_padded>());
// { dg-error "expansion of" }

  template<template<size_t> typename Layout>
    constexpr bool
@@ -76,6 +81,7 @@ template<template<size_t> typename Layout>
      return true;
    }
  static_assert(test_from_left<std::layout_left_padded>()); // { dg-error
"required from here" }
+static_assert(test_from_left<std::layout_right_padded>()); // { dg-error
"required from here" }

  template<template<size_t> typename Layout>
    constexpr bool
@@ -90,6 +96,7 @@ template<template<size_t> typename Layout>
      return true;
    }
  static_assert(test_from_left_bad_runtime_stride<std::layout_left_padded>());
// { dg-error "expansion of" }
+static_assert(test_from_left_bad_runtime_stride<std::layout_right_padded>());
// { dg-error "expansion of" }

  template<template<size_t> typename Layout>
    constexpr bool
@@ -103,6 +110,7 @@ template<template<size_t> typename Layout>
      return true;
    }
  
static_assert(test_from_left_representable_extents<std::layout_left_padded>());
// { dg-error "expansion of" }
+static_assert(test_from_left_representable_extents<std::layout_right_padded>());
// { dg-error "expansion of" }

  template<template<size_t> typename Layout, size_t PaddingValue>
    constexpr bool
@@ -116,6 +124,8 @@ template<template<size_t> typename Layout, size_t
PaddingValue>
    }
  static_assert(test_pad_overflow<std::layout_left_padded, 1>());   // {
dg-error "expansion of" }
  static_assert(test_pad_overflow<std::layout_left_padded, dyn>()); // {
dg-error "expansion of" }
+static_assert(test_pad_overflow<std::layout_right_padded, 1>());   // {
dg-error "expansion of" }
+static_assert(test_pad_overflow<std::layout_right_padded, dyn>()); // {
dg-error "expansion of" }

  template<template<size_t> typename Layout, size_t PaddingValue>
    constexpr bool
@@ -128,6 +138,8 @@ template<template<size_t> typename Layout, size_t
PaddingValue>
    }
  static_assert(test_from_pad_negative<std::layout_left_padded, 1>());   //
{ dg-error "expansion of" }
  static_assert(test_from_pad_negative<std::layout_left_padded, dyn>()); //
{ dg-error "expansion of" }
+static_assert(test_from_pad_negative<std::layout_right_padded, 1>());
  // { dg-error "expansion of" }
+static_assert(test_from_pad_negative<std::layout_right_padded, dyn>());
// { dg-error "expansion of" }

  template<template<size_t> typename Layout, size_t Pad>
    constexpr bool
@@ -142,6 +154,8 @@ template<template<size_t> typename Layout, size_t Pad>
    }
  static_assert(test_static_pad_same<std::layout_left_padded, 1>()); // {
dg-error "expansion of" }
  static_assert(test_static_pad_same<std::layout_left_padded, 3>()); // {
dg-error "expansion of" }
+static_assert(test_static_pad_same<std::layout_right_padded, 1>()); // {
dg-error "expansion of" }
+static_assert(test_static_pad_same<std::layout_right_padded, 3>()); // {
dg-error "expansion of" }

  template<template<size_t> typename Layout>
    constexpr bool
@@ -156,6 +170,7 @@ template<template<size_t> typename Layout>
      return true;
    }
  static_assert(test_from_stride_wrong_stride0<std::layout_left_padded>());
// { dg-error "expansion of" }
+static_assert(test_from_stride_wrong_stride0<std::layout_right_padded>());
// { dg-error "expansion of" }

  template<template<size_t> typename Layout>
    constexpr bool
@@ -170,6 +185,7 @@ template<template<size_t> typename Layout>
      return true;
    }
  static_assert(test_from_stride_wrong_stride1<std::layout_left_padded>());
// { dg-error "expansion of" }
+static_assert(test_from_stride_wrong_stride1<std::layout_right_padded>());
// { dg-error "expansion of" }

  template<template<size_t> typename Layout>
    constexpr bool
@@ -184,6 +200,7 @@ template<template<size_t> typename Layout>
      return true;
    }
  static_assert(test_from_stride_wrong_stride2<std::layout_left_padded>());
+static_assert(test_from_stride_wrong_stride2<std::layout_right_padded>());

  template<template<size_t> typename Layout>
    constexpr bool
@@ -200,6 +217,7 @@ template<template<size_t> typename Layout>
      return true;
    }
  static_assert(test_from_stride_oversized<std::layout_left_padded>()); //
{ dg-error "expansion of" }
+static_assert(test_from_stride_oversized<std::layout_right_padded>()); //
{ dg-error "expansion of" }

  template<template<size_t> typename Layout>
    constexpr bool
@@ -213,6 +231,7 @@ template<template<size_t> typename Layout>
      return true;
    }
  static_assert(test_from_samepad_dyn<std::layout_left_padded>()); // {
dg-error "expansion of" }
+static_assert(test_from_samepad_dyn<std::layout_right_padded>()); // {
dg-error "expansion of" }

  template<template<size_t> typename Layout>
    constexpr bool
@@ -226,6 +245,7 @@ template<template<size_t> typename Layout>
      return true;
    }
  static_assert(test_from_samepad_sta<std::layout_left_padded>()); // {
dg-error "expansion of" }
+static_assert(test_from_samepad_sta<std::layout_right_padded>()); // {
dg-error "expansion of" }

  template<template<size_t> typename Layout>
    constexpr bool
@@ -240,6 +260,7 @@ template<template<size_t> typename Layout>
      return true;
    }
  static_assert(test_from_samepad_oversized<std::layout_left_padded>()); //
{ dg-error "expansion of" }
+static_assert(test_from_samepad_oversized<std::layout_right_padded>());
// { dg-error "expansion of" }

  template<template<size_t> typename Layout, size_t RunId>
    constexpr bool
@@ -278,6 +299,10 @@
static_assert(test_to_same_not_exhaustive<std::layout_left_padded, 0>());
// { d
  static_assert(test_to_same_not_exhaustive<std::layout_left_padded, 1>());
// { dg-error "expansion of" }
  static_assert(test_to_same_not_exhaustive<std::layout_left_padded, 2>());
// { dg-error "expansion of" }
  static_assert(test_to_same_not_exhaustive<std::layout_left_padded, 3>());
// { dg-error "expansion of" }
+static_assert(test_to_same_not_exhaustive<std::layout_right_padded,
0>()); // { dg-error "expansion of" }
+static_assert(test_to_same_not_exhaustive<std::layout_right_padded,
1>()); // { dg-error "expansion of" }
+static_assert(test_to_same_not_exhaustive<std::layout_right_padded,
2>()); // { dg-error "expansion of" }
+static_assert(test_to_same_not_exhaustive<std::layout_right_padded,
3>()); // { dg-error "expansion of" }

  template<template<size_t> typename Layout>
    constexpr bool
@@ -290,6 +315,7 @@ template<template<size_t> typename Layout>
      return true;
    }
  static_assert(test_statically_bad_padding_value1<std::layout_left_padded>());
// { dg-error "required from" }
+static_assert(test_statically_bad_padding_value1<std::layout_right_padded>());
// { dg-error "required from" }

  template<template<size_t> typename Layout>
    constexpr bool
@@ -301,6 +327,7 @@ template<template<size_t> typename Layout>
      return true;
    }
  static_assert(test_statically_bad_padding_value2<std::layout_left_padded>());
// { dg-error "required from" }
+static_assert(test_statically_bad_padding_value2<std::layout_right_padded>());
// { dg-error "required from" }

  template<template<size_t> typename Layout>
    constexpr bool
@@ -312,6 +339,7 @@ template<template<size_t> typename Layout>
      return true;
    }
  static_assert(test_statically_oversized<std::layout_left_padded>()); // {
dg-error "from here" }
+static_assert(test_statically_oversized<std::layout_right_padded>()); //
{ dg-error "from here" }

  // { dg-prune-output "padding_value must be representable as index_type" }
  // { dg-prune-output "non-constant condition for static assertion" }
diff --git
a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_traits.h
b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_traits.h
index 1f0169d7c02..c267454f467 100644
--- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_traits.h
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_traits.h
@@ -2,9 +2,86 @@
  #define TEST_MDSPAN_PADDED_TRAITS_H

  #if __glibcxx_padded_layouts
+template<typename Rev, typename Fwd>
+  struct reverse_impl;
+
+template<size_t... Ir, size_t I0, size_t... I>
+  struct reverse_impl<std::integer_sequence<size_t, Ir...>,
+                     std::integer_sequence<size_t, I0, I...>>
+  {
+    using type = typename reverse_impl<std::index_sequence<I0, Ir...>,
+                                      std::index_sequence<I...>>::type;
+  };
+
+template<size_t... Ir>
+  struct reverse_impl<std::integer_sequence<size_t, Ir...>,
+                     std::integer_sequence<size_t>>
+  {
+    using type = std::index_sequence<Ir...>;
+  };
+
+template<typename I>
+  struct reverse;
+
+template<size_t... Ir>
+  struct reverse<std::index_sequence<Ir...>>
+  {
+    using type = typename reverse_impl<std::integer_sequence<size_t>,
+                                      std::integer_sequence<size_t,
Ir...>>::type;
+  };
+
+template<typename, typename>
+ struct sequence_to_extents;
+
+template<typename IndexType,  size_t... I>
+  struct sequence_to_extents<IndexType, std::index_sequence<I...>>
+  {
+    using type = std::extents<IndexType, I...>;
+  };
+
+template<typename IndexType, size_t... I>
+  struct reverse<std::extents<IndexType, I...>>
+  {
+    using type = typename sequence_to_extents<
+       IndexType,
+       typename reverse<std::index_sequence<I...>>::type
+      >::type;
+  };
+
+template<typename Extents>
+  constexpr auto
+  dynamic_extents_array(const Extents& exts)
+  {
+    std::array<typename Extents::index_type, Extents::rank()> ret;
+    for(size_t i = 0; i < Extents::rank(); ++i)
+      ret[i] = exts.extent(i);
+    return ret;
+  }
+
+template<typename T, size_t N>
+  constexpr std::array<T, N>
+  reversed(const std::array<T, N>& fwd)
+  {

+    std::array<T, N> rev;
+    for(size_t i = 0; i < N; ++i)
+      rev[(N-1) - i] = fwd[i];
+    return rev;
+  }
+
+template<typename IndexType, size_t... I>
+  constexpr auto
+  reversed(const std::extents<IndexType, I...>& exts)
+  {

I believe this can be implemented much simpler, something like:
         using E =  std::extents<IndexType, I...>;
         if contexpr (sizeof...(I) == 0) // or separate overload
            return E();
          {
            static constexpr size_t Last = sizeof...(I) - 1;
            auto _impl = [&]<template... Idx>(std::index_seqence<Idx...>)
            { return std::extents<IndexType, E::static_extents(Last -
Idx)....>(E::extent(Last - Idx)....); }
            return _impl(std::index_sequence_for<I...>);
         }



+    using rev_type = typename reverse<std::extents<IndexType,
I...>>::type;
+    return rev_type(reversed(dynamic_extents_array(exts)));
+  }
+
+static_assert(std::array{3, 2, 1} == reversed(std::array{1, 2, 3}));
+
  enum class PaddingSide
  {
-  Left
+  Left,
+  Right
  };

  template<typename Layout>
@@ -13,17 +90,33 @@ template<typename Layout>
  template<size_t PaddingValue>
    constexpr static bool
is_left_padded<std::layout_left_padded<PaddingValue>> = true;

+template<typename Layout>
+  constexpr static bool is_right_padded = false;
+
+template<size_t PaddingValue>
+  constexpr static bool
is_right_padded<std::layout_right_padded<PaddingValue>> = true;
+
  struct DeducePaddingSide
  {
    template<template<size_t> typename Layout>
      constexpr static PaddingSide
      from_template()
-    { return PaddingSide::Left; }
+    {
+      if constexpr (std::same_as<Layout<0>, std::layout_left_padded<0>>)
+       return PaddingSide::Left;
+      else
+       return PaddingSide::Right;
+    }

    template<typename Layout>
      constexpr static PaddingSide
      from_typename()
-    { return PaddingSide::Left; }
+    {
+      if constexpr (is_left_padded<Layout>)
+       return PaddingSide::Left;
+      else
+       return PaddingSide::Right;
+    }
  };

  template<PaddingSide Side>
@@ -59,5 +152,41 @@ template<>
        { return exts.extent(0); }
    };

+template<>
+  struct LayoutTraits<PaddingSide::Right>
+  {
+    using layout_same = std::layout_right;
+    using layout_other = std::layout_left;
+
+    template<typename Extents>
+      using extents_type = typename reverse<Extents>::type;
+
+    template<typename Extents>
+      constexpr static extents_type<Extents>
+      make_extents(const Extents& exts)
+      { return reversed(exts); }
+
+    template<typename T, size_t N>
+      constexpr static std::array<T, N>
+      make_expected(const std::array<T, N>& expected)
+      { return reversed(expected)

Inline the reversed function here, and use std::ranges::reverse.


; }
+
+    template<typename Mapping>
+      constexpr static auto
+      padded_stride(const Mapping& m)
+      {
+       auto rank = Mapping::extents_type::rank();
+       return m.stride(rank - 2);
+      }
+
+    template<typename Extents>
+      constexpr static auto
+      padded_extent(const Extents& exts)
+      {
+       auto rank = Extents::rank();
+       return exts.extent(rank - 1);
+      }
+  };
+
  #endif
  #endif
--
2.50.1




Reply via email to