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>; + 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 -- 2.49.0