On Tue, May 20, 2025 at 10:45 AM Luc Grosheintz <luc.groshei...@gmail.com> wrote:
> > > On 5/20/25 10:24 AM, Tomasz Kaminski wrote: > > On Sun, May 18, 2025 at 10:16 PM Luc Grosheintz < > luc.groshei...@gmail.com> > > wrote: > > > >> Implements the remaining parts of layout_left and layout_right; and all > >> of layout_stride. > >> > >> libstdc++-v3/ChangeLog: > >> > >> * include/std/mdspan(layout_stride): New class. > >> > >> Signed-off-by: Luc Grosheintz <luc.groshei...@gmail.com> > >> --- > >> libstdc++-v3/include/std/mdspan | 219 +++++++++++++++++++++++++++++++- > >> 1 file changed, 216 insertions(+), 3 deletions(-) > >> > >> diff --git a/libstdc++-v3/include/std/mdspan > >> b/libstdc++-v3/include/std/mdspan > >> index b1984eb2a33..31a38c736c2 100644 > >> --- a/libstdc++-v3/include/std/mdspan > >> +++ b/libstdc++-v3/include/std/mdspan > >> @@ -366,6 +366,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > >> class mapping; > >> }; > >> > >> + struct layout_stride > >> + { > >> + template<typename _Extents> > >> + class mapping; > >> + }; > >> + > >> namespace __mdspan > >> { > >> template<typename _Tp> > >> @@ -434,7 +440,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > >> > >> template<typename _Mapping> > >> concept __standardized_mapping = __mapping_of<layout_left, > _Mapping> > >> - || __mapping_of<layout_right, > >> _Mapping>; > >> + || __mapping_of<layout_right, > >> _Mapping> > >> + || __mapping_of<layout_stride, > >> _Mapping>; > >> > >> template<typename M> > >> concept __mapping_like = requires > >> @@ -503,6 +510,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > >> : mapping(__other.extents(), __mdspan::__internal_ctor{}) > >> { } > >> > >> + template<typename _OExtents> > >> + requires (is_constructible_v<extents_type, _OExtents>) > >> + constexpr explicit(extents_type::rank() > 0) > >> + mapping(const layout_stride::mapping<_OExtents>& __other) > >> + : mapping(__other.extents(), __mdspan::__internal_ctor{}) > >> + { > >> + __glibcxx_assert( > >> + layout_left::mapping<_OExtents>(__other.extents()) == > __other); > >> > > Could this be *this == other? > > > >> + } > >> + > >> constexpr mapping& > >> operator=(const mapping&) noexcept = default; > >> > >> @@ -518,8 +535,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > >> constexpr index_type > >> operator()(_Indices... __indices) const noexcept > >> { > >> - return __mdspan::__linear_index_left( > >> - this->extents(), static_cast<index_type>(__indices)...); > >> + return __mdspan::__linear_index_left(_M_extents, > >> + static_cast<index_type>(__indices)...); > >> } > >> > >> static constexpr bool > >> @@ -633,6 +650,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > >> : mapping(__other.extents(), __mdspan::__internal_ctor{}) > >> { } > >> > >> + template<class _OExtents> > >> + requires (is_constructible_v<extents_type, _OExtents>) > >> + constexpr explicit(extents_type::rank() > 0) > >> + mapping(const layout_stride::mapping<_OExtents>& __other) > noexcept > >> + : mapping(__other.extents(), __mdspan::__internal_ctor{}) > >> + { > >> + __glibcxx_assert( > >> + layout_right::mapping<_OExtents>(__other.extents()) == > >> __other); > >> > > Similary here. > > > >> + } > >> + > >> constexpr mapping& > >> operator=(const mapping&) noexcept = default; > >> > >> @@ -695,6 +722,192 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > >> [[no_unique_address]] _Extents _M_extents; > >> }; > >> > >> + namespace __mdspan > >> + { > >> + template<typename _Mapping, size_t... _Counts> > >> + constexpr typename _Mapping::index_type > >> + __offset_impl(const _Mapping& __m, index_sequence<_Counts...>) > >> noexcept > >> + { return __m(((void) _Counts, 0)...); } > >> + > >> + template<typename _Mapping> > >> + constexpr typename _Mapping::index_type > >> + __offset(const _Mapping& __m) noexcept > >> + { > >> > > Again, I would define __impl as nested lambda here: > > auto __impl = [&]<size_t... Countws>(index_seqeunce<_Counts>) noexcept > > { return __m(((void) _Counts, 0)...); } > > > >> + return __offset_impl(__m, > >> + make_index_sequence<_Mapping::extents_type::rank()>()); > >> + } > >> + > >> + template<typename _Mapping, typename... _Indices> > >> + constexpr typename _Mapping::index_type > >> + __linear_index_strides(const _Mapping& __m, > >> + _Indices... __indices) > >> + { > >> + using _IndexType = typename _Mapping::index_type; > >> + _IndexType __res = 0; > >> + if constexpr (sizeof...(__indices) > 0) > >> + { > >> + auto __update = [&, __pos = 0u](_IndexType __idx) mutable > >> + { > >> + __res += __idx * __m.stride(__pos++); > >> + }; > >> + (__update(__indices), ...); > >> + } > >> + return __res; > >> + } > >> + } > >> + > >> + template<typename _Extents> > >> + class layout_stride::mapping > >> + { > >> + static_assert(__mdspan::__layout_extent<_Extents>, > >> + "The size of extents_type must be representable as index_type"); > >> + > >> + public: > >> + using extents_type = _Extents; > >> + 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 layout_type = layout_stride; > >> + > >> + constexpr > >> + mapping() noexcept > >> + { > >> + auto __stride = index_type(1); > >> + for(size_t __i = extents_type::rank(); __i > 0; --__i) > >> + { > >> + _M_strides[__i - 1] = __stride; > >> + __stride *= _M_extents.extent(__i - 1); > >> + } > >> + } > >> + > >> + constexpr > >> + mapping(const mapping&) noexcept = default; > >> + > >> + template<__mdspan::__valid_index_type<index_type> _OIndexType> > >> + constexpr > >> + mapping(const extents_type& __exts, > >> + span<_OIndexType, extents_type::rank()> __strides) > noexcept > >> + : _M_extents(__exts) > >> + { > >> + for(size_t __i = 0; __i < extents_type::rank(); ++__i) > >> + _M_strides[__i] = index_type(as_const(__strides[__i])); > >> + } > >> + > >> + template<__mdspan::__valid_index_type<index_type> _OIndexType> > >> + constexpr > >> + mapping(const extents_type& __exts, > >> + const array<_OIndexType, extents_type::rank()>& > __strides) > >> + noexcept > >> + : mapping(__exts, > >> + span<const _OIndexType, > extents_type::rank()>(__strides)) > >> + { } > >> + > >> + template<__mdspan::__mapping_like _StridedMapping> > >> + requires (is_constructible_v<extents_type, > >> + typename > >> _StridedMapping::extents_type> > >> + && _StridedMapping::is_always_unique() > >> + && _StridedMapping::is_always_strided()) > >> + constexpr explicit(!( > >> + is_convertible_v<typename _StridedMapping::extents_type, > >> extents_type> > >> + && __mdspan::__standardized_mapping<_StridedMapping>)) > >> + mapping(const _StridedMapping& __other) noexcept > >> + : _M_extents(extents_type(__other.extents())) > >> + { > >> + if constexpr (extents_type::rank() > 0) > >> + for(size_t __i = 0; __i < extents_type::rank(); ++__i) > >> + _M_strides[__i] = index_type(__other.stride(__i)); > >> + } > >> + > >> + constexpr mapping& > >> + operator=(const mapping&) noexcept = default; > >> + > >> + constexpr const _Extents& > >> + extents() const noexcept { return _M_extents; } > >> + > >> + constexpr array<index_type, extents_type::rank()> > >> + strides() const noexcept { return _M_strides; } > >> > > Does this constructor work? I mean _M_strides in not std::array, > > just array. I assumed you would need to create local array and copy them. > > > >> + > >> + constexpr index_type > >> + required_span_size() const noexcept > >> + { > >> + index_type __ret = 1; > >> + for(size_t __i = 0; __i < extents_type::rank(); ++__i) > >> + { > >> + index_type __ext = _M_extents.extent(__i); > >> + if(__ext == 0) > >> + return 0; > >> + __ret += (__ext - 1) * _M_strides[__i]; > >> + } > >> + return __ret; > >> + } > >> + > >> + template<__mdspan::__valid_index_type<index_type>... _Indices> > >> + requires (sizeof...(_Indices) == extents_type::rank()) > >> + constexpr index_type operator()(_Indices... __indices) const > >> noexcept > >> + { > >> + return __mdspan::__linear_index_strides(*this, > >> + static_cast<index_type>(__indices)...); > >> + } > >> + > >> + static constexpr bool > >> + is_always_unique() noexcept { return true; } > >> + > >> + static constexpr bool > >> + is_always_exhaustive() noexcept { return false; } > >> + > >> + static constexpr bool > >> + is_always_strided() noexcept { return true; } > >> + > >> + static constexpr bool > >> + is_unique() noexcept { return true; } > >> + > >> + constexpr bool > >> + is_exhaustive() const noexcept > >> + { > >> + if constexpr (extents_type::rank() == 0) > >> + return true; > >> + else > >> + { > >> + // Under the assumption that the mapping is unique and has > >> positive > >> + // size, the mapping is exhaustive, if and only if the largest > >> value > >> + // returned by m(i...) is within the required range. > >> + // However, the standard requires implementing a condition > >> that's not > >> + // always true when the size of the mapping is zero. > >> > > With the precondition on the constructor > > https://eel.is/c++draft/mdspan.layout.stride#cons-4.3, > > I do not think you can produce a layout_stride where this would produce a > > different result. > > If you agree I would change the comment that "C++23 > > [mdspan.layout.stride.cons] p4.3" > > implies that this produces the same result. > > If the size of the extent is zero, then almost any choice of strides > will work as a counter example. For example: > > extents = [0, 1, 1] > strides = [2, 4, 8] > > is exhaustive, because every element (of the empty set) is covered. > However, [2, 4, 8] don't match the formula in the spec [1]. > My point is that constructing layout_stride with extents and strides from the example violates the precondition on the constructor: https://eel.is/c++draft/mdspan.layout.stride#cons-4.3. So this function cannot observe the difference. > > If the size of extents isn't zero, then the two approaches are the > same. > > [1]: https://eel.is/c++draft/mdspan.layout.stride#obs-5.2 > > >> > >> + auto __size = __mdspan::__fwd_prod(_M_extents, > >> extents_type::rank()); > >> + if (__size == 0) > >> + return true; > >> + return __size == required_span_size(); > >> + } > >> + } > >> + > >> + static constexpr bool > >> + is_strided() noexcept { return true; } > >> + > >> + constexpr index_type > >> + stride(rank_type __r) const noexcept { return _M_strides[__r]; } > >> + > >> + template<__mdspan::__mapping_like _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 (__self.stride(__i) != __other.stride(__i)) > >> + return false; > >> + return __mdspan::__offset(__other) == 0; > >> + } > >> + > >> + private: > >> + using _S_strides_t = typename __array_traits<index_type, > >> + > >> extents_type::rank()>::_Type; > >> + [[no_unique_address]] _Extents _M_extents; > >> + [[no_unique_address]] _S_strides_t _M_strides; > >> + }; > >> + > >> _GLIBCXX_END_NAMESPACE_VERSION > >> } > >> #endif > >> -- > >> 2.49.0 > >> > >> > > > >