On 5/22/25 14:37, Tomasz Kaminski wrote:
On Wed, May 21, 2025 at 12:04 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 | 216 +++++++++++++++++++++++++++++++-
  1 file changed, 213 insertions(+), 3 deletions(-)

diff --git a/libstdc++-v3/include/std/mdspan
b/libstdc++-v3/include/std/mdspan
index 43676c3463c..732fc4eb1c2 100644
--- a/libstdc++-v3/include/std/mdspan
+++ b/libstdc++-v3/include/std/mdspan
@@ -399,6 +399,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        class mapping;
    };

+  struct layout_stride
+  {
+    template<typename _Extents>
+      class mapping;
+  };
+
    namespace __mdspan
    {
      template<typename _Tp>
@@ -499,7 +505,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
@@ -557,6 +564,13 @@ _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)

I think I would make it noexcept, as implementations can add noexcept to
what is specified in standard.
And just add appropriate comment.

Alright, I prepared this as a separate commit. I don't remember coming
to a conclusion, when it was discussed in the the email chain about
potential bugs in the standard. IIUC Jonathan thought the missing noexcept
was both preferable and an intentional part of the standard.

We should probably make sure he's aware we made this choice (the separate
commit should help).


+       : mapping(__other.extents(), __mdspan::__internal_ctor{})
+       { __glibcxx_assert(*this == __other); }
+
        constexpr mapping&
        operator=(const mapping&) noexcept = default;

@@ -572,8 +586,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)...);

Could you move this change to  layout_left commit.

         }

        static constexpr bool
@@ -687,6 +701,13 @@ _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(*this == __other); }
+
        constexpr mapping&
        operator=(const mapping&) noexcept = default;

@@ -760,6 +781,195 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
         [[no_unique_address]] _Extents _M_extents;
      };

+  namespace __mdspan
+  {
+    template<typename _Mapping>
+      constexpr typename _Mapping::index_type
+      __offset(const _Mapping& __m) noexcept
+      {
+       using _IndexType = typename _Mapping::index_type;
+
+       auto __impl = [&__m]<size_t... _Counts>(index_sequence<_Counts...>)
+       { return __m(((void) _Counts, _IndexType(0))...); };
+       return
__impl(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
+    {
+    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;
+
+      static_assert(__mdspan::__representable_size<_Extents, index_type>,
+       "The size of extents_type must be representable as index_type");
+
+      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()))

I think this would also work: _M_extents(__other.extents())

+       {

We could guard runtime check, with compile time for size)
   if constexpr(std::numeric_limits<typename
_StridedMapping::index_type>::max()
                           >


+         if constexpr (extents_type::rank() > 0)
+           for (size_t __i = 0; __i < extents_type::rank(); ++__i)
+             _M_strides[__i] = index_type(__other.stride(__i));

I would add assert if other.required_span_size() <= numeric_limits::max.

+       }
+
+      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 {
+       array<index_type, extents_type::rank()> __ret;
+       for (size_t __i = 0; __i < extents_type::rank(); ++__i)
+         __ret[__i] = _M_strides[__i];
+       return __ret;
+      }
+
+      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];

If the index type is sized, this can overflow and cause UB, before we find
zero.
We need to check first if any of the extents is zero, before attempting
multiplication.

+         }
+       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
+       {

The brace in else should be indented.

+         // 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.

Could re rephrase this:
        // We deviate from the standard, that does not thread all zero
extents as exhaustive.

+         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




Reply via email to