On Tue, Nov 18, 2025 at 3:25 PM Luc Grosheintz <[email protected]>
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 <[email protected]>
> ---
>
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
>
>

Reply via email to