More comments below.
libstdc++-v3/include/std/mdspan | 309 +++++++++++++++++++++++++++++++-
1 file changed, 308 insertions(+), 1 deletion(-)
diff --git a/libstdc++-v3/include/std/mdspan
b/libstdc++-v3/include/std/mdspan
index 47cfa405e44..d90fed57a19 100644
--- a/libstdc++-v3/include/std/mdspan
+++ b/libstdc++-v3/include/std/mdspan
@@ -144,6 +144,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ return __exts[__i]; });
}
+ static constexpr span<const size_t>
+ _S_static_subextents(size_t __begin, size_t __end) noexcept
+ {
+ return {_Extents.data() + __begin, _Extents.data() + __end};
+ }
+
+ constexpr span<const _IndexType>
+ _M_dynamic_subextents(size_t __begin, size_t __end) const noexcept
+ requires (_Extents.size() > 0)
+ {
+ return {_M_dynamic_extents + _S_dynamic_index[__begin],
+ _M_dynamic_extents + _S_dynamic_index[__end]};
+ }
+
private:
using _S_storage = __array_traits<_IndexType,
_S_rank_dynamic>::_Type;
[[no_unique_address]] _S_storage _M_dynamic_extents;
@@ -160,6 +174,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|| _Extent <= numeric_limits<_IndexType>::max();
}
+ namespace __mdspan
+ {
+ template<typename _Extents>
+ constexpr span<const size_t>
+ __static_subextents(size_t __begin, size_t __end)
Consider adding default arguments: __begin = 0u, __end = _Extents::rank().
This would simplify calls.
+ { return _Extents::_S_storage::_S_static_subextents(__begin,
__end); }
+
+ template<typename _Extents>
+ constexpr span<const typename _Extents::index_type>
+ __dynamic_subextents(const _Extents& __exts, size_t __begin, size_t
__end)
+ {
+ return __exts._M_dynamic_extents._M_dynamic_subextents(__begin,
__end);
+ }
+ }
+
template<typename _IndexType, size_t... _Extents>
class extents
{
@@ -251,7 +280,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: _M_dynamic_extents(span<const _OIndexType, _Nm>(__exts))
{ }
-
template<__mdspan::__valid_index_type<index_type> _OIndexType,
size_t _Nm>
requires (_Nm == rank() || _Nm == rank_dynamic())
constexpr explicit(_Nm != rank_dynamic())
@@ -276,6 +304,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
private:
+ friend span<const size_t>
+ __mdspan::__static_subextents<extents>(size_t, size_t);
+
+ friend span<const index_type>
+ __mdspan::__dynamic_subextents<extents>(const extents&, size_t,
size_t);
+
using _S_storage = __mdspan::_ExtentsStorage<
_IndexType, array<size_t, sizeof...(_Extents)>{_Extents...}>;
[[no_unique_address]] _S_storage _M_dynamic_extents;
@@ -286,6 +320,52 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
namespace __mdspan
{
+ template<typename _Extents>
+ constexpr size_t
+ __static_extents_prod(size_t __begin, size_t __end)
+ {
+ auto __sta_exts = __static_subextents<_Extents>(__begin, __end);
+ size_t __ret = 1;
+ for(size_t __i = 0; __i < __sta_exts.size(); ++__i)
Again we could use each here, once we have span.
+ if (__sta_exts[__i] != dynamic_extent)
+ __ret *= __sta_exts[__i];
+ return __ret;
+ }
+
+ template<typename _Extents>
+ constexpr size_t
+ __dynamic_extents_prod(const _Extents& __exts, size_t __begin,
+ size_t __end)
+ {
+ auto __dyn_exts = __dynamic_subextents<_Extents>(__exts, __begin,
+ __end);
+ size_t __ret = 1;
+ for(size_t __i = 0; __i < __dyn_exts.size(); ++__i)
Again we could use each here, once we have span. And we could inline the
function in __exts_prod.
+ __ret *= __dyn_exts[__i];
+ return __ret;
+ }
+
+ template<typename _Extents>
+ constexpr typename _Extents::index_type
+ __exts_prod(const _Extents& __exts, size_t __begin, size_t __end)
noexcept
+ {
+ using _IndexType = typename _Extents::index_type;
+ auto __ret = __static_extents_prod<_Extents>(__begin, __end);
+ if constexpr (_Extents::rank_dynamic() > 0)
+ __ret *= __dynamic_extents_prod(__exts, __begin, __end);
+ return __ret;
+ }
+
+ template<typename _Extents>
+ constexpr typename _Extents::index_type
+ __fwd_prod(const _Extents& __exts, size_t __r) noexcept
+ { return __exts_prod(__exts, 0, __r); }
+
+ template<typename _Extents>
+ constexpr typename _Extents::index_type
+ __rev_prod(const _Extents& __exts, size_t __r) noexcept
+ { return __exts_prod(__exts, __r + 1, __exts.rank()); }
+
template<typename _IndexType, size_t... _Counts>
auto __build_dextents_type(integer_sequence<size_t, _Counts...>)
-> extents<_IndexType, ((void) _Counts, dynamic_extent)...>;
@@ -304,6 +384,233 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
explicit extents(_Integrals...) ->
extents<size_t, __mdspan::__dynamic_extent<_Integrals>()...>;
+ struct layout_left
+ {
+ template<typename _Extents>
+ class mapping;
+ };
+
+ namespace __mdspan
+ {
+ template<typename _Tp>
+ constexpr bool __is_extents = false;
+
+ template<typename _IndexType, size_t... _Extents>
+ constexpr bool __is_extents<extents<_IndexType, _Extents...>> =
true;
+
+ template<typename _Extents, typename... _Indices>
+ constexpr typename _Extents::index_type
+ __linear_index_left(const _Extents& __exts, _Indices... __indices)
+ {
+ using _IndexType = typename _Extents::index_type;
+ _IndexType __res = 0;
+ if constexpr (sizeof...(__indices) > 0)
+ {
+ _IndexType __mult = 1;
+ auto __update = [&, __pos = 0u](_IndexType __idx) mutable
+ {
+ __res += __idx * __mult;
+ __mult *= __exts.extent(__pos);
+ ++__pos;
+ };
+ (__update(__indices), ...);
+ }
+ return __res;
+ }
+
+ template<typename _Tp>
+ constexpr bool
+ __contains_zero(span<_Tp> __exts)
+ {
+ for (size_t __i = 0; __i < __exts.size(); ++__i)
+ if (__exts[__i] == 0)
+ return true;
+ return false;
+ }
+
+ template<typename _Extents>
+ consteval bool
+ __has_static_zero()
With default arguments this will become
__contains_zero(_static_exts<_Extent>()).
I would inline it then.
+ {
+ return __contains_zero(
+ __static_subextents<_Extents>(0, _Extents::rank()));
+ }
+
+ template<typename _Extents, typename _IndexType>
+ consteval _IndexType
+ __static_quotient(_IndexType __nom)
Consider adding the default argument for numeric_limits max here.
+ {
+ for(size_t __i = 0; __i < _Extents::rank(); ++__i)
Use foreach on static_exts here.
+ {
I would check if (__norm) is zero with early exit.
+ auto __factor = _Extents::static_extent(__i);
+ if (__factor != dynamic_extent)
+ __nom /= _IndexType(__factor);
+ }
+ return __nom;
+ }
+
+ template<typename _Extents>
+ constexpr bool
+ __is_representable_extents(const _Extents& __exts)
+ {
+ using _IndexType = _Extents::index_type;
+ constexpr auto __rank = _Extents::rank();
+
+ constexpr auto __sta_quo = __static_quotient<_Extents, _IndexType>(
+ numeric_limits<_IndexType>::max());
You are calling static_quotient here before checking if we have zero in
static asserts.
This will lead to division by zero. Consider using if with initializer, or
at least nested scope.
if constepxrt (static-zero)
else if constexpr(constexpr auto __static_quotient<_Extents,
_IndexType>());
_Extents::rank_dynamic() == 0)
return __static_quotient != 0;
else if (auto __dyn_exts = __dynamic_subextents(__exts);
__contains_zero(__dyn_exts))
return true;
else if constexpr(__sta_quo == 0)
return false;
else
{
}
+
+ if constexpr (__has_static_zero<_Extents>())
+ return true;
+ else if constexpr (_Extents::rank_dynamic() == 0)
We have already checked this case in mandates. But I think it's worth
having it here again.
+ return __sta_quo != 0;
+ else
+ {
+ auto __dyn_exts = __dynamic_subextents(__exts, 0, __rank);
+ if(__contains_zero(__dyn_exts))
+ return true;
+
+ if constexpr (__sta_quo == 0)
+ return false;
+ else
+ {
+ constexpr auto __dyn_rank = _Extents::rank_dynamic();
+ auto __dyn_quo = __sta_quo;
+ for(size_t __i = 0; __i < __dyn_rank && __dyn_quo > 0; ++__i)
Would prefer if you will use for_each here, with nested if __dyn_quo == 0.
+ __dyn_quo /= __dyn_exts[__i];
+ return __dyn_quo != 0;
+ }
+ }
+ }
+
+ template<typename _Extents, typename _IndexType>
+ concept __representable_size = _Extents::rank_dynamic() != 0
+ || __has_static_zero<_Extents>()
+ || (__static_quotient<_Extents, _IndexType>(
+ numeric_limits<_IndexType>::max()) != 0);
+
+ template<typename _Layout, typename _Mapping>
+ concept __mapping_of =
+ is_same_v<typename _Layout::mapping<typename
_Mapping::extents_type>,
+ _Mapping>;
+
+ template<typename _Mapping>
+ concept __standardized_mapping = __mapping_of<layout_left,
_Mapping>;
+
+ template<typename M>
+ concept __mapping_like = requires
+ {
+ requires __is_extents<typename M::extents_type>;
+ { M::is_always_strided() } -> same_as<bool>;
+ { M::is_always_exhaustive() } -> same_as<bool>;
+ { M::is_always_unique() } -> same_as<bool>;
+ bool_constant<M::is_always_strided()>::value;
+ bool_constant<M::is_always_exhaustive()>::value;
+ bool_constant<M::is_always_unique()>::value;
+ };
+
+ // A tag type to create internal ctors.
+ class __internal_ctor
+ { };
+ }
+
+ template<typename _Extents>
+ class layout_left::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_left;
+
+ static_assert(__mdspan::__representable_size<_Extents, index_type>,
+ "The size of extents_type must be representable as index_type");
+
+ constexpr
+ mapping() noexcept = default;
+
+ constexpr
+ mapping(const mapping&) noexcept = default;
+
+ constexpr
+ mapping(const _Extents& __extents) noexcept
+ : _M_extents(__extents)
+ {
__glibcxx_assert(__mdspan::__is_representable_extents(_M_extents)); }
+
+ template<typename _OExtents>
+ requires (is_constructible_v<extents_type, _OExtents>)
+ constexpr explicit(!is_convertible_v<_OExtents, extents_type>)
+ mapping(const mapping<_OExtents>& __other) noexcept
+ : mapping(__other.extents(), __mdspan::__internal_ctor{})
+ { }
+
+ constexpr mapping&
+ operator=(const mapping&) noexcept = default;
+
+ constexpr const _Extents&
+ extents() const noexcept { return _M_extents; }
+
+ constexpr index_type
+ required_span_size() const noexcept
+ { return __mdspan::__fwd_prod(_M_extents, extents_type::rank()); }
+
+ 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_left(
+ this->extents(), static_cast<index_type>(__indices)...);
+ }
+
+ static constexpr bool
+ is_always_unique() noexcept { return true; }
+
+ static constexpr bool
+ is_always_exhaustive() noexcept { return true; }
+
+ static constexpr bool
+ is_always_strided() noexcept { return true; }
+
+ static constexpr bool
+ is_unique() noexcept { return true; }
+
+ static constexpr bool
+ is_exhaustive() noexcept { return true; }
+
+ static constexpr bool
+ is_strided() noexcept { return true; }
+
+ constexpr index_type
+ stride(rank_type __i) const noexcept
+ requires (extents_type::rank() > 0)
+ {
+ __glibcxx_assert(__i < extents_type::rank());
+ return __mdspan::__fwd_prod(_M_extents, __i);
+ }
+
+ template<typename _OExtents>
+ requires (extents_type::rank() == _OExtents::rank())
+ friend constexpr bool
+ operator==(const mapping& __self, const mapping<_OExtents>&
__other)
+ noexcept
+ { return __self.extents() == __other.extents(); }
+
+ private:
+ template<typename _OExtents>
+ constexpr explicit
+ mapping(const _OExtents& __oexts, __mdspan::__internal_ctor)
noexcept
+ : _M_extents(extents_type(__oexts))
+ {
+ static_assert(__mdspan::__representable_size<_OExtents,
index_type>,
+ "The size of OtherExtents must be representable as
index_type");
+
__glibcxx_assert(__mdspan::__is_representable_extents(_M_extents));
+ }
+
+ public:
+ [[no_unique_address]] _Extents _M_extents;
+ };
+
_GLIBCXX_END_NAMESPACE_VERSION
}
#endif
--
2.49.0