On 4/9/25 9:23 AM, Luc Grosheintz wrote:
This implements std::extents from <mdspan> according to N4950 and
contains partial progress towards PR107761.

If an extent changes its type, there's a precondition in the standard,
that the value is representable in the target integer type. This commit
uses a static_cast for all of these conversions, without any additional
checks.

The precondition for 'extents::{static_,}extent' is that '__r < rank()'.
For extents<T> this precondition is always violated and results in
calling __builtin_trap. For all other specializations it's checked via
__glibcxx_assert.

        PR libstdc++/107761

libstdc++-v3/ChangeLog:

        * include/std/mdspan (extents): New class.
        * src/c++23/std.cc.in: Add 'using std::extents'.

Signed-off-by: Luc Grosheintz <luc.groshei...@gmail.com>
---
  libstdc++-v3/include/std/mdspan  | 400 +++++++++++++++++++++++++++++++
  libstdc++-v3/src/c++23/std.cc.in |   6 +-
  2 files changed, 405 insertions(+), 1 deletion(-)

diff --git a/libstdc++-v3/include/std/mdspan b/libstdc++-v3/include/std/mdspan
index 4094a416d1e..5c6a1868d62 100644
--- a/libstdc++-v3/include/std/mdspan
+++ b/libstdc++-v3/include/std/mdspan
@@ -33,6 +33,10 @@
  #pragma GCC system_header
  #endif
+#include <span>
+#include <array>
+#include <type_traits>
+
  #define __glibcxx_want_mdspan
  #include <bits/version.h>
@@ -42,6 +46,402 @@ namespace std _GLIBCXX_VISIBILITY(default)
  {
  _GLIBCXX_BEGIN_NAMESPACE_VERSION
+ namespace __detail
+  {
+    // Computed as: sum_i (i == __r) * E_i
+    template<size_t _Count, size_t... _Extents>
+      struct _StaticExtent;
+
+    template<size_t _Count, size_t _Extent, size_t... _Extents>
+      struct _StaticExtent<_Count, _Extent, _Extents...>
+      {
+       static constexpr size_t
+       _M_value(size_t __r) noexcept
+       {
+         return (_Count == __r) * _Extent
+                + _StaticExtent<_Count + 1, _Extents...>::_M_value(__r);
+       }
+      };
+
+    template<size_t _Count>
+      struct _StaticExtent<_Count>
+      {
+       static constexpr size_t
+       _M_value(size_t __r) noexcept
+       { return 0; }
+      };
+
+    // Computed as: sum_i (i < __r) * (E_i == dynamic_extent)
+    template<size_t _Count, size_t... _Extents>
+      struct _DynamicIndex;
+
+    template<size_t _Count, size_t _Extent, size_t... _Extents>
+      struct _DynamicIndex<_Count, _Extent, _Extents...>
+      {
+       static constexpr size_t
+       _M_value(size_t __r) noexcept
+       {
+         return (_Count < __r) * (_Extent == dynamic_extent)
+                + _DynamicIndex<_Count + 1, _Extents...>::_M_value(__r);
+       }
+      };
+
+    template<size_t _Count>
+      struct _DynamicIndex<_Count>
+      {
+       static constexpr size_t
+       _M_value(size_t __r) noexcept
+       { return 0; }
+      };
+
+    // Computed by: counting the number of dynamic extents (_Dr); and returning
+    // the static index first time `__di == _Dr`.
+    template<size_t _Si, size_t _Dr, size_t... _Extents>
+      struct _DynamicIndexInv;
+
+    template<size_t _Si, size_t _Dr, size_t _Extent, size_t... _Extents>
+      struct _DynamicIndexInv<_Si, _Dr, _Extent, _Extents...>
+      {
+       static constexpr size_t
+       _M_value(size_t __di) noexcept
+       {
+         if (_Extent == dynamic_extent && __di == _Dr)
+           return _Si;
+         else
+           return _DynamicIndexInv<_Si+1, _Dr + (_Extent == dynamic_extent),
+                                   _Extents...>
+                  ::_M_value(__di);
+       }
+      };
+
+    template<size_t _Si, size_t _Dr, size_t _Extent>
+      struct _DynamicIndexInv<_Si, _Dr, _Extent>
+      {
+       static constexpr size_t
+       _M_value(size_t __di) noexcept
+       {
+         __glibcxx_assert(__di == _Dr);
+         return _Si;
+       }
+      };
+
+    // Aim: __ext[i] for template parameter packs.
+    template<size_t _Count, typename... _IndexTypes>
+      struct _GetPackElement;
+
+    template<size_t _Count, typename _IndexType, typename... _IndexTypes>
+      struct _GetPackElement<_Count, _IndexType, _IndexTypes...>
+      {
+       template<size_t _Index>
+         static constexpr const auto&
+         get(const _IndexType& __current, const _IndexTypes&... __rest)
+         {
+           if constexpr (_Index == _Count)
+             return __current;
+           else
+             return _GetPackElement<_Count+1, _IndexTypes...>
+                    ::template get<_Index>(__rest...);
+         }
+      };
+
+    template<size_t _Di, typename... _IndexTypes>
+      constexpr const auto&
+      __get_element(const _IndexTypes&... __exts)
+      {
+       return _GetPackElement<0, _IndexTypes...>
+              ::template get<_Di>(__exts...);
+      }
+
+    template<size_t... _Extents>
+      struct _DynamicRank
+      {
+       static constexpr size_t _S_value = ((_Extents == dynamic_extent) + ...);
+      };
+
+    template<>
+      struct _DynamicRank<>
+      {
+       static constexpr size_t _S_value = 0;
+      };
+
+    template<typename _IndexType, typename _DynamicIndexes, typename _Indexes,
+            size_t... _Extents>
+      struct _ExtentsStorage;
+
+    template<typename _IndexType, size_t... _DynamicIndexes, size_t... 
_Indexes,
+            size_t... _Extents>
+      struct _ExtentsStorage<_IndexType, index_sequence<_DynamicIndexes...>,
+                            index_sequence<_Indexes...>, _Extents...>
+      {
+       static constexpr size_t _S_rank = sizeof...(_Extents);
+
+       static constexpr size_t _S_rank_dynamic =
+         _DynamicRank<_Extents...>::_S_value;
+
+       static constexpr array<size_t, _S_rank_dynamic> _S_dynamic_index_inv{
+         _DynamicIndexInv<0, 0, _Extents...> ::_M_value(_DynamicIndexes)...};
+
+       static constexpr array<size_t, _S_rank> _S_dynamic_index {
+         _DynamicIndex<0, _Extents...>::_M_value(_Indexes)...};
+
+       template<typename _Tp>
+         static constexpr _IndexType
+         _M_int_cast(const _Tp& __other)
+         { return __other; }
+
+       constexpr _ExtentsStorage() = default;
+
+       template<typename _OIndexType, typename _ODynamicIndexes,
+                typename _OIndexes, size_t... _OExtents>
+         constexpr _ExtentsStorage(
+           const _ExtentsStorage<_OIndexType, _ODynamicIndexes, _OIndexes,
+                                 _OExtents...>& __other)
+         : _M_dynamic_extents{_M_int_cast(__other._M_extent(
+             _S_dynamic_index_inv[_DynamicIndexes]))...}
+         { }
+
+       template<size_t _Di, typename... _OIndexTypes>
+         static constexpr const auto&
+         _M_get_dynamic_element(const _OIndexTypes&... __exts)
+         { return __get_element<_S_dynamic_index_inv[_Di]>(__exts...); }
+
+       template<typename... _OIndexTypes>
+         requires (sizeof...(_OIndexTypes) == _S_rank_dynamic)
+                  && (is_convertible_v<_OIndexTypes, _IndexType> && ...)
+         constexpr
+         _ExtentsStorage(const _OIndexTypes&... __exts)
+         : _M_dynamic_extents{_M_int_cast(
+             __get_element<_DynamicIndexes>(__exts...))...}
+         { }
+
+       template<typename... _OIndexTypes>
+         requires (sizeof...(_OIndexTypes) != _S_rank_dynamic)
+                  && (is_convertible_v<_OIndexTypes, _IndexType> && ...)
+         constexpr
+         _ExtentsStorage(const _OIndexTypes&... __exts)
+         : _M_dynamic_extents{_M_int_cast(
+             _M_get_dynamic_element<_DynamicIndexes>(__exts...))...}
+         { }
+
+       template<typename _OIndexType, size_t _Nm>
+         requires (_Nm == _S_rank_dynamic)
+         constexpr
+         _ExtentsStorage(const span<_OIndexType, _Nm>& __exts)
+         : _M_dynamic_extents{_M_int_cast(
+             as_const(__exts[_DynamicIndexes]))...}
+         { }
+
+       template<typename _OIndexType, size_t _Nm>
+         requires (_Nm != _S_rank_dynamic)
+         constexpr _ExtentsStorage(const span<_OIndexType, _Nm>& __exts)
+         : _M_dynamic_extents{_M_int_cast(
+             as_const(__exts[_S_dynamic_index_inv[_DynamicIndexes]]))...}
+         { }
+
+       template<typename _OIndexType, size_t _Nm>
+         requires (_Nm == _S_rank_dynamic)
+         constexpr
+         _ExtentsStorage(const array<_OIndexType, _Nm>& __exts)
+         : _M_dynamic_extents{_M_int_cast(
+             as_const(__exts[_DynamicIndexes]))...}
+         { }
+
+       template<typename _OIndexType, size_t _Nm>
+         requires (_Nm != _S_rank_dynamic)
+         constexpr _ExtentsStorage(const array<_OIndexType, _Nm>& __exts)
+         : _M_dynamic_extents{_M_int_cast(
+             as_const(__exts[_S_dynamic_index_inv[_DynamicIndexes]]))...}
+         { }
+
+       static constexpr size_t
+       _M_static_extent(size_t __r) noexcept
+       { return _StaticExtent<0, _Extents...>::_M_value(__r); }
+
+       constexpr _IndexType
+       _M_extent(size_t __r) const noexcept
+       {
+         auto __se = _M_static_extent(__r);
+         if (__se == dynamic_extent)
+           return _M_dynamic_extents[_S_dynamic_index[__r]];
+         else
+           return __se;
+       }
+      private:
+       [[no_unique_address]]
+       array<_IndexType, _S_rank_dynamic> _M_dynamic_extents;
+      };
+
+    template<typename _IndexType, size_t... _Extents>
+      using __extents_storage = _ExtentsStorage<
+       _IndexType, make_index_sequence<_DynamicRank<_Extents...>::_S_value>,
+       make_index_sequence<sizeof...(_Extents)>, _Extents...>;
+  }
+
+  template<typename _IndexType, size_t... _Extents>
+    class extents;
+
+  template<typename _SIndexType, size_t... _SExtents, typename _OIndexType,
+          size_t... _OExtents>
+    constexpr bool
+    operator==(const extents<_SIndexType, _SExtents...>& __self,
+              const extents<_OIndexType, _OExtents...>& __other) noexcept;
+
+  template<typename _IndexType, size_t... _Extents>
+    class extents
+    {
+      static_assert(is_integral_v<_IndexType>, "_IndexType must be integral.");
+
+    public:
+      using index_type = _IndexType;
+      using size_type = make_signed_t<index_type>;
This should be `make_unsigned_t` and will be fixed with the next
version of this patch series.> +      using rank_type = size_t;
+
+      static constexpr rank_type
+      rank() noexcept { return _ExtentsStorage::_S_rank; }
+
+      static constexpr rank_type
+      rank_dynamic() noexcept { return _ExtentsStorage::_S_rank_dynamic; }
+
+      static constexpr size_t
+      static_extent(rank_type __r) noexcept
+      {
+       if constexpr (rank() == 0)
+         __builtin_trap();
+
+       __glibcxx_assert(__r < rank());
+       return _ExtentsStorage::_M_static_extent(__r);
+      }
+
+      constexpr index_type
+      extent(rank_type __r) const noexcept
+      {
+       if constexpr (rank() == 0)
+         __builtin_trap();
+
+       __glibcxx_assert(__r < rank());
+       return _M_extents._M_extent(__r);
+      }
+
+      constexpr
+      extents() noexcept = default;
+
+    private:
+      static constexpr bool
+      _M_is_less_dynamic(size_t __ext, size_t __oext)
+      { return (__ext != dynamic_extent) && (__oext == dynamic_extent); }
+
+      template<typename _Tp>
+       static constexpr size_t
+       _M_limits_max() { return numeric_limits<_Tp>::max(); }
+
+      template<typename _OIndexType, size_t... _OExtents>
+       static constexpr bool
+       _M_ctor_explicit()
+       {
+         return (_M_is_less_dynamic(_Extents, _OExtents) || ...)
+           || (_M_limits_max<index_type>() < _M_limits_max<_OIndexType>());
+       }
+
+    public:
+      template<typename _OIndexType, size_t... _OExtents>
+       requires (sizeof...(_OExtents) == rank())
+                && ((_OExtents == dynamic_extent || _Extents == dynamic_extent
+                     || _OExtents == _Extents) && ...)
+       constexpr explicit(_M_ctor_explicit<_OIndexType, _OExtents...>())
+       extents(const extents<_OIndexType, _OExtents...>& __other) noexcept
+       : _M_extents(__other._M_extents)
+       { }
+
+      template<typename... _OIndexTypes>
+       requires (
+         ((sizeof...(_OIndexTypes) == rank()
+           || sizeof...(_OIndexTypes) == rank_dynamic()))
+         && (is_convertible_v<_OIndexTypes, index_type> && ...)
+         && (is_nothrow_constructible_v<index_type, _OIndexTypes> && ...))
+       constexpr explicit extents(_OIndexTypes... __exts) noexcept
+       : _M_extents(__exts...)
+       { }
+
+      template<typename _OIndexType, size_t _Nm>
+       requires (is_convertible_v<const _OIndexType& , index_type>
+                && is_nothrow_constructible_v<index_type, const _OIndexType&>
+                && (_Nm == rank_dynamic() || _Nm == rank()))
+       constexpr explicit(_Nm != rank_dynamic())
+       extents(span<_OIndexType, _Nm> __exts) noexcept
+       : _M_extents(__exts)
+       { }
+
+      template<typename _OIndexType, size_t _Nm>
+       requires (is_convertible_v<const _OIndexType& , index_type>
+                && is_nothrow_constructible_v<index_type, const _OIndexType&>
+                && (_Nm == rank_dynamic() || _Nm == rank()))
+       constexpr explicit(_Nm != rank_dynamic())
+       extents(const array<_OIndexType, _Nm>& __exts) noexcept
+       : _M_extents(__exts)
+       { }
+
+      template<typename _SIndexType, size_t... _SExtents, typename _OIndexType,
+              size_t... _OExtents>
+       friend constexpr bool
+       operator==(const extents&,
+                  const extents<_OIndexType, _OExtents...>&) noexcept;
+
+    private:
+      using _ExtentsStorage =
+       typename __detail::__extents_storage<_IndexType, _Extents...>;
+
+      [[no_unique_address]] _ExtentsStorage _M_extents;
+
+      template<typename _OIndexType, size_t... _OExtents>
+      friend class extents;
+    };
+
+  template<typename _SIndexType, size_t... _SExtents, typename _OIndexType,
+          size_t... _OExtents>
+    constexpr bool
+    operator==(const extents<_SIndexType, _SExtents...>& __self,
+              const extents<_OIndexType, _OExtents...>& __other) noexcept
+    {
+      if constexpr (__self.rank() != __other.rank())
+        return false;
+
+      for (size_t __i = 0; __i < __self.rank(); ++__i)
+       if (__self.extent(__i) != __other.extent(__i))
+         return false;
+      return true;
+    }
+
+  namespace __detail
+  {
+    template<typename _IndexType, size_t _Count, size_t _Rank,
+            size_t... _Extents>
+      struct _Dextents
+      {
+       using type = typename _Dextents<_IndexType, _Count+1, _Rank,
+                                       _Extents..., dynamic_extent>::type;
+      };
+
+    template<typename _IndexType, size_t _Rank, size_t... _Extents>
+      struct _Dextents<_IndexType, _Rank, _Rank, _Extents...>
+      {
+       using type = extents<_IndexType, _Extents...>;
+      };
+
+    template<typename _Tp>
+      struct _DynamicExtent
+      {
+       static constexpr size_t _S_dyn = dynamic_extent;
+      };
+  }
+
+  template<typename _IndexType, size_t _Rank>
+    using dextents = typename __detail::_Dextents<_IndexType, 0, _Rank>::type;
+
+  template<typename... _Integrals>
+    requires (is_convertible_v<_Integrals, size_t> && ...)
+    explicit extents(_Integrals...) ->
+      extents<size_t, __detail::_DynamicExtent<_Integrals>::_S_dyn...>;
+
  _GLIBCXX_END_NAMESPACE_VERSION
  }
  #endif
diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/std.cc.in
index 12253b95c5a..481e39b2821 100644
--- a/libstdc++-v3/src/c++23/std.cc.in
+++ b/libstdc++-v3/src/c++23/std.cc.in
@@ -1825,7 +1825,11 @@ export namespace std
    }
  }
-// FIXME <mdspan>
+// <mdspan>
+{
+  using std::extents;
+  // FIXME layout_*, default_accessor and mdspan
+}
// 20.2 <memory>
  export namespace std

Reply via email to