[PATCH v4 2/2] libstdc++: Implement layout_right_padded [PR110352].

Jonathan Wakely jwakely@redhat.com
Wed Oct 8 08:46:51 GMT 2025


On Mon, 29 Sep 2025 at 08:00 +0200, Luc Grosheintz wrote:
>This commit adds the right padded layout as described in N5014, with
>LWG4372 (dynamic padding value) and LWG4314 (move in operator()).
>
>	PR libstdc++/110352
>
>libstdc++-v3/ChangeLog:
>
>	* include/std/mdspan (_RightPaddedIndices): Traits for right
>	padded layouts.
>	(layout_right::mapping::mapping) New overload for right padded
>	layouts.
>	(layout_right_padded): Add implementation.
>	* src/c++23/std.cc.in (layout_right_padded): Add.
>	* testsuite/23_containers/mdspan/layouts/ctors.cc: Update
>	test for right padded layouts.
>	* 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 <luc.grosheintz@gmail.com>
>---
> libstdc++-v3/include/std/mdspan               | 248 ++++++++++++++++++
> libstdc++-v3/src/c++23/std.cc.in              |   5 +-
> .../23_containers/mdspan/layouts/ctors.cc     |   1 +
> .../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            |  84 +++++-
> 8 files changed, 369 insertions(+), 8 deletions(-)
>
>diff --git a/libstdc++-v3/include/std/mdspan b/libstdc++-v3/include/std/mdspan
>index 6bd75ffde0b..957da1e0e44 100644
>--- a/libstdc++-v3/include/std/mdspan
>+++ b/libstdc++-v3/include/std/mdspan
>@@ -977,6 +977,34 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	: mapping(__other.extents(), __mdspan::__internal_ctor{})
> 	{ __glibcxx_assert(*this == __other); }
>
>+#if __glibcxx_padded_layouts
>+      template<class _RightPaddedMapping>

s/class/typename/

and now that I notice it, that's present in the left padded layout
too:

       template<class _LeftpadMapping>


>+	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>();

I would usually break the line before the '=' and keep the function
call together, like so:

	  constexpr size_t __ostride_sta
             = __mdspan::__get_static_stride<_RightPaddedMapping>();

>+
>+	  if constexpr (__rank > 1)
>+	    {
>+	      if constexpr (extents_type::static_extent(__rank - 1) != dynamic_extent
>+		  && __ostride_sta != dynamic_extent)
>+		static_assert(extents_type::static_extent(__rank - 1)
>+		    == __ostride_sta);
>+	      else
>+		__glibcxx_assert(__other.stride(__rank - 2)
>+		    == __other.extents().extent(__rank - 1));
>+	    }
>+	}
>+#endif
>+
>       constexpr mapping&
>       operator=(const mapping&) noexcept = default;
>
>@@ -1370,6 +1398,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
>       {
>@@ -1396,6 +1455,31 @@ _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 _IndexType, size_t _StaticStride, size_t..._Extents>
>+	  constexpr static auto _S_make_padded_extent(

The _S_make_padded_extent function name should start a new line (same
observation for the left padded layout too).

>+	    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
>       {
>@@ -1822,6 +1906,170 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	  noexcept
> 	  { return __self._M_storage._M_equal(__other); }
>       };
>+
>+  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 _PaddedStorage = __mdspan::_PaddedStorage<_PaddingValue,
>+	      _Extents, __mdspan::_RightPaddedLayoutTraits<_S_rank>>;
>+	[[no_unique_address]] _PaddedStorage _M_storage;
>+
>+	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_padstride() 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 __pad)

'mapping' on a new line (and in the left padded layout too).

>+	  : _M_storage(__exts,
>+	      __mdspan::__index_type_cast<index_type>(std::move(__pad)))
>+	  { }
>+
>+	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(layout_right{}, __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(layout_left{}, __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_padstride();
>+	  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 _PaddedStorage::_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; }

For the six functions above, could we either group all the static
is_always_foo functions together (as in the standard), or group each
static is_always_foo with the non-static is_foo.

Currently we have:
static non-static static static non-static non-static.

(same comment for lefty).


>+
>+	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_padstride();
>+	  else
>+	    return static_cast<index_type>(
>+	      static_cast<size_t>(_M_padstride()) *
>+	      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._M_storage._M_equal(__other); }
>+      };
> #endif

Comment on the #endif please.

Those are all minor tweaks, so this is OK for trunk, thanks!


>
>   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 8cba8094abc..27065a0dfc6 100644
>--- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
>+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
>@@ -469,6 +469,7 @@ main()
>   test_all<std::layout_right>();
> #if __cplusplus > 202302L
>   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 05188432f14..8840d07879c 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>());
> #if __cplusplus > 202302L
>   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 10ce622523d..d1486e4e11c 100644
>--- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
>+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
>@@ -373,7 +373,7 @@ template<>
>
> #if __cplusplus > 202302L
> template<typename Layout>
>-  requires is_left_padded<Layout>
>+  requires is_left_padded<Layout> || is_right_padded<Layout>
>   struct TestStride2D<Layout>
>   {
>     static constexpr void
>@@ -457,7 +457,7 @@ template<>
>
> #if __cplusplus > 202302L
> template<typename Layout>
>-  requires is_left_padded<Layout>
>+  requires is_left_padded<Layout> || is_right_padded<Layout>
>   struct TestStride3D<Layout>
>   {
>     static constexpr void
>@@ -701,6 +701,7 @@ main()
>   test_all<std::layout_stride>();
> #if __cplusplus > 202302L
>   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>();
>@@ -708,6 +709,7 @@ main()
>   test_has_op_eq<std::layout_left, std::layout_stride, true>();
> #if __cplusplus > 202302L
>   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 d43b84ef875..cf4821a3c74 100644
>--- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc
>+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc
>@@ -668,6 +668,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 f0dbfe9d9c6..a758f74287f 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 9171d8c1ce1..788ae82fcc4 100644
>--- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_traits.h
>+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_traits.h
>@@ -12,15 +12,34 @@ template<size_t PaddingValue>
>     = true;
> #endif
>
>+template<typename Layout>
>+  constexpr static bool is_right_padded = false;
>+
>+#if __cplusplus > 202302L
>+template<size_t PaddingValue>
>+  constexpr static bool is_right_padded<std::layout_right_padded<PaddingValue>>
>+    = true;
>+#endif
>+
> template<typename Layout>
>   constexpr bool
>-  is_padded_layout = is_left_padded<Layout>;
>+  is_padded_layout = is_left_padded<Layout> || is_right_padded<Layout>;
>
> #if __cplusplus > 202302L
>+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;
>+  }
>
> enum class PaddingSide
> {
>-  Left
>+  Left,
>+  Right
> };
>
> struct DeducePaddingSide
>@@ -28,12 +47,22 @@ 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>
>@@ -69,5 +98,52 @@ template<>
>       { return exts.extent(0); }
>   };
>
>+template<>
>+  struct LayoutTraits<PaddingSide::Right>
>+  {
>+    using layout_same = std::layout_right;
>+    using layout_other = std::layout_left;
>+
>+    template<typename IndexType, size_t... Extents>
>+      constexpr static auto
>+      make_extents(const std::extents<IndexType, Extents...>& exts)
>+      {
>+	constexpr size_t rank = sizeof...(Extents);
>+	auto impl = [&]<size_t... I>(std::index_sequence<I...>)
>+	{
>+	  auto dyn_exts = make_array(dynamic_extents_array(exts));
>+	  return std::extents<IndexType, Extents...[rank - 1 - I]...>(dyn_exts);
>+	};
>+	return impl(std::make_index_sequence<rank>());
>+      }
>+
>+    template<typename Extents>
>+      using extents_type = decltype(make_extents(std::declval<Extents>()));
>+
>+    template<typename T, size_t N>
>+      constexpr static std::array<T, N>
>+      make_array(std::array<T, N> a)
>+      {
>+	std::ranges::reverse(a);
>+	return a;
>+      }
>+
>+    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
>
>



More information about the Libstdc++ mailing list