[PATCH v2 01/10] libstdc++: Prepare mdspan-related code for submdspan.
Tomasz Kaminski
tkaminsk@redhat.com
Thu Nov 20 13:55:31 GMT 2025
On Tue, Nov 18, 2025 at 3:25 PM Luc Grosheintz <luc.grosheintz@gmail.com>
wrote:
> 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>
> ---
>
LGTM, one very minor comment, but the change is not required.
> 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
>
The 80 collumn limit is our soft limit, and 100 is the max, I think for
here I preffer going slightly
over it than having condition in next line.
> && __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
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20251120/df3a5c00/attachment-0001.htm>
More information about the Libstdc++
mailing list