On Thu, Dec 4, 2025 at 10:31 PM Luc Grosheintz <[email protected]> wrote:
> Add submdspan_mapping for layout_stride as in P3663. > > PR libstdc++/110352 > > libstdc++-v3/ChangeLog: > > * include/std/mdspan (layout_stride::mapping::submdspan_mapping): > New > friend function. > * testsuite/23_containers/mdspan/submdspan/submdspan.cc: > Instantiate tests for layout_stride. > * testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc: > Ditto. > * testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc: > Add tests for layout_stride. > > Signed-off-by: Luc Grosheintz <[email protected]> > --- > This was really small. Except usual static_assert and test separation, I would remove _M_submdspan_impl and just inline the code into hidden friend, with if constexpr. > libstdc++-v3/include/std/mdspan | 25 +++++++++++++++++++ > .../mdspan/submdspan/submdspan.cc | 1 + > .../mdspan/submdspan/submdspan_mapping.cc | 18 +++++++++++++ > .../mdspan/submdspan/submdspan_neg.cc | 9 +++++++ > 4 files changed, 53 insertions(+) > > diff --git a/libstdc++-v3/include/std/mdspan > b/libstdc++-v3/include/std/mdspan > index d8da7b41868..8b6c24885ae 100644 > --- a/libstdc++-v3/include/std/mdspan > +++ b/libstdc++-v3/include/std/mdspan > @@ -1905,40 +1905,65 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > constexpr index_type > stride(rank_type __r) const noexcept { return _M_strides[__r]; } > > template<__mdspan::__mapping_alike _OMapping> > requires ((extents_type::rank() == _OMapping::extents_type::rank()) > && _OMapping::is_always_strided()) > friend constexpr bool > operator==(const mapping& __self, const _OMapping& __other) > noexcept > { > if (__self.extents() != __other.extents()) > return false; > if constexpr (extents_type::rank() > 0) > for (size_t __i = 0; __i < extents_type::rank(); ++__i) > if (!cmp_equal(__self.stride(__i), __other.stride(__i))) > return false; > return __mdspan::__offset(__other) == 0; > } > > private: > +#if __glibcxx_submdspan > + constexpr auto > + _M_submdspan_mapping_impl() const > + { return submdspan_mapping_result{*this, 0}; } > + > + template<typename... _Slices> > + requires (sizeof...(_Slices) > 0) > + constexpr auto > + _M_submdspan_mapping_impl(_Slices... __slices) const > + { > + auto __offset = __mdspan::__suboffset(*this, __slices...); > + auto __sub_exts = __mdspan::__subextents(extents(), __slices...); > + auto __sub_strides > + = __mdspan::__substrides<decltype(__sub_exts)>(*this, > __slices...); > + return submdspan_mapping_result{ > + layout_stride::mapping(__sub_exts, __sub_strides), __offset}; > + } > + > + template<__mdspan::__acceptable_slice_type<index_type>... _Slices> > This becomes a static_assert. > + requires (extents_type::rank() == sizeof...(_Slices)) > + friend constexpr auto > + submdspan_mapping(const mapping& __mapping, _Slices... __slices) > + { return __mapping._M_submdspan_mapping_impl(__slices...); } > I know that standard here specifies separate members, but there is no need to do so really, so I would put the implementation inside here, with if constexpr for sizeof...(Slices) == 0. > +#endif > + > using _Strides = typename __array_traits<index_type, > > extents_type::rank()>::_Type; > [[no_unique_address]] extents_type _M_extents; > [[no_unique_address]] _Strides _M_strides; > }; > > #ifdef __glibcxx_padded_layouts > namespace __mdspan > { > constexpr size_t > __least_multiple(size_t __x, size_t __y) > { > if (__x <= 1) > return __y; > return (__y / __x + (__y % __x != 0)) * __x ; > } > > template<typename _IndexType> > constexpr bool > __is_representable_least_multiple(size_t __x, size_t __y) > diff --git > a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc > b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc > index cd6e9454b17..645c4711294 100644 > --- a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc > +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc > @@ -349,22 +349,23 @@ template<typename Layout> > > run(std::extents(3, 5, 7)); > run(std::extents<int, 3, 5, 7>{}); > return true; > } > > template<typename Layout> > constexpr bool > test_all() > { > test_all_cheap<Layout>(); > test_all_expensive<Layout>(); > return true; > } > > int > main() > { > test_all<std::layout_left>(); > test_all<std::layout_right>(); > + test_all<std::layout_stride>(); > Same comment on separating the file. > 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 cc832cdb415..cf6167dc3b0 100644 > --- > a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc > +++ > b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc > @@ -106,37 +106,55 @@ template<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) > { > 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); > }; > > check(std::extents(std::cw<3>, std::cw<5>, std::cw<7>, 11, 13), 3*5); > check(std::extents(std::cw<3>, std::cw<5>, 7, 11, 13), 3*5); > 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() > +{ > + auto exts = std::extents(3, 5); > + auto m = std::layout_stride::mapping(exts, std::array{2, 12}); > + > + using index_type = decltype(exts)::index_type; > + auto s1 = std::strided_slice{index_type(2), index_type(2), > + std::cw<index_type(2)>}; > + auto result = submdspan_mapping(m, index_type(1), s1); > + using layout_type = decltype(result.mapping)::layout_type; > + static_assert(std::same_as<layout_type, std::layout_stride>); > + return true; > +} > + > int > main() > { > test_layout_unpadded_return_types<std::layout_left>(); > static_assert(test_layout_unpadded_return_types<std::layout_left>()); > > test_layout_unpadded_return_types<std::layout_right>(); > static_assert(test_layout_unpadded_return_types<std::layout_right>()); > > + 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; > } > > diff --git > a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc > b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc > index 5d977822dfe..8ee8af08fc1 100644 > --- > a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc > +++ > b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc > @@ -8,120 +8,129 @@ template<typename Layout, typename... Slices> > check_slice_range(Slices... slices) > { > auto m = typename Layout::mapping<std::extents<int, 3, 5, 7>>{}; > auto storage = std::vector<double>(m.required_span_size()); > auto md = std::mdspan(storage.data(), m); > > auto submd = submdspan(md, slices...); // { dg-error > "expansion of" } > (void) submd; > return true; > } > > template<typename Layout> > constexpr bool > test_int_under() > { > check_slice_range<Layout>(1, -1, 2); // { dg-error > "expansion of" } > return true; > } > static_assert(test_int_under<std::layout_left>()); // { dg-error > "expansion of" } > static_assert(test_int_under<std::layout_right>()); // { dg-error > "expansion of" } > +static_assert(test_int_under<std::layout_stride>()); // { dg-error > "expansion of" } > > template<typename Layout> > constexpr bool > test_int_over() > { > check_slice_range<Layout>(1, 5, 2); // { dg-error > "expansion of" } > return true; > } > static_assert(test_int_over<std::layout_left>()); // { dg-error > "expansion of" } > static_assert(test_int_over<std::layout_right>()); // { dg-error > "expansion of" } > +static_assert(test_int_over<std::layout_stride>()); // { dg-error > "expansion of" } > > template<typename Layout> > constexpr bool > test_tuple_under() > { > check_slice_range<Layout>(1, std::tuple{-1, 2}, 2); // { dg-error > "expansion of" } > return true; > } > static_assert(test_tuple_under<std::layout_left>()); // { dg-error > "expansion of" } > static_assert(test_tuple_under<std::layout_right>()); // { dg-error > "expansion of" } > +static_assert(test_tuple_under<std::layout_stride>()); // { dg-error > "expansion of" } > > template<typename Layout> > constexpr bool > test_tuple_reversed() > { > check_slice_range<Layout>(1, std::tuple{3, 2}, 2); // { dg-error > "expansion of" } > return true; > } > static_assert(test_tuple_reversed<std::layout_left>()); // { dg-error > "expansion of" } > static_assert(test_tuple_reversed<std::layout_right>()); // { dg-error > "expansion of" } > +static_assert(test_tuple_reversed<std::layout_stride>()); // { dg-error > "expansion of" } > > template<typename Layout> > constexpr bool > test_tuple_over() > { > check_slice_range<Layout>(1, std::tuple{0, 6}, 2); // { dg-error > "expansion of" } > return true; > } > static_assert(test_tuple_over<std::layout_left>()); // { dg-error > "expansion of" } > static_assert(test_tuple_over<std::layout_right>()); // { dg-error > "expansion of" } > +static_assert(test_tuple_over<std::layout_stride>()); // { dg-error > "expansion of" } > > template<typename Layout> > constexpr bool > test_strided_slice_zero() > { > check_slice_range<Layout>(1, std::strided_slice{1, 1, 0}, 2); // { > dg-error "expansion of" } > return true; > } > static_assert(test_strided_slice_zero<std::layout_left>()); // { > dg-error "expansion of" } > static_assert(test_strided_slice_zero<std::layout_right>()); // { > dg-error "expansion of" } > +static_assert(test_strided_slice_zero<std::layout_stride>()); // { > dg-error "expansion of" } > > template<typename Layout> > constexpr bool > test_strided_slice_offset_under() > { > check_slice_range<Layout>(1, std::strided_slice{-1, 1, 1}, 2); // { > dg-error "expansion of" } > return true; > } > static_assert(test_strided_slice_offset_under<std::layout_left>()); // > { dg-error "expansion of" } > static_assert(test_strided_slice_offset_under<std::layout_right>()); // > { dg-error "expansion of" } > +static_assert(test_strided_slice_offset_under<std::layout_stride>()); // > { dg-error "expansion of" } > > template<typename Layout> > constexpr bool > test_strided_slice_offset_over() > { > check_slice_range<Layout>(1, std::strided_slice{6, 0, 1}, 2); // { > dg-error "expansion of" } > return true; > } > static_assert(test_strided_slice_offset_over<std::layout_left>()); // { > dg-error "expansion of" } > static_assert(test_strided_slice_offset_over<std::layout_right>()); // { > dg-error "expansion of" } > +static_assert(test_strided_slice_offset_over<std::layout_stride>()); // { > dg-error "expansion of" } > > template<typename Layout> > constexpr bool > test_strided_slice_extent_over() > { > check_slice_range<Layout>(1, std::strided_slice{1, 5, 1}, 2); // { > dg-error "expansion of" } > return true; > } > static_assert(test_strided_slice_extent_over<std::layout_left>()); // { > dg-error "expansion of" } > static_assert(test_strided_slice_extent_over<std::layout_right>()); // { > dg-error "expansion of" } > +static_assert(test_strided_slice_extent_over<std::layout_stride>()); // { > dg-error "expansion of" } > > namespace adl > { > struct NoFull > { > template<typename Extents> > class mapping > { > public: > using extents_type = Extents; > using index_type = typename extents_type::index_type; > > private: > friend constexpr auto > submdspan_mapping(mapping, int) > { return std::submdspan_mapping_result{mapping{}, 0}; } > }; > }; > > struct WrongReturnValue > -- > 2.52.0 > >
