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

Reply via email to