On Mon, 7 Jul 2025 at 11:11, Luc Grosheintz <luc.groshei...@gmail.com> wrote:
>
>
>
> On 7/7/25 11:52, Jonathan Wakely wrote:
> > On Fri, 4 Jul 2025 at 09:37, Luc Grosheintz <luc.groshei...@gmail.com> 
> > wrote:
> >>
> >> Implements the class mdspan as described in N4950, i.e. without P3029.
> >> It also adds tests for mdspan. This commit completes the implementation
> >> of P0009, i.e. the C++23 part <mdspan>.
> >>
> >>          PR libstdc++/107761
> >>
> >> libstdc++-v3/ChangeLog:
> >>
> >>          * include/std/mdspan (mdspan): New class.
> >>          * src/c++23/std.cc.in (mdspan): Add.
> >>          * testsuite/23_containers/mdspan/class_mandate_neg.cc: New test.
> >>          * testsuite/23_containers/mdspan/mdspan.cc: New test.
> >>          * testsuite/23_containers/mdspan/layout_like.h: Add class
> >>          LayoutLike which models a user-defined layout.
> >>
> >> Signed-off-by: Luc Grosheintz <luc.groshei...@gmail.com>
> >> ---
> >>   libstdc++-v3/include/std/mdspan               | 284 +++++++++
> >>   libstdc++-v3/src/c++23/std.cc.in              |   3 +-
> >>   .../23_containers/mdspan/class_mandate_neg.cc |  41 ++
> >>   .../23_containers/mdspan/layout_like.h        |  80 +++
> >>   .../testsuite/23_containers/mdspan/mdspan.cc  | 603 ++++++++++++++++++
> >>   .../23_containers/mdspan/out_of_bounds_neg.cc |  24 +
> >>   6 files changed, 1034 insertions(+), 1 deletion(-)
> >>   create mode 100644 
> >> libstdc++-v3/testsuite/23_containers/mdspan/class_mandate_neg.cc
> >>   create mode 100644 
> >> libstdc++-v3/testsuite/23_containers/mdspan/layout_like.h
> >>   create mode 100644 libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
> >>   create mode 100644 
> >> libstdc++-v3/testsuite/23_containers/mdspan/out_of_bounds_neg.cc
> >>
> >> diff --git a/libstdc++-v3/include/std/mdspan 
> >> b/libstdc++-v3/include/std/mdspan
> >> index 7e970c2b905..f64804e2a42 100644
> >> --- a/libstdc++-v3/include/std/mdspan
> >> +++ b/libstdc++-v3/include/std/mdspan
> >> @@ -1057,6 +1057,290 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>         { return __p + __i; }
> >>       };
> >>
> >> +  namespace __mdspan
> >> +  {
> >> +    template<typename _Extents, typename _IndexType, size_t _Nm>
> >> +      constexpr bool
> >> +      __is_multi_index(const _Extents& __exts, span<_IndexType, _Nm> 
> >> __indices)
> >> +      {
> >> +       static_assert(__exts.rank() == _Nm);
> >> +       for (size_t __i = 0; __i < __exts.rank(); ++__i)
> >> +         if (__indices[__i] >= __exts.extent(__i))
> >> +           return false;
> >> +       return true;
> >> +      }
> >> +  }
> >> +
> >> +  template<typename _ElementType, typename _Extents,
> >> +          typename _LayoutPolicy = layout_right,
> >> +          typename _AccessorPolicy = default_accessor<_ElementType>>
> >> +    class mdspan
> >> +    {
> >> +      static_assert(!is_array_v<_ElementType>,
> >> +       "ElementType must not be an array type");
> >> +      static_assert(!is_abstract_v<_ElementType>,
> >> +       "ElementType must not be an abstract class type");
> >> +      static_assert(__mdspan::__is_extents<_Extents>,
> >> +       "Extents must be a specialization of std::extents");
> >> +      static_assert(is_same_v<_ElementType,
> >> +                             typename _AccessorPolicy::element_type>);
> >> +
> >> +    public:
> >> +      using extents_type = _Extents;
> >> +      using layout_type = _LayoutPolicy;
> >> +      using accessor_type = _AccessorPolicy;
> >> +      using mapping_type = typename layout_type::template 
> >> mapping<extents_type>;
> >> +      using element_type = _ElementType;
> >> +      using value_type = remove_cv_t<element_type>;
> >> +      using index_type = typename extents_type::index_type;
> >> +      using size_type = typename extents_type::size_type;
> >> +      using rank_type = typename extents_type::rank_type;
> >> +      using data_handle_type = typename accessor_type::data_handle_type;
> >> +      using reference = typename accessor_type::reference;
> >> +
> >> +      static constexpr rank_type
> >> +      rank() noexcept { return extents_type::rank(); }
> >> +
> >> +      static constexpr rank_type
> >> +      rank_dynamic() noexcept { return extents_type::rank_dynamic(); }
> >> +
> >> +      static constexpr size_t
> >> +      static_extent(rank_type __r) noexcept
> >> +      { return extents_type::static_extent(__r); }
> >> +
> >> +      constexpr index_type
> >> +      extent(rank_type __r) const noexcept { return 
> >> extents().extent(__r); }
> >> +
> >> +      constexpr
> >> +      mdspan()
> >> +      requires (rank_dynamic() > 0)
> >> +          && is_default_constructible_v<data_handle_type>
> >> +         && is_default_constructible_v<mapping_type>
> >> +         && is_default_constructible_v<accessor_type>
> >> +      : _M_accessor(), _M_mapping(), _M_handle()
> >> +      { }
> >> +
> >> +      constexpr
> >> +      mdspan(const mdspan& __other) = default;
> >> +
> >> +      constexpr
> >> +      mdspan(mdspan&& __other) = default;
> >> +
> >> +      template<__mdspan::__valid_index_type<index_type>... _OIndexTypes>
> >> +       requires (sizeof...(_OIndexTypes) == rank()
> >> +                  || sizeof...(_OIndexTypes) == rank_dynamic())
> >> +                && is_constructible_v<mapping_type, extents_type>
> >> +                && is_default_constructible_v<accessor_type>
> >> +       constexpr explicit
> >> +       mdspan(data_handle_type __handle, _OIndexTypes... __exts)
> >> +       : _M_accessor(),
> >> +         
> >> _M_mapping(_Extents(static_cast<index_type>(std::move(__exts))...)),
> >> +         _M_handle(std::move(__handle))
> >> +       { }
> >> +
> >> +      template<__mdspan::__valid_index_type<index_type> _OIndexType,
> >> +              size_t _Nm>
> >> +       requires (_Nm == rank() || _Nm == rank_dynamic())
> >> +                && is_constructible_v<mapping_type, extents_type>
> >> +                && is_default_constructible_v<accessor_type>
> >> +       constexpr explicit(_Nm != rank_dynamic())
> >> +       mdspan(data_handle_type __handle, span<_OIndexType, _Nm> __exts)
> >> +       : _M_accessor(), _M_mapping(extents_type(__exts)),
> >> +         _M_handle(std::move(__handle))
> >> +       { }
> >> +
> >> +      template<__mdspan::__valid_index_type<index_type> _OIndexType,
> >> +              size_t _Nm>
> >> +       requires (_Nm == rank() || _Nm == rank_dynamic())
> >> +                && is_constructible_v<mapping_type, extents_type>
> >> +                && is_default_constructible_v<accessor_type>
> >> +       constexpr explicit(_Nm != rank_dynamic())
> >> +       mdspan(data_handle_type __handle, const array<_OIndexType, _Nm>& 
> >> __exts)
> >> +       : _M_accessor(), _M_mapping(extents_type(__exts)),
> >> +         _M_handle(std::move(__handle))
> >> +       { }
> >> +
> >> +      constexpr
> >> +      mdspan(data_handle_type __handle, const extents_type& __exts)
> >> +      requires is_constructible_v<mapping_type, const extents_type&>
> >> +              && is_default_constructible_v<accessor_type>
> >> +      : _M_accessor(), _M_mapping(__exts), _M_handle(std::move(__handle))
> >> +      { }
> >> +
> >> +      constexpr
> >> +      mdspan(data_handle_type __handle, const mapping_type& __mapping)
> >> +      requires is_default_constructible_v<accessor_type>
> >> +      : _M_accessor(), _M_mapping(__mapping), 
> >> _M_handle(std::move(__handle))
> >> +      { }
> >> +
> >> +      constexpr
> >> +      mdspan(data_handle_type __handle, const mapping_type& __mapping,
> >> +            const accessor_type& __accessor)
> >> +      : _M_accessor(__accessor), _M_mapping(__mapping),
> >> +        _M_handle(std::move(__handle))
> >> +      { }
> >> +
> >> +      template<typename _OElementType, typename _OExtents, typename 
> >> _OLayout,
> >> +              typename _OAccessor>
> >> +       requires is_constructible_v<mapping_type,
> >> +             const typename _OLayout::mapping<_OExtents>&>
> >> +         && is_constructible_v<accessor_type, const _OAccessor&>
> >> +       constexpr explicit(!is_convertible_v<
> >> +           const typename _OLayout::mapping<_OExtents>&, mapping_type>
> >> +         || !is_convertible_v<const _OAccessor&, accessor_type>)
> >> +       mdspan(const mdspan<_OElementType, _OExtents, _OLayout, 
> >> _OAccessor>&
> >> +                __other)
> >> +       : _M_accessor(__other.accessor()), _M_mapping(__other.mapping()),
> >> +         _M_handle(__other.data_handle())
> >> +       {
> >> +         static_assert(is_constructible_v<data_handle_type,
> >> +             const typename _OAccessor::data_handle_type&>);
> >> +         static_assert(is_constructible_v<extents_type, _OExtents>);
> >> +       }
> >> +
> >> +      constexpr mdspan&
> >> +      operator=(const mdspan& __other) = default;
> >> +
> >> +      constexpr mdspan&
> >> +      operator=(mdspan&& __other) = default;
> >> +
> >> +      template<__mdspan::__valid_index_type<index_type>... _OIndexTypes>
> >> +       requires (sizeof...(_OIndexTypes) == rank())
> >> +       constexpr reference
> >> +       operator[](_OIndexTypes... __indices) const
> >> +       {
> >> +         auto __checked_call = [this](auto... __idxs) -> index_type
> >> +           {
> >> +             if constexpr (sizeof...(__idxs) > 0)
> >> +               __glibcxx_assert(__mdspan::__is_multi_index(extents(),
> >> +                 span<const index_type, sizeof...(__idxs)>({__idxs...})));
> >> +             return _M_mapping(__idxs...);
> >> +           };
> >> +
> >> +         auto __index = __checked_call(
> >> +           static_cast<index_type>(std::move(__indices))...);
> >> +         return _M_accessor.access(_M_handle, __index);
> >> +       }
> >> +
> >> +      template<__mdspan::__valid_index_type<index_type> _OIndexType>
> >> +       constexpr reference
> >> +       operator[](span<_OIndexType, rank()> __indices) const
> >> +       {
> >> +         auto __call = [&]<size_t... _Counts>(index_sequence<_Counts...>)
> >> +           -> reference
> >> +           { return (*this)[index_type(as_const(__indices[_Counts]))...]; 
> >> };
> >> +         return __call(make_index_sequence<rank()>());
> >> +       }
> >> +
> >> +      template<__mdspan::__valid_index_type<index_type> _OIndexType>
> >> +       constexpr reference
> >> +       operator[](const array<_OIndexType, rank()>& __indices) const
> >> +       { return (*this)[span<const _OIndexType, rank()>(__indices)]; }
> >> +
> >> +      constexpr size_type
> >> +      size() const noexcept
> >> +      {
> >> +       __glibcxx_assert(cmp_less_equal(_M_mapping.required_span_size(),
> >> +                                       numeric_limits<size_t>::max()));
> >> +       return size_type(__mdspan::__size(extents()));
> >> +      }
> >> +
> >> +      [[nodiscard]]
> >> +      constexpr bool
> >> +      empty() const noexcept
> >> +      {
> >> +       return __mdspan::__empty(extents());
> >> +      }
> >> +
> >> +      friend constexpr void
> >> +      swap(mdspan& __x, mdspan& __y) noexcept
> >> +      {
> >> +       std::swap(__x._M_mapping, __y._M_mapping);
> >> +       std::swap(__x._M_accessor, __y._M_accessor);
> >> +       std::swap(__x._M_handle, __y._M_handle);
> >
> > Do we care about finding custom swap overlaods via ADL for any of these 
> > types?
>
> The standard reads:
>
> https://eel.is/c++draft/mdspan.mdspan.members#10

And due to [swappable.requirements] that means it must be implemented as:

using std::swap;
swap(__x._M_mapping, __y._M_mapping);
swap(__x._M_accessor, __y._M_accessor);
swap(__x._M_handle, __y._M_handle);


> So I think I made a mistake. If you and Tomasz like, I can also write
> the tests needed to chatch this (to prevent a regression).

That would be useful, thanks.

See e.g. commit  0368c42507328774cadbea589509b95aaf3cb826 for a
similar case with tests to verify that ADL swap gets used.

Reply via email to