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.