On Mon, 8 Dec 2025 at 22:31, Luc Grosheintz <[email protected]> wrote: > > Implements submdspan for layout_left_padded as described in P3663.
OK.. > > PR libstdc++/110352 > > libstdc++-v3/ChangeLog: > > * include/std/mdspan (layout_left_padded::submdspan_mapping): > New friend method. > * testsuite/23_containers/mdspan/layout_traits.h > (LayoutTraits::layout_same_padded): New template type alias. > * > testsuite/23_containers/mdspan/submdspan/selections/left_padded_1.cc: > Instantiate tests for layout_left_padded. > * > testsuite/23_containers/mdspan/submdspan/selections/left_padded_8.cc: > Ditto. > * > testsuite/23_containers/mdspan/submdspan/selections/left_padded_dyn.cc: > Ditto. > * testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc: > Ditto. > > Signed-off-by: Luc Grosheintz <[email protected]> > --- > libstdc++-v3/include/std/mdspan | 56 +++++- > .../23_containers/mdspan/layout_traits.h | 4 + > .../submdspan/selections/left_padded_1.cc | 9 + > .../submdspan/selections/left_padded_8.cc | 9 + > .../submdspan/selections/left_padded_dyn.cc | 9 + > .../mdspan/submdspan/submdspan_mapping.cc | 179 ++++++++++++++++-- > 6 files changed, 241 insertions(+), 25 deletions(-) > create mode 100644 > libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left_padded_1.cc > create mode 100644 > libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left_padded_8.cc > create mode 100644 > libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left_padded_dyn.cc > > diff --git a/libstdc++-v3/include/std/mdspan b/libstdc++-v3/include/std/mdspan > index 16dda4c8468..520cf322d8c 100644 > --- a/libstdc++-v3/include/std/mdspan > +++ b/libstdc++-v3/include/std/mdspan > @@ -812,6 +812,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > template<typename _Mapping> > constexpr bool __is_right_padded_mapping = __padded_mapping_of< > layout_right_padded, _Mapping>; > + > + template<typename _Mapping> > + constexpr bool __is_padded_mapping = __is_left_padded_mapping<_Mapping> > + || __is_right_padded_mapping<_Mapping>; > #endif > > template<typename _PaddedMapping> > @@ -1276,11 +1280,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > } > } > > - template<_LayoutSide _Side> > + template<_LayoutSide _Side, bool _Padded> > struct _SubMdspanMapping; > > template<> > - struct _SubMdspanMapping<_LayoutSide::__left> > + struct _SubMdspanMapping<_LayoutSide::__left, false> > { > using _Layout = layout_left; > template<size_t _Pad> using _PaddedLayout = layout_left_padded<_Pad>; > @@ -1304,7 +1308,42 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > }; > > template<> > - struct _SubMdspanMapping<_LayoutSide::__right> > + struct _SubMdspanMapping<_LayoutSide::__left, true> > + { > + using _Layout = layout_left; > + template<size_t _Pad> using _PaddedLayout = layout_left_padded<_Pad>; > + > + template<typename _Mapping, size_t _Us> > + static consteval size_t > + _S_pad() > + { > + using _Extents = typename _Mapping::extents_type; > + constexpr auto __sta_exts > + = __mdspan::__static_extents<_Extents>(1, _Us); > + constexpr auto __sta_padstride > + = __mdspan::__get_static_stride<_Mapping>(); > + if constexpr (__sta_padstride == dynamic_extent > + || !__mdspan::__all_static(__sta_exts)) > + return dynamic_extent; > + else > + return __sta_padstride * __mdspan::__fwd_prod(__sta_exts); > + } > + > + template<size_t _Nm> > + static consteval bool > + _S_is_unpadded_submdspan(span<const _SliceKind, _Nm> __slice_kinds, > + size_t __sub_rank) > + { > + if (__sub_rank == 1) > + return __slice_kinds[0] == _SliceKind::__unit_strided_slice > + || __slice_kinds[0] == _SliceKind::__full; > + else > + return false; > + } > + }; > + > + template<> > + struct _SubMdspanMapping<_LayoutSide::__right, false> > { > using _Layout = layout_right; > template<size_t _Pad> using _PaddedLayout = layout_right_padded<_Pad>; > @@ -1350,7 +1389,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > constexpr auto __side = __mdspan::__mapping_side<_Mapping>(); > constexpr auto __rank = sizeof...(_Slices); > - using _Trait = _SubMdspanMapping<__side>; > + using _Trait = _SubMdspanMapping<__side, > __is_padded_mapping<_Mapping>>; > using _SliceView = span<const _SliceKind, __rank>; > > constexpr auto __slice_kinds = > __mdspan::__make_slice_kind_array<_Slices...>(); > @@ -2558,6 +2597,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > operator==(const mapping& __self, const _LeftpadMapping& __other) > noexcept > { return __self._M_storage._M_equal(__other); } > + > + private: > +#if __glibcxx_submdspan > + template<typename... _Slices> > + requires (extents_type::rank() == sizeof...(_Slices)) > + friend constexpr auto > + submdspan_mapping(const mapping& __mapping, _Slices... __slices) > + { return __mdspan::__submdspan_mapping_impl(__mapping, > __slices...); } > +#endif // __glibcxx_submdspan > }; > > template<size_t _PaddingValue> > diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layout_traits.h > b/libstdc++-v3/testsuite/23_containers/mdspan/layout_traits.h > index 619cab53b9e..f0aeac320de 100644 > --- a/libstdc++-v3/testsuite/23_containers/mdspan/layout_traits.h > +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layout_traits.h > @@ -87,6 +87,8 @@ template<> > { > using layout_same = std::layout_left; > using layout_other = std::layout_right; > + template<size_t PaddingValue> > + using layout_same_padded = std::layout_left_padded<PaddingValue>; > > template<typename Extents> > using extents_type = Extents; > @@ -126,6 +128,8 @@ template<> > struct LayoutTraits<PaddingSide::Right> > { > using layout_same = std::layout_right; > + template<size_t PaddingValue> > + using layout_same_padded = std::layout_right_padded<PaddingValue>; > using layout_other = std::layout_left; > > template<typename IndexType, size_t... Extents> > diff --git > a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left_padded_1.cc > > b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left_padded_1.cc > new file mode 100644 > index 00000000000..b21de0cb59a > --- /dev/null > +++ > b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left_padded_1.cc > @@ -0,0 +1,9 @@ > +// { dg-do run { target c++26 } } > +#include "testcases.h" > + > +int > +main() > +{ > + test_all<std::layout_left_padded<1>>(); > + return 0; > +} > diff --git > a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left_padded_8.cc > > b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left_padded_8.cc > new file mode 100644 > index 00000000000..f2402d833f3 > --- /dev/null > +++ > b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left_padded_8.cc > @@ -0,0 +1,9 @@ > +// { dg-do run { target c++26 } } > +#include "testcases.h" > + > +int > +main() > +{ > + test_all<std::layout_left_padded<8>>(); > + return 0; > +} > diff --git > a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left_padded_dyn.cc > > b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left_padded_dyn.cc > new file mode 100644 > index 00000000000..0f6e4e35550 > --- /dev/null > +++ > b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left_padded_dyn.cc > @@ -0,0 +1,9 @@ > +// { dg-do run { target c++26 } } > +#include "testcases.h" > + > +int > +main() > +{ > + test_all<std::layout_left_padded<dyn>>(); > + return 0; > +} > diff --git > a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc > b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc > index cf6167dc3b0..50836968a06 100644 > --- > a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc > +++ > b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc > @@ -6,6 +6,7 @@ > #include <testsuite_hooks.h> > > constexpr size_t dyn = std::dynamic_extent; > +constexpr auto all = std::full_extent; > > template<typename Mapping, typename... Slices> > constexpr auto > @@ -18,10 +19,11 @@ template<typename Mapping, typename... Slices> > > template<typename Layout> > constexpr bool > - test_layout_unpadded_return_types() > + test_layout_common_return_types() > { > constexpr auto padding_side = DeducePaddingSide::from_typename<Layout>(); > using Traits = LayoutTraits<padding_side>; > + using layout_unpadded = typename Traits::layout_same; > > { > auto m0 = typename Layout::mapping(std::extents()); > @@ -32,21 +34,13 @@ template<typename Layout> > > auto exts = Traits::make_extents(std::dims<5, int>(3, 5, 7, 11, 13)); > auto m = typename Layout::mapping(exts); > - auto all = std::full_extent; > auto s251 = std::strided_slice{2, 5, std::cw<1>}; > > { > auto slices = std::tuple{0, 0, 0, 0, 0}; > auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); > using layout_type = typename decltype(result.mapping)::layout_type; > - static_assert(std::same_as<layout_type, Layout>); > - } > - > - { > - auto slices = std::tuple{all, all, all, s251, 0}; > - auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); > - using layout_type = typename decltype(result.mapping)::layout_type; > - static_assert(std::same_as<layout_type, Layout>); > + static_assert(std::same_as<layout_type, layout_unpadded>); > } > > { > @@ -98,6 +92,72 @@ template<typename Layout> > return true; > } > > +template<typename Layout> > + constexpr bool > + test_layout_unpadded_return_types() > + { > + constexpr auto padding_side = DeducePaddingSide::from_typename<Layout>(); > + using Traits = LayoutTraits<padding_side>; > + > + auto exts = Traits::make_extents(std::dims<5, int>(3, 5, 7, 11, 13)); > + auto m = typename Layout::mapping(exts); > + auto s251 = std::strided_slice{2, 5, std::cw<1>}; > + > + { > + auto slices = std::tuple{all, all, all, s251, 0}; > + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); > + using layout_type = typename decltype(result.mapping)::layout_type; > + static_assert(std::same_as<layout_type, Layout>); > + } > + return true; > + } > + > +template<typename Layout> > + constexpr bool > + test_layout_padded_return_types() > + { > + constexpr auto padding_side = DeducePaddingSide::from_typename<Layout>(); > + using Traits = LayoutTraits<padding_side>; > + > + auto exts = Traits::make_extents(std::dims<5, int>(3, 5, 7, 11, 13)); > + auto m = typename Layout::mapping(exts); > + auto s251 = std::strided_slice{2, 5, std::cw<1>}; > + > + { > + auto slices = std::tuple{all, all, all, s251, 0}; > + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); > + using layout_type = typename decltype(result.mapping)::layout_type; > + using layout_expected = typename Traits::layout_same_padded<dyn>; > + static_assert(std::same_as<layout_type, layout_expected>); > + } > + > + { > + auto slices = std::tuple{all, 0, 0, 0, 0}; > + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); > + using layout_type = typename decltype(result.mapping)::layout_type; > + using layout_expected = typename Traits::layout_same; > + static_assert(std::same_as<layout_type, layout_expected>); > + } > + > + { > + auto s121 = std::strided_slice{1, 2, std::cw<1>}; > + auto slices = std::tuple{s121, 0, 0, 0, 0}; > + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); > + using layout_type = typename decltype(result.mapping)::layout_type; > + using layout_expected = typename Traits::layout_same; > + static_assert(std::same_as<layout_type, layout_expected>); > + } > + > + { > + auto s121 = std::strided_slice{1, 2, std::cw<1>}; > + auto slices = std::tuple{0, s121, 0, 0, 0}; > + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); > + using layout_type = typename decltype(result.mapping)::layout_type; > + static_assert(std::same_as<layout_type, std::layout_stride>); > + } > + return true; > + } > + > template<typename Layout> > constexpr bool > test_layout_unpadded_padding_value() > @@ -105,7 +165,6 @@ template<typename Layout> > using Traits = LayoutTraits<DeducePaddingSide::from_typename<Layout>()>; > auto s0 = std::strided_slice{size_t(1), size_t(2), std::cw<size_t(1)>}; > auto s3 = std::strided_slice{size_t(2), size_t(5), std::cw<size_t(1)>}; > - auto all = std::full_extent; > > auto check = [&](auto exts, size_t expected) > { > @@ -123,6 +182,52 @@ template<typename Layout> > return true; > } > > +template<typename Layout> > +constexpr size_t static_padding_value = 1; > + > +template<size_t PaddingValue> > +constexpr size_t static_padding_value<std::layout_left_padded<PaddingValue>> > = PaddingValue; > + > +template<size_t PaddingValue> > +constexpr size_t > static_padding_value<std::layout_right_padded<PaddingValue>> = PaddingValue; > + > +template<typename Layout> > + constexpr bool > + test_layout_padded_padding_value() > + { > + using Traits = LayoutTraits<DeducePaddingSide::from_typename<Layout>()>; > + auto s0 = std::strided_slice{size_t(1), size_t(2), std::cw<size_t(1)>}; > + auto s3 = std::strided_slice{size_t(2), size_t(5), std::cw<size_t(1)>}; > + > + auto check = [&](auto exts, size_t expected) > + { > + auto m = typename Layout::mapping(Traits::make_extents(exts)); > + auto slices = std::tuple{s0, size_t(0), all, s3, size_t(0)}; > + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); > + auto padding_value = decltype(result.mapping)::padding_value; > + VERIFY(padding_value == expected); > + }; > + > + auto pad = [](int n, int m) -> size_t > + { > + constexpr auto padding_value = static_padding_value<Layout>; > + if constexpr (padding_value != dyn) > + { > + auto npad = ((n + padding_value - 1) / padding_value) * > padding_value; > + return npad * m; > + } > + else > + return dyn; > + }; > + > + check(std::extents(std::cw<3>, std::cw<5>, std::cw<7>, 11, 13), pad(3, > 5)); > + check(std::extents(std::cw<3>, std::cw<5>, 7, 11, 13), pad(3, 5)); > + check(std::extents(std::cw<3>, std::cw<6>, 7, 11, 13), pad(3, 6)); > + check(std::extents(std::cw<3>, 5, 7, 11, 13), dyn); > + check(std::extents(3, 5, 7, 11, 13), dyn); > + return true; > + } > + > constexpr bool > test_layout_stride_return_types() > { > @@ -138,23 +243,55 @@ test_layout_stride_return_types() > return true; > } > > +template<typename Layout> > + constexpr bool > + test_return_types_all() > + { > + return true; > + } > + > +template<typename Layout> > + constexpr bool > + test_return_types_unpadded_all() > + { > + test_layout_common_return_types<Layout>(); > + static_assert(test_layout_common_return_types<Layout>()); > + > + test_layout_unpadded_return_types<Layout>(); > + static_assert(test_layout_unpadded_return_types<Layout>()); > + > + test_layout_unpadded_padding_value<Layout>(); > + static_assert(test_layout_unpadded_padding_value<Layout>()); > + return true; > + } > + > +template<typename Layout> > + constexpr bool > + test_return_types_padded_all() > + { > + test_layout_common_return_types<Layout>(); > + static_assert(test_layout_common_return_types<Layout>()); > + > + test_layout_padded_return_types<Layout>(); > + static_assert(test_layout_padded_return_types<Layout>()); > + > + test_layout_padded_padding_value<Layout>(); > + static_assert(test_layout_padded_padding_value<Layout>()); > + return true; > + } > + > int > main() > { > - test_layout_unpadded_return_types<std::layout_left>(); > - static_assert(test_layout_unpadded_return_types<std::layout_left>()); > + test_return_types_unpadded_all<std::layout_left>(); > + test_return_types_unpadded_all<std::layout_right>(); > > - test_layout_unpadded_return_types<std::layout_right>(); > - static_assert(test_layout_unpadded_return_types<std::layout_right>()); > + test_return_types_padded_all<std::layout_left_padded<1>>(); > + test_return_types_padded_all<std::layout_left_padded<2>>(); > + test_return_types_padded_all<std::layout_left_padded<dyn>>(); > > test_layout_stride_return_types(); > static_assert(test_layout_stride_return_types()); > - > - test_layout_unpadded_padding_value<std::layout_left>(); > - static_assert(test_layout_unpadded_padding_value<std::layout_left>()); > - > - test_layout_unpadded_padding_value<std::layout_right>(); > - static_assert(test_layout_unpadded_padding_value<std::layout_right>()); > return 0; > } > > -- > 2.52.0 >
