[PATCH v1 1/6] libstdc++: Prepare mdspan-related code for submdspan.

Luc Grosheintz luc.grosheintz@gmail.com
Fri Nov 14 14:09:37 GMT 2025


The changes needed for submdspan are:

  * In submdspan related code the user-defined integer-like
    types need to be copy- and move-constructable.

  * The traits for writing tests that work with both left- and right,
    possibly padded, layouts will also be useful for submdspan.
    Therefore, this code is moved up and generalized.

  * Move __offset further up in <mdspan> and fix some formatting
    mistakes.

	* include/std/mdspan: Improve formatting and placement.
	* testsuite/23_containers/mdspan/int_like.h: Optionally,
	add move- and copy-ctors.
	* testsuite/23_containers/mdspan/layouts/padded_traits.h: Move to...
	* testsuite/23_containers/mdspan/layout_traits.h: ...here.
	* testsuite/23_containers/mdspan/layouts/ctors.cc: Fix include.
	* 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.

Signed-off-by: Luc Grosheintz <luc.grosheintz@gmail.com>
---
 libstdc++-v3/include/std/mdspan               | 52 ++++++++-------
 .../testsuite/23_containers/mdspan/int_like.h | 25 ++++---
 .../padded_traits.h => layout_traits.h}       | 65 +++++++++++++++----
 .../23_containers/mdspan/layouts/ctors.cc     |  2 +-
 .../23_containers/mdspan/layouts/mapping.cc   |  2 +-
 .../23_containers/mdspan/layouts/padded.cc    |  2 +-
 .../mdspan/layouts/padded_neg.cc              |  2 +-
 7 files changed, 103 insertions(+), 47 deletions(-)
 rename libstdc++-v3/testsuite/23_containers/mdspan/{layouts/padded_traits.h => layout_traits.h} (70%)

diff --git a/libstdc++-v3/include/std/mdspan b/libstdc++-v3/include/std/mdspan
index bd7a2a201a7..0a49f24fd3f 100644
--- a/libstdc++-v3/include/std/mdspan
+++ b/libstdc++-v3/include/std/mdspan
@@ -338,21 +338,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
 #if __glibcxx_submdspan
   struct full_extent_t
   {
     explicit full_extent_t() = default;
   };
 
   inline constexpr full_extent_t full_extent{};
 
   template<typename _OffsetType, typename _ExtentType, typename _StrideType>
-    struct strided_slice {
+    struct strided_slice
+    {
       static_assert(__is_standard_integer<_OffsetType>::value
 	|| __detail::__integral_constant_like<_OffsetType>);
       static_assert(__is_standard_integer<_ExtentType>::value
 	|| __detail::__integral_constant_like<_ExtentType>);
       static_assert(__is_standard_integer<_StrideType>::value
 	|| __detail::__integral_constant_like<_StrideType>);
 
       using offset_type = _OffsetType;
       using extent_type = _ExtentType;
       using stride_type = _StrideType;
@@ -361,21 +362,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       [[no_unique_address]] extent_type extent{};
       [[no_unique_address]] stride_type stride{};
     };
 
   template<typename _Mapping>
     struct submdspan_mapping_result
     {
       [[no_unique_address]] _Mapping mapping = _Mapping();
       size_t offset{};
     };
-#endif
+#endif // __glibcxx_submdspan
 
   template<typename _IndexType, size_t... _Extents>
     class extents
     {
       static_assert(__is_standard_integer<_IndexType>::value,
 		    "IndexType must be a signed or unsigned integer type");
       static_assert(
 	  (__mdspan::__valid_static_extent<_Extents, _IndexType> && ...),
 	  "Extents must either be dynamic or representable as IndexType");
 
@@ -546,21 +547,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    __ret *= size_t(__factor);
 	return static_cast<typename _Extents::index_type>(__ret);
       }
 
     // Preconditions: _r < _Extents::rank()
     template<typename _Extents>
       constexpr typename _Extents::index_type
       __fwd_prod(const _Extents& __exts, size_t __begin, size_t __end) noexcept
       {
 	size_t __sta_prod = [__begin, __end] {
-	  span<const size_t> __sta_exts = __static_extents<_Extents>(__begin, __end);
+	  span<const size_t> __sta_exts
+	    = __static_extents<_Extents>(__begin, __end);
 	  size_t __ret = 1;
 	  for(auto __ext : __sta_exts)
 	    if (__ext != dynamic_extent)
 	      __ret *= __ext;
 	  return __ret;
 	}();
 	return __extents_prod(__exts, __sta_prod, __begin, __end);
       }
 
     template<typename _Extents>
@@ -762,21 +764,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  }
       }
 
     template<typename _Extents, typename _IndexType>
       concept __representable_size = _Extents::rank_dynamic() != 0
 	|| __contains_zero(__static_extents<_Extents>())
 	|| (__static_quotient<_Extents, _IndexType>() != 0);
 
     template<typename _Layout, typename _Mapping>
       concept __mapping_of =
-	is_same_v<typename _Layout::template mapping<typename _Mapping::extents_type>,
+	is_same_v<typename _Layout::template mapping<
+		    typename _Mapping::extents_type>,
 		  _Mapping>;
 
     template<template<size_t> typename _Layout, typename _Mapping>
       concept __padded_mapping_of = __mapping_of<
 	_Layout<_Mapping::padding_value>, _Mapping>;
 
 #ifdef __glibcxx_padded_layouts
     template<typename _Mapping>
       constexpr bool __is_left_padded_mapping = __padded_mapping_of<
 	layout_left_padded, _Mapping>;
@@ -797,20 +800,39 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 				       || __mapping_of<layout_stride, _Mapping>
 #ifdef __glibcxx_padded_layouts
 				       || __is_left_padded_mapping<_Mapping>
 				       || __is_right_padded_mapping<_Mapping>
 #endif
 				       ;
 
     // A tag type to create internal ctors.
     class __internal_ctor
     { };
+
+    template<typename _Mapping>
+      constexpr typename _Mapping::index_type
+      __offset(const _Mapping& __m) noexcept
+      {
+	using _IndexType = typename _Mapping::index_type;
+	constexpr auto __rank = _Mapping::extents_type::rank();
+
+	if constexpr (__standardized_mapping<_Mapping>)
+	  return 0;
+	else if (__empty(__m.extents()))
+	  return 0;
+	else
+	  {
+	    auto __impl = [&__m]<size_t... _Counts>(index_sequence<_Counts...>)
+	      { return __m(((void) _Counts, _IndexType(0))...); };
+	    return __impl(make_index_sequence<__rank>());
+	  }
+      }
   }
 
   template<typename _Extents>
     class layout_left::mapping
     {
     public:
       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;
@@ -1030,21 +1052,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 				   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)
 	    {
-	      if constexpr (extents_type::static_extent(__rank - 1) != dynamic_extent
+	      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
 
@@ -1129,39 +1152,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       {
 	requires __is_extents<typename _Mp::extents_type>;
 	{ _Mp::is_always_strided() } -> same_as<bool>;
 	{ _Mp::is_always_exhaustive() } -> same_as<bool>;
 	{ _Mp::is_always_unique() } -> same_as<bool>;
 	bool_constant<_Mp::is_always_strided()>::value;
 	bool_constant<_Mp::is_always_exhaustive()>::value;
 	bool_constant<_Mp::is_always_unique()>::value;
       };
 
-    template<typename _Mapping>
-      constexpr typename _Mapping::index_type
-      __offset(const _Mapping& __m) noexcept
-      {
-	using _IndexType = typename _Mapping::index_type;
-	constexpr auto __rank = _Mapping::extents_type::rank();
-
-	if constexpr (__standardized_mapping<_Mapping>)
-	  return 0;
-	else if (__empty(__m.extents()))
-	  return 0;
-	else
-	  {
-	    auto __impl = [&__m]<size_t... _Counts>(index_sequence<_Counts...>)
-	      { return __m(((void) _Counts, _IndexType(0))...); };
-	    return __impl(make_index_sequence<__rank>());
-	  }
-      }
-
     template<typename _Mapping, typename... _Indices>
       constexpr typename _Mapping::index_type
       __linear_index_strides(const _Mapping& __m, _Indices... __indices)
       noexcept
       {
 	using _IndexType = typename _Mapping::index_type;
 	_IndexType __res = 0;
 	if constexpr (sizeof...(__indices) > 0)
 	  {
 	    auto __update = [&, __pos = 0u](_IndexType __idx) mutable
diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h b/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h
index e9172c13455..2f79b9dd3b5 100644
--- a/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h
@@ -2,38 +2,47 @@
 #define TEST_MDSPAN_INT_LIKE_H
 
 enum class CustomIndexKind
 {
   Const,
   Throwing,
   Mutating,
   RValue,
 };
 
-template<CustomIndexKind Kind>
+template<CustomIndexKind Kind, bool Copyable = false>
   class CustomIndexType
   {
   public:
     explicit
     CustomIndexType(int i)
     : value(i)
     { }
 
-    CustomIndexType() = delete;
-    CustomIndexType(const CustomIndexType&) = delete;
-    CustomIndexType(CustomIndexType&&) = delete;
+    CustomIndexType() requires(Copyable) = default;
+    CustomIndexType() requires(!Copyable) = delete;
 
-    const CustomIndexType&
-    operator=(const CustomIndexType&) = delete;
+    CustomIndexType(const CustomIndexType&) requires(Copyable) = default;
+    CustomIndexType(const CustomIndexType&) requires(!Copyable) = delete;
 
-    const CustomIndexType&
-    operator=(CustomIndexType&&) = delete;
+    CustomIndexType(CustomIndexType&&) requires(Copyable) = default;
+    CustomIndexType(CustomIndexType&&) requires(!Copyable) = delete;
+
+    CustomIndexType&
+    operator=(const CustomIndexType&) requires(Copyable) = default;
+    CustomIndexType&
+    operator=(const CustomIndexType&) requires(!Copyable) = delete;
+
+    CustomIndexType&
+    operator=(CustomIndexType&&) requires(Copyable) = default;
+    CustomIndexType&
+    operator=(CustomIndexType&&) requires(!Copyable) = delete;
 
     constexpr
     operator int() const noexcept
     requires (Kind == CustomIndexKind::Const)
     { return value; }
 
     constexpr
     operator int() const
     requires (Kind == CustomIndexKind::Throwing)
     { return value; }
diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_traits.h b/libstdc++-v3/testsuite/23_containers/mdspan/layout_traits.h
similarity index 70%
rename from libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_traits.h
rename to libstdc++-v3/testsuite/23_containers/mdspan/layout_traits.h
index 788ae82fcc4..619cab53b9e 100644
--- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_traits.h
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layout_traits.h
@@ -1,15 +1,21 @@
-#ifndef TEST_MDSPAN_PADDED_TRAITS_H
-#define TEST_MDSPAN_PADDED_TRAITS_H
+#ifndef TEST_MDSPAN_LAYOUT_TRAITS_H
+#define TEST_MDSPAN_LAYOUT_TRAITS_H
 
 #include <algorithm>
 
+enum class PaddingSide
+{
+  Left,
+  Right
+};
+
 template<typename Layout>
   constexpr static bool is_left_padded = false;
 
 #if __cplusplus > 202302L
 template<size_t PaddingValue>
   constexpr static bool is_left_padded<std::layout_left_padded<PaddingValue>>
     = true;
 #endif
 
 template<typename Layout>
@@ -19,53 +25,61 @@ template<typename Layout>
 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_right_padded<Layout>;
 
 #if __cplusplus > 202302L
+template<PaddingSide Side, typename Layout>
+  constexpr bool
+  is_same_padded;
+
+template<typename Layout>
+  constexpr bool
+  is_same_padded<PaddingSide::Left, Layout> = is_left_padded<Layout>;
+
+template<typename Layout>
+  constexpr bool
+  is_same_padded<PaddingSide::Right, Layout> = is_right_padded<Layout>;
+
 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,
-  Right
-};
-
 struct DeducePaddingSide
 {
   template<template<size_t> typename Layout>
     constexpr static PaddingSide
     from_template()
     {
       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()
     {
-      if constexpr (is_left_padded<Layout>)
+      if constexpr (std::same_as<Layout, std::layout_left>)
+	return PaddingSide::Left;
+      else if constexpr (is_left_padded<Layout>)
 	return PaddingSide::Left;
       else
 	return PaddingSide::Right;
     }
 };
 
 template<PaddingSide Side>
   struct LayoutTraits;
 
 template<>
@@ -77,22 +91,32 @@ template<>
     template<typename Extents>
       using extents_type = Extents;
 
     template<typename Extents>
       constexpr static extents_type<Extents>
       make_extents(const Extents& exts)
       { return exts; }
 
     template<typename T, size_t N>
       constexpr static std::array<T, N>
-      make_array(const std::array<T, N>& expected)
-      { return expected; }
+      make_array(const std::array<T, N>& a)
+      { return a; }
+
+    template<typename... Indices>
+      constexpr static auto
+      make_indices(Indices... indices)
+      { return std::array{indices...}; }
+
+    template<typename... Ts>
+      constexpr static std::tuple<Ts...>
+      make_tuple(const std::tuple<Ts...>& tup)
+      { return tup; }
 
     template<typename Mapping>
       constexpr static auto
       padded_stride(const Mapping& m)
       { return m.stride(1); }
 
     template<typename Extents>
       constexpr static auto
       padded_extent(const Extents& exts)
       { return exts.extent(0); }
@@ -121,20 +145,39 @@ template<>
       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... Indices>
+      constexpr static auto
+      make_indices(Indices... indices)
+      { return make_array(std::array{indices...}); }
+
+    template<typename... Ts>
+      constexpr static auto
+      make_tuple(const std::tuple<Ts...>& tup)
+      {
+	constexpr size_t rank = sizeof...(Ts);
+	auto impl = [&]<size_t... I>(std::index_sequence<I...>)
+	{
+	  auto idx = [rank](size_t i) consteval
+	  { return rank - 1 - i; };
+	  return std::tuple<Ts...[idx(I)]...>{get<idx(I)>(tup)...};
+	};
+	return impl(std::make_index_sequence<rank>());
+      }
+
     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
diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
index 27065a0dfc6..80ae5d8d56a 100644
--- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
@@ -1,14 +1,14 @@
 // { dg-do run { target c++23 } }
 #include <mdspan>
 
-#include "padded_traits.h"
+#include "../layout_traits.h"
 #include <cstdint>
 #include <testsuite_hooks.h>
 
 constexpr size_t dyn = std::dynamic_extent;
 
 template<typename Mapping, typename IndexType, size_t... Extents>
   constexpr void
   verify(std::extents<IndexType, Extents...> oexts)
   {
     auto m = Mapping(oexts);
diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
index d1486e4e11c..17cfac54113 100644
--- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
@@ -1,15 +1,15 @@
 // { dg-do run { target c++23 } }
 #include <mdspan>
 
 #include "../int_like.h"
-#include "padded_traits.h"
+#include "../layout_traits.h"
 #include <cstdint>
 #include <testsuite_hooks.h>
 
 constexpr size_t dyn = std::dynamic_extent;
 
 template<typename Mapping>
   concept has_static_is_exhaustive = requires
   {
     { Mapping::is_exhaustive() } -> std::same_as<bool>;
   };
diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc
index cf4821a3c74..19fdf93ce0d 100644
--- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc
@@ -1,16 +1,16 @@
 // { dg-do run { target c++26 } }
 #include <mdspan>
 
 #include <cstdint>
 #include "../int_like.h"
-#include "padded_traits.h"
+#include "../layout_traits.h"
 #include <testsuite_hooks.h>
 
 constexpr size_t dyn = std::dynamic_extent;
 
 template<template<size_t> typename Layout>
   constexpr bool
   test_representable_padded_size()
   {
     using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
     {
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 a758f74287f..4073f683822 100644
--- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_neg.cc
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_neg.cc
@@ -1,14 +1,14 @@
 // { dg-do compile { target c++26 } }
 #include <mdspan>
 
-#include "padded_traits.h"
+#include "../layout_traits.h"
 #include <cstdint>
 
 constexpr size_t dyn = std::dynamic_extent;
 
 template<template<size_t> typename Layout>
   constexpr bool
   test_from_extens_representable_sta()
   {
     using E1 = std::extents<uint8_t, 8, 128>;
     auto m = typename Layout<dyn>::mapping(E1{}); // { dg-error "required from" }
-- 
2.51.2



More information about the Libstdc++ mailing list