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 > >
