[PATCH v1 3/4] libstdc++: Implement std::extents [PR107761].
Luc Grosheintz
luc.grosheintz@gmail.com
Thu Apr 10 15:54:22 GMT 2025
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.grosheintz@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
More information about the Libstdc++
mailing list