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]>
> ---
>
Suggestions here:
* Add _PaddingStride::_M_equal for opearator== and use _M_stride to
implement it
* Fix spaces, and cast index_type as in layout_left
* Suggestion for LWG issue for both
* Explanation that we can use existing member names for internal classes,
and suggestion
  to use it for _PaddingStorage.

With that ready for v4


>  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>
>
Tabs instead of spaces, here and the rest of the file.

> +       constexpr
> +       explicit(!is_convertible_v<typename
> _RightPaddedMapping::extents_type,
> +                                  extents_type>)
>
We should report bug against standard, because this constructor should be
explicit,
for Ranks > 1 and extent(0) or _S_padded_stride are dynamic, as we have
precondition
then. I think it should also be constrained, on what is now mandated, about
satic_stride
matching.

We should match stride_mismatch, the same way as static extent mismatch is
handled here.

Same for layot_left.


> +       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
> +             && 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>>;
> +       [[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))
>
Cast to index_type here.

> +         { }
> +
> +       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)
> +         { }
> +
> +       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)
> +         { }
> +
> +       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(); }
>
You can name the matching function in PaddedStorage as
"required_span_size()"
without the "_M_prefix". We use _M_prefix as the name is reserved for
implementation,
and people are not allowed to do:
#define _M_required_span_size
#include <mdspan>
Or in other words, if they do, is on them.

Similary if the name is used somewhere, like required_span_size, we can use
it direclty.
I think I would prefer using exactly the same name for function that we
just forward to,
but I will leave it up to you.


> +
> +       // _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
> +           {
> +             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)));
>
+         }
>
This could be just __self.extents() == __other.extents()
                       && __self._M_stride == other._M_stride;
I would add _M_equal method to _PaddingStride to handle this, and call it
from here.



> +      };
>  #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)
> +  {
> +    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); }
> +
> +    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