2023-09-14 21:59:38 +03:00
|
|
|
// mdspan standard header
|
|
|
|
|
|
|
|
// Copyright (c) Microsoft Corporation.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
|
|
|
|
#ifndef _MDSPAN_
|
|
|
|
#define _MDSPAN_
|
|
|
|
#include <yvals.h>
|
|
|
|
#if _STL_COMPILER_PREPROCESSOR
|
|
|
|
#if !_HAS_CXX23 || !defined(__cpp_lib_concepts) // TRANSITION, GH-395
|
|
|
|
_EMIT_STL_WARNING(STL4038, "The contents of <mdspan> are available only with C++23 or later.");
|
|
|
|
#else // ^^^ not supported / supported language mode vvv
|
|
|
|
#include <array>
|
|
|
|
#include <limits>
|
|
|
|
#include <span>
|
|
|
|
#include <tuple>
|
|
|
|
#include <type_traits>
|
|
|
|
|
|
|
|
#pragma pack(push, _CRT_PACKING)
|
|
|
|
#pragma warning(push, _STL_WARNING_LEVEL)
|
|
|
|
#pragma warning(disable : _STL_DISABLED_WARNINGS)
|
|
|
|
_STL_DISABLE_CLANG_WARNINGS
|
|
|
|
#pragma push_macro("new")
|
|
|
|
#undef new
|
|
|
|
|
|
|
|
// TRANSITION, non-_Ugly attribute tokens
|
|
|
|
#pragma push_macro("empty_bases")
|
|
|
|
#undef empty_bases
|
|
|
|
|
|
|
|
_STD_BEGIN
|
|
|
|
template <class _IndexType, size_t _Size>
|
|
|
|
struct _Maybe_empty_array {
|
|
|
|
array<_IndexType, _Size> _Array{};
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class _IndexType>
|
|
|
|
struct _Maybe_empty_array<_IndexType, 0> {};
|
|
|
|
|
|
|
|
template <size_t... _Extents>
|
|
|
|
inline constexpr size_t _Calculate_rank_dynamic = (static_cast<size_t>(_Extents == dynamic_extent) + ... + 0);
|
|
|
|
|
|
|
|
struct _Extents_from_tuple {
|
|
|
|
explicit _Extents_from_tuple() = default;
|
|
|
|
};
|
|
|
|
|
|
|
|
_EXPORT_STD template <class _IndexType, size_t... _Extents>
|
|
|
|
class extents : private _Maybe_empty_array<_IndexType, _Calculate_rank_dynamic<_Extents...>> {
|
|
|
|
public:
|
|
|
|
using index_type = _IndexType;
|
|
|
|
using size_type = make_unsigned_t<index_type>;
|
|
|
|
using rank_type = size_t;
|
|
|
|
|
|
|
|
static_assert(_Is_standard_integer<index_type>,
|
|
|
|
"IndexType must be a signed or unsigned integer type (N4950 [mdspan.extents.overview]/1.1).");
|
|
|
|
static_assert(((_Extents == dynamic_extent || _STD in_range<index_type>(_Extents)) && ...),
|
|
|
|
"Each element of Extents must either be equal to dynamic_extent, or be representable as a value of type "
|
|
|
|
"IndexType (N4950 [mdspan.extents.overview]/1.2).");
|
|
|
|
|
|
|
|
static constexpr rank_type _Rank = sizeof...(_Extents);
|
|
|
|
static constexpr rank_type _Rank_dynamic = _Calculate_rank_dynamic<_Extents...>;
|
|
|
|
static constexpr array<rank_type, _Rank> _Static_extents = {_Extents...};
|
|
|
|
static constexpr bool _Multidim_index_space_size_is_always_zero = ((_Extents == 0) || ...);
|
|
|
|
|
|
|
|
private:
|
|
|
|
_NODISCARD static consteval auto _Make_dynamic_indices() noexcept {
|
|
|
|
array<rank_type, _Rank + 1> _Result{};
|
|
|
|
rank_type _Counter = 0;
|
|
|
|
for (rank_type _Idx = 0; _Idx < _Rank; ++_Idx) {
|
|
|
|
_Result[_Idx] = _Counter;
|
|
|
|
if (_Static_extents[_Idx] == dynamic_extent) {
|
|
|
|
++_Counter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_Result[_Rank] = _Counter;
|
|
|
|
return _Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static constexpr array<rank_type, _Rank + 1> _Dynamic_indices = _Make_dynamic_indices();
|
|
|
|
|
|
|
|
_NODISCARD static consteval auto _Make_dynamic_indices_inv() noexcept {
|
|
|
|
array<rank_type, _Rank_dynamic> _Result{};
|
|
|
|
rank_type _Counter = 0;
|
|
|
|
for (rank_type _Idx = 0; _Idx < _Rank; ++_Idx) {
|
|
|
|
if (_Static_extents[_Idx] == dynamic_extent) {
|
|
|
|
_Analysis_assume_(_Counter < _Rank_dynamic); // TRANSITION, DevCom-923103
|
|
|
|
_Result[_Counter] = _Idx;
|
|
|
|
++_Counter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return _Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static constexpr array<rank_type, _Rank_dynamic> _Dynamic_indices_inv = _Make_dynamic_indices_inv();
|
|
|
|
|
|
|
|
using _Base = _Maybe_empty_array<_IndexType, _Rank_dynamic>;
|
|
|
|
|
|
|
|
template <class _OtherIndexType, size_t... _OtherExtents, size_t... _Indices>
|
|
|
|
constexpr explicit extents(
|
|
|
|
const extents<_OtherIndexType, _OtherExtents...>& _Other, index_sequence<_Indices...>) noexcept
|
|
|
|
: _Base{static_cast<index_type>(_Other.extent(_Dynamic_indices_inv[_Indices]))...} {
|
|
|
|
_STL_INTERNAL_STATIC_ASSERT(sizeof...(_OtherExtents) == _Rank);
|
|
|
|
_STL_INTERNAL_STATIC_ASSERT(
|
|
|
|
((_OtherExtents == dynamic_extent || _Extents == dynamic_extent || _OtherExtents == _Extents) && ...));
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
if constexpr (rank() > 0) {
|
|
|
|
for (rank_type _Idx = 0; _Idx < _Rank; ++_Idx) {
|
|
|
|
if constexpr (rank() != rank_dynamic()) {
|
|
|
|
_STL_VERIFY(_Static_extents[_Idx] == dynamic_extent
|
|
|
|
|| _STD cmp_equal(_Static_extents[_Idx], _Other.extent(_Idx)),
|
|
|
|
"Value of other.extent(r) must be equal to extent(r) for each r for which extent(r) is a "
|
|
|
|
"static extent (N4950 [mdspan.extents.cons]/2.1)");
|
|
|
|
}
|
|
|
|
_STL_VERIFY(_STD in_range<index_type>(_Other.extent(_Idx)),
|
|
|
|
"Value of other.extent(r) must be representable as a value of type index_type for every rank index "
|
|
|
|
"r (N4950 [mdspan.extents.cons]/2.2)");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _ExtsTuple, size_t... _Indices>
|
|
|
|
requires (tuple_size_v<_ExtsTuple> == _Rank_dynamic)
|
|
|
|
constexpr explicit extents(_Extents_from_tuple, _ExtsTuple _Tpl, index_sequence<_Indices...>) noexcept
|
|
|
|
: _Base{static_cast<index_type>(_STD move(_STD get<_Indices>(_Tpl)))...} {}
|
|
|
|
|
|
|
|
template <class _ExtsTuple, size_t... _DynIndices>
|
|
|
|
requires (tuple_size_v<_ExtsTuple> != _Rank_dynamic)
|
|
|
|
constexpr explicit extents(_Extents_from_tuple, _ExtsTuple _Tpl, index_sequence<_DynIndices...>) noexcept
|
|
|
|
: _Base{static_cast<index_type>(_STD move(_STD get<_Dynamic_indices_inv[_DynIndices]>(_Tpl)))...} {
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
[&]<size_t... _MixedIndices>(index_sequence<_MixedIndices...>) {
|
|
|
|
_STL_VERIFY(((_Static_extents[_MixedIndices] == dynamic_extent
|
|
|
|
|| _STD cmp_equal(_Static_extents[_MixedIndices],
|
|
|
|
static_cast<index_type>(_STD move(_STD get<_MixedIndices>(_Tpl)))))
|
|
|
|
&& ...),
|
|
|
|
"Value of exts_arr[r] must be equal to extent(r) for each r for which extent(r) is a static extent "
|
|
|
|
"(N4950 [mdspan.extents.cons]/7.1)");
|
|
|
|
}(make_index_sequence<rank()>{});
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _OtherIndexType, size_t... _Indices>
|
|
|
|
constexpr explicit extents(span<_OtherIndexType, _Rank_dynamic> _Dynamic_exts, index_sequence<_Indices...>) noexcept
|
|
|
|
: _Base{static_cast<index_type>(_STD as_const(_Dynamic_exts[_Indices]))...} {
|
|
|
|
_STL_INTERNAL_STATIC_ASSERT(is_convertible_v<const _OtherIndexType&, index_type>
|
|
|
|
&& is_nothrow_constructible_v<index_type, const _OtherIndexType&>);
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
if constexpr (_Is_standard_integer<_OtherIndexType> && _Rank_dynamic != 0) {
|
|
|
|
_STL_VERIFY(((_Dynamic_exts[_Indices] >= 0 && _STD in_range<index_type>(_Dynamic_exts[_Indices])) && ...),
|
|
|
|
"exts[r] must be representable as a nonnegative value of type index_type for every rank index r "
|
|
|
|
"(N4950 [mdspan.extents.cons]/10.2)");
|
|
|
|
}
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _OtherIndexType, size_t _Size, size_t... _Indices>
|
|
|
|
constexpr explicit extents(span<_OtherIndexType, _Size> _Mixed_exts, index_sequence<_Indices...>) noexcept
|
|
|
|
: _Base{static_cast<index_type>(_STD as_const(_Mixed_exts[_Dynamic_indices_inv[_Indices]]))...} {
|
|
|
|
_STL_INTERNAL_STATIC_ASSERT(_Size != _Rank_dynamic);
|
|
|
|
_STL_INTERNAL_STATIC_ASSERT(is_convertible_v<const _OtherIndexType&, index_type>
|
|
|
|
&& is_nothrow_constructible_v<index_type, const _OtherIndexType&>);
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
if constexpr (_Is_standard_integer<_OtherIndexType>) {
|
|
|
|
for (rank_type _Idx = 0; _Idx < _Rank; ++_Idx) {
|
|
|
|
_STL_VERIFY(
|
|
|
|
_Static_extents[_Idx] == dynamic_extent || _STD cmp_equal(_Static_extents[_Idx], _Mixed_exts[_Idx]),
|
|
|
|
"Value of exts[r] must be equal to extent(r) for each r for which extent(r) is a static extent "
|
|
|
|
"(N4950 [mdspan.extents.cons]/10.1)");
|
|
|
|
_STL_VERIFY(_Mixed_exts[_Idx] >= 0 && _STD in_range<index_type>(_Mixed_exts[_Idx]),
|
|
|
|
"exts[r] must be representable as a nonnegative value of type index_type for every rank index r "
|
|
|
|
"(N4950 [mdspan.extents.cons]/10.2)");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
_NODISCARD static constexpr rank_type rank() noexcept {
|
|
|
|
return _Rank;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD static constexpr rank_type rank_dynamic() noexcept {
|
|
|
|
return _Rank_dynamic;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD static constexpr size_t static_extent(const rank_type _Idx) noexcept {
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
_STL_VERIFY(_Idx < _Rank, "Index must be less than rank() (N4950 [mdspan.extents.obs]/1)");
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
return _Static_extents[_Idx];
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD constexpr index_type extent(const rank_type _Idx) const noexcept {
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
_STL_VERIFY(_Idx < _Rank, "Index must be less than rank() (N4950 [mdspan.extents.obs]/3)");
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
if constexpr (rank_dynamic() == 0) {
|
|
|
|
return static_cast<index_type>(_Static_extents[_Idx]);
|
|
|
|
} else if constexpr (rank_dynamic() == rank()) {
|
|
|
|
return this->_Array[_Idx];
|
|
|
|
} else {
|
|
|
|
if (_Static_extents[_Idx] == dynamic_extent) {
|
|
|
|
return this->_Array[_Dynamic_indices[_Idx]];
|
|
|
|
} else {
|
|
|
|
return static_cast<index_type>(_Static_extents[_Idx]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr extents() noexcept = default;
|
|
|
|
|
|
|
|
template <class _OtherIndexType, size_t... _OtherExtents>
|
|
|
|
requires (sizeof...(_OtherExtents) == rank())
|
|
|
|
&& ((_OtherExtents == dynamic_extent || _Extents == dynamic_extent || _OtherExtents == _Extents) && ...)
|
|
|
|
constexpr explicit(((_Extents != dynamic_extent && _OtherExtents == dynamic_extent) || ...)
|
|
|
|
|| (numeric_limits<index_type>::max)() < (numeric_limits<_OtherIndexType>::max)())
|
|
|
|
extents(const extents<_OtherIndexType, _OtherExtents...>& _Other) noexcept
|
|
|
|
: extents(_Other, make_index_sequence<rank_dynamic()>{}) {}
|
|
|
|
|
|
|
|
template <class... _OtherIndexTypes>
|
|
|
|
requires (is_convertible_v<_OtherIndexTypes, index_type> && ...)
|
|
|
|
&& (is_nothrow_constructible_v<index_type, _OtherIndexTypes> && ...)
|
|
|
|
&& (sizeof...(_OtherIndexTypes) == rank_dynamic() || sizeof...(_OtherIndexTypes) == rank())
|
|
|
|
constexpr explicit extents(_OtherIndexTypes... _Exts) noexcept
|
|
|
|
: extents(_Extents_from_tuple{}, _STD tie(_Exts...), make_index_sequence<rank_dynamic()>{}) {
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
auto _Check_extent = []<class _Ty>(const _Ty& _Ext) {
|
|
|
|
if constexpr (_Is_standard_integer<_Ty>) {
|
|
|
|
return _Ext >= 0 && _STD in_range<index_type>(_Ext);
|
|
|
|
} else if constexpr (integral<_Ty> && !same_as<_Ty, bool>) { // NB: character types
|
|
|
|
const auto _Integer_ext = static_cast<long long>(_Ext);
|
|
|
|
return _Integer_ext >= 0 && _STD in_range<index_type>(_Integer_ext);
|
|
|
|
} else {
|
|
|
|
return true; // NB: We cannot check preconditions
|
|
|
|
}
|
|
|
|
};
|
|
|
|
_STL_VERIFY((_Check_extent(_Exts) && ...), "Each argument must be representable as a nonnegative value of type "
|
|
|
|
"index_type (N4950 [mdspan.extents.cons]/7.2)");
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _OtherIndexType, size_t _Size>
|
|
|
|
requires is_convertible_v<const _OtherIndexType&, index_type>
|
|
|
|
&& is_nothrow_constructible_v<index_type, const _OtherIndexType&>
|
|
|
|
&& (_Size == rank_dynamic() || _Size == rank())
|
|
|
|
constexpr explicit(_Size != rank_dynamic()) extents(span<_OtherIndexType, _Size> _Exts) noexcept
|
|
|
|
: extents(_Exts, make_index_sequence<rank_dynamic()>{}) {}
|
|
|
|
|
|
|
|
template <class _OtherIndexType, size_t _Size>
|
|
|
|
requires is_convertible_v<const _OtherIndexType&, index_type>
|
|
|
|
&& is_nothrow_constructible_v<index_type, const _OtherIndexType&>
|
|
|
|
&& (_Size == rank_dynamic() || _Size == rank())
|
|
|
|
constexpr explicit(_Size != rank_dynamic()) extents(const array<_OtherIndexType, _Size>& _Exts) noexcept
|
|
|
|
: extents(span{_Exts}, make_index_sequence<rank_dynamic()>{}) {}
|
|
|
|
|
|
|
|
template <class _OtherIndexType, size_t... _OtherExtents>
|
|
|
|
_NODISCARD_FRIEND constexpr bool operator==(
|
|
|
|
const extents& _Left, const extents<_OtherIndexType, _OtherExtents...>& _Right) noexcept {
|
|
|
|
if constexpr (rank() != sizeof...(_OtherExtents)) {
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
for (rank_type _Idx = 0; _Idx < _Rank; ++_Idx) {
|
|
|
|
if (_STD cmp_not_equal(_Left.extent(_Idx), _Right.extent(_Idx))) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD static consteval bool _Is_static_multidim_index_space_size_representable() noexcept {
|
|
|
|
// Pre: rank_dynamic() == 0
|
|
|
|
if constexpr (_Multidim_index_space_size_is_always_zero) {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
index_type _Result{1};
|
|
|
|
const bool _Overflow = (_Mul_overflow(static_cast<index_type>(_Extents), _Result, _Result) || ...);
|
|
|
|
return !_Overflow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <integral _Ty = index_type>
|
|
|
|
_NODISCARD constexpr bool _Is_dynamic_multidim_index_space_size_representable() const noexcept {
|
|
|
|
// Pre: rank_dynamic() != 0
|
|
|
|
if constexpr (_Multidim_index_space_size_is_always_zero) {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
bool _Overflow = false;
|
|
|
|
_Ty _Result = 1;
|
|
|
|
for (rank_type _Idx = 0; _Idx < _Rank; ++_Idx) {
|
|
|
|
const auto _Ext = static_cast<_Ty>(extent(_Idx));
|
|
|
|
if (_Ext == 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!_Overflow) {
|
|
|
|
_Overflow = _Mul_overflow(_Ext, _Result, _Result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return !_Overflow;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <size_t... _Seq, class... _IndexTypes>
|
|
|
|
_NODISCARD constexpr bool _Contains_multidimensional_index(
|
|
|
|
index_sequence<_Seq...>, _IndexTypes... _Indices) const noexcept {
|
|
|
|
_STL_INTERNAL_STATIC_ASSERT((same_as<_IndexTypes, index_type> && ...));
|
|
|
|
if constexpr (unsigned_integral<index_type>) {
|
|
|
|
return ((_Indices < extent(_Seq)) && ...);
|
|
|
|
} else {
|
|
|
|
return ((0 <= _Indices && _Indices < extent(_Seq)) && ...);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class>
|
|
|
|
inline constexpr size_t _Repeat_dynamic_extent = dynamic_extent;
|
|
|
|
|
|
|
|
template <class... _Integrals>
|
|
|
|
requires (is_convertible_v<_Integrals, size_t> && ...)
|
|
|
|
explicit extents(_Integrals...) -> extents<size_t, _Repeat_dynamic_extent<_Integrals>...>;
|
|
|
|
|
|
|
|
template <class _IndexType, class _Indices>
|
|
|
|
struct _Dextents_impl;
|
|
|
|
|
|
|
|
template <class _IndexType, size_t... _Indices>
|
|
|
|
struct _Dextents_impl<_IndexType, index_sequence<_Indices...>> {
|
|
|
|
using type = extents<_IndexType, ((void) _Indices, dynamic_extent)...>;
|
|
|
|
};
|
|
|
|
|
|
|
|
_EXPORT_STD template <class _IndexType, size_t _Rank>
|
|
|
|
using dextents = _Dextents_impl<_IndexType, make_index_sequence<_Rank>>::type;
|
|
|
|
|
|
|
|
template <class _Ty>
|
|
|
|
inline constexpr bool _Is_extents = false;
|
|
|
|
|
|
|
|
template <class _IndexType, size_t... _Args>
|
|
|
|
inline constexpr bool _Is_extents<extents<_IndexType, _Args...>> = true;
|
|
|
|
|
|
|
|
template <class _Extents>
|
|
|
|
class _Fwd_prod_of_extents {
|
|
|
|
public:
|
|
|
|
_STL_INTERNAL_STATIC_ASSERT(_Is_extents<_Extents>);
|
|
|
|
|
|
|
|
_NODISCARD static constexpr _Extents::index_type _Calculate(const _Extents& _Exts, const size_t _Idx) noexcept {
|
|
|
|
_STL_INTERNAL_CHECK(_Idx <= _Extents::_Rank);
|
|
|
|
if constexpr (_Extents::rank() == 0) {
|
|
|
|
return 1;
|
|
|
|
} else {
|
2023-09-21 22:37:19 +03:00
|
|
|
// Use an unsigned type to prevent overflow when called from mdspan::size.
|
|
|
|
typename _Extents::size_type _Result = 1;
|
2023-09-14 21:59:38 +03:00
|
|
|
for (size_t _Dim = 0; _Dim < _Idx; ++_Dim) {
|
2023-09-21 22:37:19 +03:00
|
|
|
_Result *= static_cast<_Extents::size_type>(_Exts.extent(_Dim));
|
2023-09-14 21:59:38 +03:00
|
|
|
}
|
2023-09-21 22:37:19 +03:00
|
|
|
|
|
|
|
return static_cast<_Extents::index_type>(_Result);
|
2023-09-14 21:59:38 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class _Extents>
|
|
|
|
requires (_Extents::rank() > 0) && (_Extents::rank_dynamic() == 0)
|
|
|
|
class _Fwd_prod_of_extents<_Extents> {
|
|
|
|
private:
|
|
|
|
_STL_INTERNAL_STATIC_ASSERT(_Is_extents<_Extents>);
|
|
|
|
|
|
|
|
_NODISCARD static consteval auto _Make_prods() noexcept {
|
|
|
|
array<typename _Extents::index_type, _Extents::rank() + 1> _Result;
|
|
|
|
_Result.front() = 1;
|
|
|
|
for (size_t _Idx = 1; _Idx < _Extents::_Rank + 1; ++_Idx) {
|
|
|
|
_Result[_Idx] = static_cast<_Extents::index_type>(_Result[_Idx - 1] * _Extents::_Static_extents[_Idx - 1]);
|
|
|
|
}
|
|
|
|
return _Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static constexpr array<typename _Extents::index_type, _Extents::rank() + 1> _Cache = _Make_prods();
|
|
|
|
|
|
|
|
public:
|
|
|
|
_NODISCARD static constexpr _Extents::index_type _Calculate(const _Extents&, const size_t _Idx) noexcept {
|
|
|
|
_STL_INTERNAL_CHECK(_Idx <= _Extents::_Rank);
|
|
|
|
return _Cache[_Idx];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class _Extents>
|
|
|
|
class _Rev_prod_of_extents {
|
|
|
|
public:
|
|
|
|
_STL_INTERNAL_STATIC_ASSERT(_Is_extents<_Extents>);
|
|
|
|
_STL_INTERNAL_STATIC_ASSERT(_Extents::rank() > 0);
|
|
|
|
|
|
|
|
_NODISCARD static constexpr _Extents::index_type _Calculate(const _Extents& _Exts, const size_t _Idx) noexcept {
|
|
|
|
_STL_INTERNAL_CHECK(_Idx < _Extents::_Rank);
|
|
|
|
typename _Extents::index_type _Result = 1;
|
|
|
|
for (size_t _Dim = _Idx + 1; _Dim < _Extents::_Rank; ++_Dim) {
|
|
|
|
_Result *= _Exts.extent(_Dim);
|
|
|
|
}
|
|
|
|
return _Result;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class _Extents>
|
|
|
|
requires (_Extents::rank_dynamic() == 0)
|
|
|
|
class _Rev_prod_of_extents<_Extents> {
|
|
|
|
private:
|
|
|
|
_STL_INTERNAL_STATIC_ASSERT(_Is_extents<_Extents>);
|
|
|
|
_STL_INTERNAL_STATIC_ASSERT(_Extents::rank() > 0);
|
|
|
|
|
|
|
|
_NODISCARD static consteval auto _Make_prods() noexcept {
|
|
|
|
array<typename _Extents::index_type, _Extents::rank()> _Result;
|
|
|
|
_Result.back() = 1;
|
|
|
|
for (size_t _Idx = _Extents::_Rank; _Idx-- > 1;) {
|
|
|
|
_Result[_Idx - 1] = static_cast<_Extents::index_type>(_Result[_Idx] * _Extents::_Static_extents[_Idx]);
|
|
|
|
}
|
|
|
|
return _Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static constexpr array<typename _Extents::index_type, _Extents::rank()> _Cache = _Make_prods();
|
|
|
|
|
|
|
|
public:
|
|
|
|
_NODISCARD static constexpr _Extents::index_type _Calculate(const _Extents&, const size_t _Idx) noexcept {
|
|
|
|
_STL_INTERNAL_CHECK(_Idx < _Extents::_Rank);
|
|
|
|
return _Cache[_Idx];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class _Layout, class _Mapping>
|
|
|
|
inline constexpr bool _Is_mapping_of =
|
|
|
|
is_same_v<typename _Layout::template mapping<typename _Mapping::extents_type>, _Mapping>;
|
|
|
|
|
|
|
|
_EXPORT_STD struct layout_left {
|
|
|
|
template <class _Extents>
|
|
|
|
class mapping;
|
|
|
|
};
|
|
|
|
|
|
|
|
_EXPORT_STD struct layout_right {
|
|
|
|
template <class _Extents>
|
|
|
|
class mapping;
|
|
|
|
};
|
|
|
|
|
|
|
|
_EXPORT_STD struct layout_stride {
|
|
|
|
template <class _Extents>
|
|
|
|
class mapping;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class _Extents>
|
|
|
|
struct _Maybe_fully_static_extents {
|
|
|
|
_STL_INTERNAL_STATIC_ASSERT(_Is_extents<_Extents>);
|
|
|
|
|
|
|
|
constexpr _Maybe_fully_static_extents() noexcept = default;
|
|
|
|
|
|
|
|
template <class _OtherExtents>
|
|
|
|
constexpr explicit _Maybe_fully_static_extents(const _OtherExtents& _Exts_) : _Exts(_Exts_) {}
|
|
|
|
|
|
|
|
_Extents _Exts{};
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class _Extents>
|
|
|
|
requires (_Extents::rank_dynamic() == 0)
|
|
|
|
struct _Maybe_fully_static_extents<_Extents> {
|
|
|
|
_STL_INTERNAL_STATIC_ASSERT(_Is_extents<_Extents>);
|
|
|
|
|
|
|
|
constexpr _Maybe_fully_static_extents() noexcept = default;
|
|
|
|
|
|
|
|
template <class _OtherExtents>
|
|
|
|
constexpr explicit _Maybe_fully_static_extents([[maybe_unused]] const _OtherExtents& _Exts_) {
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
(void) _Extents{_Exts_}; // NB: temporary created for preconditions check
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
static constexpr _Extents _Exts{};
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class _Extents>
|
|
|
|
class layout_left::mapping : private _Maybe_fully_static_extents<_Extents> {
|
|
|
|
public:
|
|
|
|
using extents_type = _Extents;
|
|
|
|
using index_type = extents_type::index_type;
|
|
|
|
using size_type = extents_type::size_type;
|
|
|
|
using rank_type = extents_type::rank_type;
|
|
|
|
using layout_type = layout_left;
|
|
|
|
|
|
|
|
private:
|
|
|
|
using _Base = _Maybe_fully_static_extents<extents_type>;
|
|
|
|
|
|
|
|
static_assert(_Is_extents<extents_type>,
|
|
|
|
"Extents must be a specialization of std::extents (N4950 [mdspan.layout.left.overview]/2).");
|
|
|
|
static_assert(
|
|
|
|
extents_type::rank_dynamic() != 0 || extents_type::_Is_static_multidim_index_space_size_representable(),
|
|
|
|
"If Extents::rank_dynamic() == 0 is true, then the size of the multidimensional index space Extents() must be "
|
|
|
|
"representable as a value of type typename Extents::index_type (N4950 [mdspan.layout.left.overview]/4).");
|
|
|
|
|
|
|
|
public:
|
|
|
|
constexpr mapping() noexcept = default;
|
|
|
|
constexpr mapping(const mapping&) noexcept = default;
|
|
|
|
|
|
|
|
constexpr mapping(const extents_type& _Exts_) noexcept : _Base(_Exts_) {
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
if constexpr (extents_type::rank_dynamic() != 0) {
|
|
|
|
_STL_VERIFY(_Exts_._Is_dynamic_multidim_index_space_size_representable(),
|
|
|
|
"The size of the multidimensional index space e must be representable as a value of type index_type "
|
|
|
|
"(N4950 [mdspan.layout.left.cons]/1).");
|
|
|
|
}
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _OtherExtents>
|
|
|
|
requires is_constructible_v<extents_type, _OtherExtents>
|
|
|
|
constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>)
|
|
|
|
mapping(const mapping<_OtherExtents>& _Other) noexcept
|
|
|
|
: _Base(_Other.extents()) {
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
_STL_VERIFY(_STD in_range<index_type>(_Other.required_span_size()),
|
|
|
|
"Value of other.required_span_size() must be representable as a value of type index_type (N4950 "
|
|
|
|
"[mdspan.layout.left.cons]/4).");
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _OtherExtents>
|
|
|
|
requires (extents_type::rank() <= 1) && is_constructible_v<extents_type, _OtherExtents>
|
|
|
|
constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>)
|
|
|
|
mapping(const layout_right::mapping<_OtherExtents>& _Other) noexcept
|
|
|
|
: _Base(_Other.extents()) {
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
_STL_VERIFY(_STD in_range<index_type>(_Other.required_span_size()),
|
|
|
|
"Value of other.required_span_size() must be representable as a value of type index_type (N4950 "
|
|
|
|
"[mdspan.layout.left.cons]/7).");
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _OtherExtents>
|
|
|
|
requires is_constructible_v<extents_type, _OtherExtents>
|
|
|
|
constexpr explicit(extents_type::rank() > 0)
|
|
|
|
mapping(const layout_stride::mapping<_OtherExtents>& _Other) noexcept // strengthened
|
|
|
|
: _Base(_Other.extents()) {
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
if constexpr (extents_type::rank() > 0) {
|
|
|
|
index_type _Prod = 1;
|
|
|
|
for (size_t _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) {
|
2023-12-07 19:28:23 +03:00
|
|
|
_STL_VERIFY(_STD cmp_equal(_Other.stride(_Idx), _Prod),
|
2023-09-14 21:59:38 +03:00
|
|
|
"For all r in the range [0, extents_type::rank()), other.stride(r) must be equal to "
|
|
|
|
"extents().fwd-prod-of-extents(r) (N4950 [mdspan.layout.left.cons]/10.1).");
|
|
|
|
_Prod = static_cast<index_type>(_Prod * this->_Exts.extent(_Idx));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_STL_VERIFY(_STD in_range<index_type>(_Other.required_span_size()),
|
|
|
|
"Value of other.required_span_size() must be representable as a value of type index_type (N4950 "
|
|
|
|
"[mdspan.layout.left.cons]/10.2).");
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr mapping& operator=(const mapping&) noexcept = default;
|
|
|
|
|
|
|
|
_NODISCARD constexpr const extents_type& extents() const noexcept {
|
|
|
|
return this->_Exts;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD constexpr index_type required_span_size() const noexcept {
|
|
|
|
return _Fwd_prod_of_extents<extents_type>::_Calculate(this->_Exts, extents_type::_Rank);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class... _IndexTypes>
|
|
|
|
requires (sizeof...(_IndexTypes) == extents_type::rank()) && (is_convertible_v<_IndexTypes, index_type> && ...)
|
|
|
|
&& (is_nothrow_constructible_v<index_type, _IndexTypes> && ...)
|
|
|
|
_NODISCARD constexpr index_type operator()(_IndexTypes... _Indices) const noexcept {
|
|
|
|
return _Index_impl(make_index_sequence<extents_type::rank()>{}, static_cast<index_type>(_Indices)...);
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD static constexpr bool is_always_unique() noexcept {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD static constexpr bool is_always_exhaustive() noexcept {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD static constexpr bool is_always_strided() noexcept {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD static constexpr bool is_unique() noexcept {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD static constexpr bool is_exhaustive() noexcept {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD static constexpr bool is_strided() noexcept {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD constexpr index_type stride(const rank_type _Idx) const noexcept
|
|
|
|
requires (extents_type::rank() > 0)
|
|
|
|
{
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
_STL_VERIFY(_Idx < extents_type::_Rank,
|
|
|
|
"Value of i must be less than extents_type::rank() (N4950 [mdspan.layout.left.obs]/6).");
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
return _Fwd_prod_of_extents<extents_type>::_Calculate(this->_Exts, _Idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _OtherExtents>
|
|
|
|
requires (extents_type::rank() == _OtherExtents::rank())
|
|
|
|
_NODISCARD_FRIEND constexpr bool operator==(const mapping& _Left, const mapping<_OtherExtents>& _Right) noexcept {
|
|
|
|
return _Left._Exts == _Right.extents();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
template <class... _IndexTypes, size_t... _Seq>
|
|
|
|
_NODISCARD constexpr index_type _Index_impl(
|
|
|
|
[[maybe_unused]] index_sequence<_Seq...> _Index_seq, _IndexTypes... _Indices) const noexcept {
|
|
|
|
_STL_INTERNAL_STATIC_ASSERT((same_as<_IndexTypes, index_type> && ...));
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
_STL_VERIFY(this->_Exts._Contains_multidimensional_index(_Index_seq, _Indices...),
|
|
|
|
"Value of extents_type::index-cast(i) must be a multidimensional index in extents_ (N4950 "
|
|
|
|
"[mdspan.layout.left.obs]/3).");
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
|
|
|
|
index_type _Stride = 1;
|
|
|
|
index_type _Result = 0;
|
|
|
|
(((_Result += _Indices * _Stride), (_Stride *= this->_Exts.extent(_Seq))), ...);
|
|
|
|
return _Result;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class _Extents>
|
|
|
|
class layout_right::mapping : private _Maybe_fully_static_extents<_Extents> {
|
|
|
|
public:
|
|
|
|
using extents_type = _Extents;
|
|
|
|
using index_type = extents_type::index_type;
|
|
|
|
using size_type = extents_type::size_type;
|
|
|
|
using rank_type = extents_type::rank_type;
|
|
|
|
using layout_type = layout_right;
|
|
|
|
|
|
|
|
private:
|
|
|
|
using _Base = _Maybe_fully_static_extents<extents_type>;
|
|
|
|
|
|
|
|
static_assert(_Is_extents<extents_type>,
|
|
|
|
"Extents must be a specialization of std::extents (N4950 [mdspan.layout.right.overview]/2).");
|
|
|
|
static_assert(
|
|
|
|
extents_type::rank_dynamic() != 0 || extents_type::_Is_static_multidim_index_space_size_representable(),
|
|
|
|
"If Extents::rank_dynamic() == 0 is true, then the size of the multidimensional index space Extents() must be "
|
|
|
|
"representable as a value of type typename Extents::index_type (N4950 [mdspan.layout.right.overview]/4).");
|
|
|
|
|
|
|
|
public:
|
|
|
|
constexpr mapping() noexcept = default;
|
|
|
|
constexpr mapping(const mapping&) noexcept = default;
|
|
|
|
|
|
|
|
constexpr mapping(const extents_type& _Exts_) noexcept : _Base(_Exts_) {
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
if constexpr (extents_type::rank_dynamic() != 0) {
|
|
|
|
_STL_VERIFY(_Exts_._Is_dynamic_multidim_index_space_size_representable(),
|
|
|
|
"The size of the multidimensional index space e must be representable as a value of type index_type "
|
|
|
|
"(N4950 [mdspan.layout.right.cons]/1).");
|
|
|
|
}
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _OtherExtents>
|
|
|
|
requires is_constructible_v<extents_type, _OtherExtents>
|
|
|
|
constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>)
|
|
|
|
mapping(const mapping<_OtherExtents>& _Other) noexcept
|
|
|
|
: _Base(_Other.extents()) {
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
_STL_VERIFY(_STD in_range<index_type>(_Other.required_span_size()),
|
|
|
|
"Value of other.required_span_size() must be representable as a value of type index_type (N4950 "
|
|
|
|
"[mdspan.layout.right.cons]/4).");
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _OtherExtents>
|
|
|
|
requires (extents_type::rank() <= 1) && is_constructible_v<extents_type, _OtherExtents>
|
|
|
|
constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>)
|
|
|
|
mapping(const layout_left::mapping<_OtherExtents>& _Other) noexcept
|
|
|
|
: _Base(_Other.extents()) {
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
_STL_VERIFY(_STD in_range<index_type>(_Other.required_span_size()),
|
|
|
|
"Value of other.required_span_size() must be representable as a value of type index_type (N4950 "
|
|
|
|
"[mdspan.layout.right.cons]/7).");
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _OtherExtents>
|
|
|
|
requires is_constructible_v<extents_type, _OtherExtents>
|
|
|
|
constexpr explicit(extents_type::rank() > 0) mapping(const layout_stride::mapping<_OtherExtents>& _Other) noexcept
|
|
|
|
: _Base(_Other.extents()) {
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
if constexpr (extents_type::rank() > 0) {
|
|
|
|
index_type _Prod = 1;
|
|
|
|
for (size_t _Idx = extents_type::_Rank; _Idx-- > 0;) {
|
2023-12-07 19:28:23 +03:00
|
|
|
_STL_VERIFY(_STD cmp_equal(_Prod, _Other.stride(_Idx)),
|
2023-09-14 21:59:38 +03:00
|
|
|
"For all r in the range [0, extents_type::rank()), other.stride(r) must be equal to "
|
|
|
|
"extents().rev-prod-of-extents(r) (N4950 [mdspan.layout.right.cons]/10.1).");
|
|
|
|
_Prod = static_cast<index_type>(_Prod * this->_Exts.extent(_Idx));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_STL_VERIFY(_STD in_range<index_type>(_Other.required_span_size()),
|
|
|
|
"Value of other.required_span_size() must be representable as a value of type index_type (N4950 "
|
|
|
|
"[mdspan.layout.right.cons]/10.2).");
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr mapping& operator=(const mapping&) noexcept = default;
|
|
|
|
|
|
|
|
_NODISCARD constexpr const extents_type& extents() const noexcept {
|
|
|
|
return this->_Exts;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD constexpr index_type required_span_size() const noexcept {
|
|
|
|
return _Fwd_prod_of_extents<extents_type>::_Calculate(this->_Exts, extents_type::_Rank);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class... _IndexTypes>
|
|
|
|
requires (sizeof...(_IndexTypes) == extents_type::rank()) && (is_convertible_v<_IndexTypes, index_type> && ...)
|
|
|
|
&& (is_nothrow_constructible_v<index_type, _IndexTypes> && ...)
|
|
|
|
_NODISCARD constexpr index_type operator()(_IndexTypes... _Indices) const noexcept {
|
|
|
|
return _Index_impl(make_index_sequence<extents_type::rank()>{}, static_cast<index_type>(_Indices)...);
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD static constexpr bool is_always_unique() noexcept {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD static constexpr bool is_always_exhaustive() noexcept {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD static constexpr bool is_always_strided() noexcept {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD static constexpr bool is_unique() noexcept {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD static constexpr bool is_exhaustive() noexcept {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD static constexpr bool is_strided() noexcept {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD constexpr index_type stride(const rank_type _Idx) const noexcept
|
|
|
|
requires (extents_type::rank() > 0)
|
|
|
|
{
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
_STL_VERIFY(_Idx < extents_type::_Rank,
|
|
|
|
"Value of i must be less than extents_type::rank() (N4950 [mdspan.layout.right.obs]/6).");
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
return _Rev_prod_of_extents<extents_type>::_Calculate(this->_Exts, _Idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _OtherExtents>
|
|
|
|
requires (extents_type::rank() == _OtherExtents::rank())
|
|
|
|
_NODISCARD_FRIEND constexpr bool operator==(const mapping& _Left, const mapping<_OtherExtents>& _Right) noexcept {
|
|
|
|
return _Left._Exts == _Right.extents();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
template <class... _IndexTypes, size_t... _Seq>
|
|
|
|
_NODISCARD constexpr index_type _Index_impl(
|
|
|
|
[[maybe_unused]] index_sequence<_Seq...> _Index_seq, _IndexTypes... _Indices) const noexcept {
|
|
|
|
_STL_INTERNAL_STATIC_ASSERT((same_as<_IndexTypes, index_type> && ...));
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
_STL_VERIFY(this->_Exts._Contains_multidimensional_index(_Index_seq, _Indices...),
|
|
|
|
"Value of extents_type::index-cast(i) must be a multidimensional index in extents_ (N4950 "
|
|
|
|
"[mdspan.layout.right.obs]/3).");
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
|
|
|
|
index_type _Result = 0;
|
|
|
|
((_Result = static_cast<index_type>(_Indices + this->_Exts.extent(_Seq) * _Result)), ...);
|
|
|
|
return _Result;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class _Mp>
|
|
|
|
concept _Layout_mapping_alike = requires {
|
|
|
|
requires _Is_extents<typename _Mp::extents_type>;
|
|
|
|
{ _Mp::is_always_strided() } -> same_as<bool>;
|
|
|
|
{ _Mp::is_always_exhaustive() } -> same_as<bool>;
|
|
|
|
{ _Mp::is_always_unique() } -> same_as<bool>;
|
|
|
|
bool_constant<_Mp::is_always_strided()>::value;
|
|
|
|
bool_constant<_Mp::is_always_exhaustive()>::value;
|
|
|
|
bool_constant<_Mp::is_always_unique()>::value;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class _Extents>
|
|
|
|
class layout_stride::mapping : private _Maybe_fully_static_extents<_Extents>,
|
|
|
|
private _Maybe_empty_array<typename _Extents::index_type, _Extents::rank()> {
|
|
|
|
public:
|
|
|
|
using extents_type = _Extents;
|
|
|
|
using index_type = extents_type::index_type;
|
|
|
|
using size_type = extents_type::size_type;
|
|
|
|
using rank_type = extents_type::rank_type;
|
|
|
|
using layout_type = layout_stride;
|
|
|
|
|
|
|
|
private:
|
|
|
|
using _Extents_base = _Maybe_fully_static_extents<extents_type>;
|
|
|
|
using _Strides_base = _Maybe_empty_array<index_type, _Extents::rank()>;
|
|
|
|
|
|
|
|
static_assert(_Is_extents<extents_type>,
|
|
|
|
"Extents must be a specialization of std::extents (N4950 [mdspan.layout.stride.overview]/2).");
|
|
|
|
static_assert(
|
|
|
|
extents_type::rank_dynamic() != 0 || extents_type::_Is_static_multidim_index_space_size_representable(),
|
|
|
|
"If Extents::rank_dynamic() == 0 is true, then the size of the multidimensional index space Extents() must be "
|
|
|
|
"representable as a value of type typename Extents::index_type (N4950 [mdspan.layout.stride.overview]/4).");
|
|
|
|
|
|
|
|
template <class _OtherIndexType, size_t... _Indices>
|
|
|
|
constexpr mapping(const extents_type& _Exts_, span<_OtherIndexType, extents_type::rank()> _Strides_,
|
|
|
|
index_sequence<_Indices...>) noexcept
|
|
|
|
: _Extents_base(_Exts_), _Strides_base{static_cast<index_type>(_STD as_const(_Strides_[_Indices]))...} {
|
|
|
|
_STL_INTERNAL_STATIC_ASSERT(is_convertible_v<const _OtherIndexType&, index_type>
|
|
|
|
&& is_nothrow_constructible_v<index_type, const _OtherIndexType&>);
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
if constexpr (extents_type::rank() != 0) {
|
|
|
|
bool _Found_zero = false;
|
|
|
|
bool _Overflow = false;
|
|
|
|
index_type _Req_span_size = 0;
|
|
|
|
for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) {
|
|
|
|
const index_type _Stride = this->_Array[_Idx];
|
|
|
|
_STL_VERIFY(_Stride > 0, "Value of s[i] must be greater than 0 for all i in the range [0, rank_) "
|
|
|
|
"(N4950 [mdspan.layout.stride.cons]/4.1).");
|
|
|
|
const index_type _Ext = this->_Exts.extent(_Idx);
|
|
|
|
if (_Ext == 0) {
|
|
|
|
_Found_zero = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!_Found_zero && !_Overflow) {
|
|
|
|
index_type _Prod;
|
|
|
|
_Overflow = _Mul_overflow(static_cast<index_type>(_Ext - 1), _Stride, _Prod)
|
|
|
|
|| _Add_overflow(_Req_span_size, _Prod, _Req_span_size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_STL_VERIFY(_Found_zero || !_Overflow, "REQUIRED-SPAN-SIZE(e, s) must be representable as a value of type "
|
|
|
|
"index_type (N4950 [mdspan.layout.stride.cons]/4.2).");
|
|
|
|
}
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
constexpr mapping() noexcept : _Extents_base(extents_type{}) {
|
|
|
|
if constexpr (extents_type::rank() != 0) {
|
|
|
|
this->_Array.back() = 1;
|
|
|
|
for (rank_type _Idx = extents_type::_Rank - 1; _Idx-- > 0;) {
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
const bool _Overflow =
|
|
|
|
_Mul_overflow(this->_Array[_Idx + 1], this->_Exts.extent(_Idx + 1), this->_Array[_Idx]);
|
|
|
|
// NB: N4950 requires value of 'layout_right::mapping<extents_type>().required_span_size()' to be
|
|
|
|
// representable as a value of type 'index_type', but this is not enough. We need to require every
|
|
|
|
// single stride to be representable as a value of type 'index_type', so we can get desired effects.
|
|
|
|
_STL_VERIFY(!_Overflow,
|
|
|
|
"Value of layout_right::mapping<extents_type>().required_span_size() must be "
|
|
|
|
"representable as a value of type index_type (N4950 [mdspan.layout.stride.cons]/1).");
|
|
|
|
#else // ^^^ _CONTAINER_DEBUG_LEVEL > 0 / _CONTAINER_DEBUG_LEVEL == 0 vvv
|
|
|
|
this->_Array[_Idx] = static_cast<index_type>(this->_Array[_Idx + 1] * this->_Exts.extent(_Idx + 1));
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr mapping(const mapping&) noexcept = default;
|
|
|
|
|
|
|
|
template <class _OtherIndexType>
|
2023-09-15 05:53:05 +03:00
|
|
|
requires is_convertible_v<const _OtherIndexType&, index_type>
|
|
|
|
&& is_nothrow_constructible_v<index_type, const _OtherIndexType&>
|
|
|
|
constexpr mapping(const extents_type& _Exts_, span<_OtherIndexType, extents_type::rank()> _Strides_) noexcept
|
2023-09-14 21:59:38 +03:00
|
|
|
: mapping(_Exts_, _Strides_, make_index_sequence<extents_type::rank()>{}) {}
|
|
|
|
|
|
|
|
template <class _OtherIndexType>
|
2023-09-15 05:53:05 +03:00
|
|
|
requires is_convertible_v<const _OtherIndexType&, index_type>
|
|
|
|
&& is_nothrow_constructible_v<index_type, const _OtherIndexType&>
|
|
|
|
constexpr mapping(
|
|
|
|
const extents_type& _Exts_, const array<_OtherIndexType, extents_type::rank()>& _Strides_) noexcept
|
2023-09-14 21:59:38 +03:00
|
|
|
: mapping(_Exts_, span{_Strides_}, make_index_sequence<extents_type::rank()>{}) {}
|
|
|
|
|
|
|
|
template <class _StridedLayoutMapping>
|
|
|
|
requires _Layout_mapping_alike<_StridedLayoutMapping>
|
|
|
|
&& is_constructible_v<extents_type, typename _StridedLayoutMapping::extents_type>
|
|
|
|
&& (_StridedLayoutMapping::is_always_unique()) && (_StridedLayoutMapping::is_always_strided())
|
|
|
|
constexpr explicit(!(
|
|
|
|
is_convertible_v<typename _StridedLayoutMapping::extents_type, extents_type>
|
|
|
|
&& (_Is_mapping_of<layout_left, _StridedLayoutMapping> || _Is_mapping_of<layout_right, _StridedLayoutMapping>
|
|
|
|
|| _Is_mapping_of<layout_stride, _StridedLayoutMapping>) ))
|
|
|
|
mapping(const _StridedLayoutMapping& _Other) noexcept
|
|
|
|
: _Extents_base(_Other.extents()) {
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
_STL_VERIFY(_STD in_range<index_type>(_Other.required_span_size()),
|
|
|
|
"Value of other.required_span_size() must be representable as a value of type index_type (N4950 "
|
|
|
|
"[mdspan.layout.stride.cons]/7.3).");
|
|
|
|
_STL_VERIFY(
|
|
|
|
_Offset(_Other) == 0, "Value of OFFSET(other) must be equal to 0 (N4950 [mdspan.layout.stride.cons]/7.4).");
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
if constexpr (extents_type::_Rank != 0) {
|
|
|
|
for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) {
|
|
|
|
const auto _Stride = _Other.stride(_Idx);
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
_STL_VERIFY(_Stride > 0, "Value of other.stride(r) must be greater than 0 for every rank index r of "
|
|
|
|
"extents() (N4950 [mdspan.layout.stride.cons]/7.2).");
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
this->_Array[_Idx] = static_cast<index_type>(_Stride);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr mapping& operator=(const mapping&) noexcept = default;
|
|
|
|
|
|
|
|
_NODISCARD constexpr const extents_type& extents() const noexcept {
|
|
|
|
return this->_Exts;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD constexpr array<index_type, extents_type::rank()> strides() const noexcept {
|
|
|
|
if constexpr (extents_type::rank() == 0) {
|
|
|
|
return {};
|
|
|
|
} else {
|
|
|
|
return this->_Array;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD constexpr index_type required_span_size() const noexcept {
|
|
|
|
if constexpr (extents_type::rank() == 0) {
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
index_type _Result = 1;
|
|
|
|
for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) {
|
|
|
|
const index_type _Ext = this->_Exts.extent(_Idx);
|
|
|
|
if (_Ext == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
_Result += (_Ext - 1) * this->_Array[_Idx];
|
|
|
|
}
|
|
|
|
|
|
|
|
return _Result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class... _IndexTypes>
|
|
|
|
requires (sizeof...(_IndexTypes) == extents_type::rank()) && (is_convertible_v<_IndexTypes, index_type> && ...)
|
|
|
|
&& (is_nothrow_constructible_v<index_type, _IndexTypes> && ...)
|
|
|
|
_NODISCARD constexpr index_type operator()(_IndexTypes... _Indices) const noexcept {
|
|
|
|
return _Index_impl(make_index_sequence<extents_type::rank()>{}, static_cast<index_type>(_Indices)...);
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD static constexpr bool is_always_unique() noexcept {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD static constexpr bool is_always_exhaustive() noexcept {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD static constexpr bool is_always_strided() noexcept {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD static constexpr bool is_unique() noexcept {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD constexpr bool is_exhaustive() const noexcept {
|
|
|
|
if constexpr (extents_type::rank() == 0) {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return required_span_size()
|
|
|
|
== _Fwd_prod_of_extents<extents_type>::_Calculate(this->_Exts, extents_type::_Rank);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD static constexpr bool is_strided() noexcept {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD constexpr index_type stride(const rank_type _Idx) const noexcept {
|
|
|
|
if constexpr (extents_type::rank() == 0) {
|
|
|
|
_STL_VERIFY(false, "The argument to stride must be nonnegative and less than extents_type::rank().");
|
|
|
|
} else {
|
|
|
|
return this->_Array[_Idx];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _OtherMapping>
|
|
|
|
requires _Layout_mapping_alike<_OtherMapping> && (extents_type::rank() == _OtherMapping::extents_type::rank())
|
|
|
|
&& (_OtherMapping::is_always_strided())
|
|
|
|
_NODISCARD_FRIEND constexpr bool operator==(const mapping& _Left, const _OtherMapping& _Right) noexcept {
|
|
|
|
if constexpr (extents_type::rank() != 0) {
|
|
|
|
if (_Left.extents() != _Right.extents()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) {
|
|
|
|
if (_STD cmp_not_equal(_Left.stride(_Idx), _Right.stride(_Idx))) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return _Offset(_Right) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
template <class _OtherMapping>
|
|
|
|
_NODISCARD static constexpr _OtherMapping::index_type _Offset(_OtherMapping& _Mapping) noexcept {
|
|
|
|
if constexpr (extents_type::rank() == 0) {
|
|
|
|
return _Mapping();
|
|
|
|
} else {
|
|
|
|
for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) {
|
|
|
|
if (_Mapping.extents().extent(_Idx) == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return [&]<size_t... _Indices>(index_sequence<_Indices...>) { return _Mapping(((void) _Indices, 0)...); }(
|
|
|
|
make_index_sequence<extents_type::rank()>{});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class... _IndexTypes, size_t... _Seq>
|
|
|
|
_NODISCARD constexpr index_type _Index_impl(
|
|
|
|
[[maybe_unused]] index_sequence<_Seq...> _Index_seq, _IndexTypes... _Indices) const noexcept {
|
|
|
|
_STL_INTERNAL_STATIC_ASSERT((same_as<_IndexTypes, index_type> && ...));
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
_STL_VERIFY(this->_Exts._Contains_multidimensional_index(_Index_seq, _Indices...),
|
|
|
|
"Value of extents_type::index-cast(i) must be a multidimensional index in extents_ (N4950 "
|
|
|
|
"[mdspan.layout.stride.obs]/3).");
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
|
|
|
|
return static_cast<index_type>(((_Indices * this->_Array[_Seq]) + ... + 0));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
_EXPORT_STD template <class _ElementType>
|
|
|
|
struct default_accessor {
|
|
|
|
using offset_policy = default_accessor;
|
|
|
|
using element_type = _ElementType;
|
|
|
|
using reference = _ElementType&;
|
|
|
|
using data_handle_type = _ElementType*;
|
|
|
|
|
|
|
|
static_assert(
|
|
|
|
sizeof(element_type) > 0, "ElementType must be a complete type (N4950 [mdspan.accessor.default.overview]/2).");
|
|
|
|
static_assert(!is_abstract_v<element_type>,
|
|
|
|
"ElementType cannot be an abstract type (N4950 [mdspan.accessor.default.overview]/2).");
|
|
|
|
static_assert(
|
|
|
|
!is_array_v<element_type>, "ElementType cannot be an array type (N4950 [mdspan.accessor.default.overview]/2).");
|
|
|
|
|
|
|
|
constexpr default_accessor() noexcept = default;
|
|
|
|
|
|
|
|
template <class _OtherElementType>
|
|
|
|
requires is_convertible_v<_OtherElementType (*)[], element_type (*)[]>
|
|
|
|
constexpr default_accessor(default_accessor<_OtherElementType>) noexcept {}
|
|
|
|
|
|
|
|
_NODISCARD constexpr reference access(data_handle_type _Ptr, size_t _Idx) const noexcept {
|
|
|
|
return _Ptr[_Idx];
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD constexpr data_handle_type offset(data_handle_type _Ptr, size_t _Idx) const noexcept {
|
|
|
|
return _Ptr + _Idx;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class _LayoutPolicy, class _Extents>
|
|
|
|
concept _Elidable_layout_mapping =
|
|
|
|
(_Is_any_of_v<_LayoutPolicy, layout_left, layout_right> && _Extents::rank_dynamic() == 0)
|
|
|
|
|| (same_as<_LayoutPolicy, layout_stride> && _Extents::rank() == 0);
|
|
|
|
|
|
|
|
template <class _Extents, class _LayoutPolicy>
|
|
|
|
struct _Mdspan_mapping_base {
|
|
|
|
_STL_INTERNAL_STATIC_ASSERT(_Is_extents<_Extents>);
|
|
|
|
|
|
|
|
using _Mapping = _LayoutPolicy::template mapping<_Extents>;
|
|
|
|
|
|
|
|
constexpr _Mdspan_mapping_base() noexcept = default;
|
|
|
|
|
|
|
|
constexpr explicit _Mdspan_mapping_base(const _Extents& _Exts) : _Map(_Exts) {}
|
|
|
|
|
|
|
|
template <class _OtherMapping>
|
|
|
|
constexpr explicit _Mdspan_mapping_base(const _OtherMapping& _Map_) : _Map(_Map_) {}
|
|
|
|
|
|
|
|
_Mapping _Map = _Mapping();
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class _Extents, _Elidable_layout_mapping<_Extents> _LayoutPolicy>
|
|
|
|
struct _Mdspan_mapping_base<_Extents, _LayoutPolicy> {
|
|
|
|
_STL_INTERNAL_STATIC_ASSERT(_Is_extents<_Extents>);
|
|
|
|
|
|
|
|
using _Mapping = _LayoutPolicy::template mapping<_Extents>;
|
|
|
|
|
|
|
|
constexpr _Mdspan_mapping_base() noexcept = default;
|
|
|
|
|
|
|
|
constexpr explicit _Mdspan_mapping_base(const _Extents&) noexcept {}
|
|
|
|
|
|
|
|
template <class _OtherMapping>
|
|
|
|
constexpr explicit _Mdspan_mapping_base(const _OtherMapping& _Map_) {
|
|
|
|
// NB: Constructing _Mapping from _OtherMapping may have side effects - we should create a temporary.
|
|
|
|
if constexpr (!_Elidable_layout_mapping<typename _OtherMapping::layout_type, _Extents>) {
|
|
|
|
(void) _Mapping{_Map_};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static constexpr _Mapping _Map{};
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class _AccessorPolicy>
|
|
|
|
concept _Elidable_accessor_policy = _Is_specialization_v<_AccessorPolicy, default_accessor>;
|
|
|
|
|
|
|
|
template <class _AccessorPolicy>
|
|
|
|
struct _Mdspan_accessor_base {
|
|
|
|
constexpr _Mdspan_accessor_base() noexcept = default;
|
|
|
|
|
|
|
|
template <class _OtherAccessorPolicy>
|
|
|
|
constexpr explicit _Mdspan_accessor_base(const _OtherAccessorPolicy& _Acc_) : _Acc(_Acc_) {}
|
|
|
|
|
|
|
|
_AccessorPolicy _Acc = _AccessorPolicy();
|
|
|
|
};
|
|
|
|
|
|
|
|
template <_Elidable_accessor_policy _AccessorPolicy>
|
|
|
|
struct _Mdspan_accessor_base<_AccessorPolicy> {
|
|
|
|
constexpr _Mdspan_accessor_base() noexcept = default;
|
|
|
|
|
|
|
|
template <class _OtherAccessorPolicy>
|
|
|
|
constexpr explicit _Mdspan_accessor_base(const _OtherAccessorPolicy& _Acc_) {
|
|
|
|
// NB: Constructing _AccessorPolicy from _OtherAccessorPolicy may have side effects - we should create a
|
|
|
|
// temporary.
|
|
|
|
if constexpr (!_Elidable_accessor_policy<_OtherAccessorPolicy>) {
|
|
|
|
(void) _AccessorPolicy{_Acc_};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static constexpr _AccessorPolicy _Acc{};
|
|
|
|
};
|
|
|
|
|
2023-11-29 23:50:15 +03:00
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
template <class _IndexType, class _OtherIndexType>
|
|
|
|
_NODISCARD constexpr _IndexType _Mdspan_checked_index_cast(_OtherIndexType&& _Idx) noexcept(
|
|
|
|
is_nothrow_constructible_v<_IndexType, _OtherIndexType>) {
|
|
|
|
_STL_INTERNAL_STATIC_ASSERT(is_integral_v<_IndexType> && is_constructible_v<_IndexType, _OtherIndexType>);
|
|
|
|
|
|
|
|
using _Arg_value_t = remove_cvref_t<_OtherIndexType>;
|
|
|
|
if constexpr (is_integral_v<_Arg_value_t> && !is_same_v<_Arg_value_t, bool>) {
|
|
|
|
_STL_VERIFY(_STD in_range<_IndexType>(_Idx),
|
|
|
|
"Each argument to operator[] must be representable by index_type in order for the pack of arguments to be "
|
|
|
|
"a valid multidimensional index (N4964 [mdspan.mdspan.members]/3).");
|
|
|
|
}
|
|
|
|
return static_cast<_IndexType>(_STD forward<_OtherIndexType>(_Idx));
|
|
|
|
}
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
|
2023-09-14 21:59:38 +03:00
|
|
|
_EXPORT_STD template <class _ElementType, class _Extents, class _LayoutPolicy = layout_right,
|
|
|
|
class _AccessorPolicy = default_accessor<_ElementType>>
|
|
|
|
class __declspec(empty_bases) mdspan : private _Mdspan_mapping_base<_Extents, _LayoutPolicy>,
|
|
|
|
private _Mdspan_accessor_base<_AccessorPolicy> {
|
|
|
|
public:
|
|
|
|
using extents_type = _Extents;
|
|
|
|
using layout_type = _LayoutPolicy;
|
|
|
|
using accessor_type = _AccessorPolicy;
|
|
|
|
using mapping_type = layout_type::template mapping<extents_type>;
|
|
|
|
using element_type = _ElementType;
|
|
|
|
using value_type = remove_cv_t<element_type>;
|
|
|
|
using index_type = extents_type::index_type;
|
|
|
|
using size_type = extents_type::size_type;
|
|
|
|
using rank_type = extents_type::rank_type;
|
|
|
|
using data_handle_type = accessor_type::data_handle_type;
|
|
|
|
using reference = accessor_type::reference;
|
|
|
|
|
|
|
|
private:
|
|
|
|
using _Mapping_base = _Mdspan_mapping_base<extents_type, layout_type>;
|
|
|
|
using _Accessor_base = _Mdspan_accessor_base<accessor_type>;
|
|
|
|
|
|
|
|
static_assert(
|
|
|
|
sizeof(element_type) > 0, "ElementType must be a complete type (N4950 [mdspan.mdspan.overview]/2.1).");
|
|
|
|
static_assert(
|
|
|
|
!is_abstract_v<element_type>, "ElementType cannot be an abstract type (N4950 [mdspan.mdspan.overview]/2.1).");
|
|
|
|
static_assert(
|
|
|
|
!is_array_v<element_type>, "ElementType cannot be an array type (N4950 [mdspan.mdspan.overview]/2.1).");
|
|
|
|
static_assert(_Is_extents<extents_type>,
|
|
|
|
"Extents must be a specialization of std::extents (N4950 [mdspan.mdspan.overview]/2.2).");
|
|
|
|
static_assert(is_same_v<element_type, typename accessor_type::element_type>,
|
|
|
|
"ElementType and typename AccessorPolicy::element_type must be the same type (N4950 "
|
|
|
|
"[mdspan.mdspan.overview]/2.3).");
|
|
|
|
|
|
|
|
public:
|
|
|
|
_NODISCARD static constexpr rank_type rank() noexcept {
|
|
|
|
return extents_type::_Rank;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD static constexpr rank_type rank_dynamic() noexcept {
|
|
|
|
return extents_type::_Rank_dynamic;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD static constexpr size_t static_extent(const rank_type _Idx) noexcept {
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
_STL_VERIFY(_Idx < extents_type::_Rank, "Index must be less than rank() (N4950 [mdspan.extents.obs]/1)");
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
return extents_type::_Static_extents[_Idx];
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD constexpr index_type extent(const rank_type _Idx) const noexcept {
|
|
|
|
return this->_Map.extents().extent(_Idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr mdspan() noexcept(
|
|
|
|
is_nothrow_default_constructible_v<data_handle_type>&& is_nothrow_default_constructible_v<mapping_type>&&
|
|
|
|
is_nothrow_default_constructible_v<accessor_type>) // strengthened
|
|
|
|
requires (rank_dynamic() > 0) && is_default_constructible_v<data_handle_type>
|
|
|
|
&& is_default_constructible_v<mapping_type> && is_default_constructible_v<accessor_type>
|
|
|
|
{}
|
|
|
|
|
|
|
|
constexpr mdspan(const mdspan&) = default;
|
|
|
|
constexpr mdspan(mdspan&&) = default;
|
|
|
|
|
|
|
|
template <class... _OtherIndexTypes>
|
|
|
|
requires (is_convertible_v<_OtherIndexTypes, index_type> && ...)
|
|
|
|
&& (is_nothrow_constructible_v<index_type, _OtherIndexTypes> && ...)
|
|
|
|
&& (sizeof...(_OtherIndexTypes) == rank() || sizeof...(_OtherIndexTypes) == rank_dynamic())
|
|
|
|
&& is_constructible_v<mapping_type, extents_type> && is_default_constructible_v<accessor_type>
|
|
|
|
constexpr explicit mdspan(data_handle_type _Ptr_, _OtherIndexTypes... _Exts) noexcept(
|
|
|
|
is_nothrow_constructible_v<mapping_type, extents_type>&&
|
|
|
|
is_nothrow_default_constructible_v<accessor_type>) // strengthened
|
|
|
|
: _Mapping_base(extents_type{static_cast<index_type>(_STD move(_Exts))...}), _Accessor_base(),
|
|
|
|
_Ptr(_STD move(_Ptr_)) {}
|
|
|
|
|
|
|
|
template <class _OtherIndexType, size_t _Size>
|
|
|
|
requires is_convertible_v<const _OtherIndexType&, index_type>
|
|
|
|
&& is_nothrow_constructible_v<index_type, const _OtherIndexType&>
|
|
|
|
&& (_Size == rank() || _Size == rank_dynamic())
|
|
|
|
&& is_constructible_v<mapping_type, extents_type> && is_default_constructible_v<accessor_type>
|
|
|
|
constexpr explicit(_Size != rank_dynamic()) mdspan(data_handle_type _Ptr_,
|
|
|
|
span<_OtherIndexType, _Size> _Exts) noexcept(is_nothrow_constructible_v<mapping_type, extents_type>&&
|
|
|
|
is_nothrow_default_constructible_v<accessor_type>) // strengthened
|
|
|
|
: _Mapping_base(extents_type{_Exts}), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {}
|
|
|
|
|
|
|
|
template <class _OtherIndexType, size_t _Size>
|
|
|
|
requires is_convertible_v<const _OtherIndexType&, index_type>
|
|
|
|
&& is_nothrow_constructible_v<index_type, const _OtherIndexType&>
|
|
|
|
&& (_Size == rank() || _Size == rank_dynamic())
|
|
|
|
&& is_constructible_v<mapping_type, extents_type> && is_default_constructible_v<accessor_type>
|
|
|
|
constexpr explicit(_Size != rank_dynamic()) mdspan(data_handle_type _Ptr_,
|
|
|
|
const array<_OtherIndexType, _Size>& _Exts) noexcept(is_nothrow_constructible_v<mapping_type, extents_type>&&
|
|
|
|
is_nothrow_default_constructible_v<accessor_type>) // strengthened
|
|
|
|
: _Mapping_base(extents_type{_Exts}), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {}
|
|
|
|
|
|
|
|
constexpr mdspan(data_handle_type _Ptr_, const extents_type& _Exts) noexcept(
|
|
|
|
is_nothrow_constructible_v<mapping_type,
|
|
|
|
const extents_type&>&& is_nothrow_default_constructible_v<accessor_type>) // strengthened
|
|
|
|
requires is_constructible_v<mapping_type, const extents_type&> && is_default_constructible_v<accessor_type>
|
|
|
|
: _Mapping_base(_Exts), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {}
|
|
|
|
|
|
|
|
constexpr mdspan(data_handle_type _Ptr_, const mapping_type& _Map_) noexcept(
|
|
|
|
is_nothrow_copy_constructible_v<mapping_type>&&
|
|
|
|
is_nothrow_default_constructible_v<accessor_type>) // strengthened
|
|
|
|
requires is_default_constructible_v<accessor_type>
|
|
|
|
: _Mapping_base(_Map_), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {}
|
|
|
|
|
|
|
|
constexpr mdspan(data_handle_type _Ptr_, const mapping_type& _Map_, const accessor_type& _Acc_) noexcept(
|
|
|
|
is_nothrow_copy_constructible_v<mapping_type>&& is_nothrow_copy_constructible_v<accessor_type>) // strengthened
|
|
|
|
: _Mapping_base(_Map_), _Accessor_base(_Acc_), _Ptr(_STD move(_Ptr_)) {}
|
|
|
|
|
|
|
|
template <class _OtherElementType, class _OtherExtents, class _OtherLayoutPolicy, class _OtherAccessor>
|
|
|
|
requires is_constructible_v<mapping_type, const typename _OtherLayoutPolicy::template mapping<_OtherExtents>&>
|
|
|
|
&& is_constructible_v<accessor_type, const _OtherAccessor&>
|
|
|
|
constexpr explicit(
|
|
|
|
!is_convertible_v<const typename _OtherLayoutPolicy::template mapping<_OtherExtents>&, mapping_type>
|
|
|
|
|| !is_convertible_v<const _OtherAccessor&, accessor_type>)
|
|
|
|
mdspan(const mdspan<_OtherElementType, _OtherExtents, _OtherLayoutPolicy, _OtherAccessor>& _Other) noexcept(
|
|
|
|
is_nothrow_constructible_v<data_handle_type, const typename _OtherAccessor::data_handle_type&>&&
|
|
|
|
is_nothrow_constructible_v<mapping_type,
|
|
|
|
const typename _OtherLayoutPolicy::template mapping<_OtherExtents>&>&&
|
|
|
|
is_nothrow_constructible_v<accessor_type, const _OtherAccessor&>) // strengthened
|
|
|
|
: _Mapping_base(_Other.mapping()), _Accessor_base(_Other.accessor()), _Ptr(_Other.data_handle()) {
|
|
|
|
static_assert(is_constructible_v<data_handle_type, const typename _OtherAccessor::data_handle_type&>,
|
|
|
|
"The data_handle_type must be constructible from const typename OtherAccessor::data_handle_type& (N4950 "
|
|
|
|
"[mdspan.mdspan.cons]/20.1).");
|
|
|
|
static_assert(is_constructible_v<extents_type, _OtherExtents>,
|
|
|
|
"The extents_type must be constructible from OtherExtents (N4950 [mdspan.mdspan.cons]/20.2).");
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) {
|
|
|
|
const auto _Static_ext = extents_type::_Static_extents[_Idx];
|
|
|
|
_STL_VERIFY(_STD cmp_equal(_Static_ext, dynamic_extent) || _STD cmp_equal(_Static_ext, _Other.extent(_Idx)),
|
|
|
|
"For each rank index r of extents_type, static_extent(r) == dynamic_extent || static_extent(r) == "
|
|
|
|
"other.extent(r) must be true (N4950 [mdspan.mdspan.cons]/21.1).");
|
|
|
|
}
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
constexpr mdspan& operator=(const mdspan&) = default;
|
|
|
|
constexpr mdspan& operator=(mdspan&&) = default;
|
|
|
|
|
|
|
|
#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6
|
|
|
|
template <class... _OtherIndexTypes>
|
|
|
|
requires (is_convertible_v<_OtherIndexTypes, index_type> && ...)
|
|
|
|
&& (is_nothrow_constructible_v<index_type, _OtherIndexTypes> && ...)
|
|
|
|
&& (sizeof...(_OtherIndexTypes) == rank())
|
|
|
|
_NODISCARD constexpr reference operator[](_OtherIndexTypes... _Indices) const
|
|
|
|
noexcept(noexcept(_Access_impl(static_cast<index_type>(_STD move(_Indices))...))) /* strengthened */ {
|
2023-11-29 23:50:15 +03:00
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
return _Access_impl(_STD _Mdspan_checked_index_cast<index_type>(_STD move(_Indices))...);
|
|
|
|
#else // ^^^ _CONTAINER_DEBUG_LEVEL > 0 / _CONTAINER_DEBUG_LEVEL <= 0 vvv
|
2023-09-14 21:59:38 +03:00
|
|
|
return _Access_impl(static_cast<index_type>(_STD move(_Indices))...);
|
2023-11-29 23:50:15 +03:00
|
|
|
#endif // ^^^ _CONTAINER_DEBUG_LEVEL <= 0 ^^^
|
2023-09-14 21:59:38 +03:00
|
|
|
}
|
|
|
|
#endif // ^^^ defined(__cpp_multidimensional_subscript) ^^^
|
|
|
|
|
|
|
|
private:
|
|
|
|
template <class _OtherIndexType, size_t... _Seq>
|
|
|
|
_NODISCARD constexpr reference _Multidimensional_subscript(
|
|
|
|
span<_OtherIndexType, rank()> _Indices, index_sequence<_Seq...>) const
|
|
|
|
noexcept(noexcept(_Access_impl(static_cast<index_type>(_STD as_const(_Indices[_Seq]))...))) {
|
2023-11-29 23:50:15 +03:00
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
return _Access_impl(_STD _Mdspan_checked_index_cast<index_type>(_STD as_const(_Indices[_Seq]))...);
|
|
|
|
#else // ^^^ _CONTAINER_DEBUG_LEVEL > 0 / _CONTAINER_DEBUG_LEVEL <= 0 vvv
|
2023-09-14 21:59:38 +03:00
|
|
|
return _Access_impl(static_cast<index_type>(_STD as_const(_Indices[_Seq]))...);
|
2023-11-29 23:50:15 +03:00
|
|
|
#endif // ^^^ _CONTAINER_DEBUG_LEVEL <= 0 ^^^
|
2023-09-14 21:59:38 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
template <class _OtherIndexType>
|
|
|
|
requires is_convertible_v<const _OtherIndexType&, index_type>
|
|
|
|
&& is_nothrow_constructible_v<index_type, const _OtherIndexType&>
|
|
|
|
_NODISCARD constexpr reference operator[](span<_OtherIndexType, rank()> _Indices) const
|
2023-12-07 19:41:33 +03:00
|
|
|
noexcept(noexcept(_Multidimensional_subscript(_Indices, make_index_sequence<rank()>{}))) /* strengthened */ {
|
2023-09-14 21:59:38 +03:00
|
|
|
return _Multidimensional_subscript(_Indices, make_index_sequence<rank()>{});
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class _OtherIndexType>
|
|
|
|
requires is_convertible_v<const _OtherIndexType&, index_type>
|
|
|
|
&& is_nothrow_constructible_v<index_type, const _OtherIndexType&>
|
|
|
|
_NODISCARD constexpr reference operator[](const array<_OtherIndexType, rank()>& _Indices) const noexcept(
|
2023-12-07 19:41:33 +03:00
|
|
|
noexcept(_Multidimensional_subscript(span{_Indices}, make_index_sequence<rank()>{}))) /* strengthened */ {
|
2023-09-14 21:59:38 +03:00
|
|
|
return _Multidimensional_subscript(span{_Indices}, make_index_sequence<rank()>{});
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD constexpr size_type size() const noexcept {
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
if constexpr (rank_dynamic() != 0) {
|
|
|
|
_STL_VERIFY(this->_Map.extents().template _Is_dynamic_multidim_index_space_size_representable<size_type>(),
|
|
|
|
"The size of the multidimensional index space extents() must be representable as a value of type "
|
|
|
|
"size_type (N4950 [mdspan.mdspan.members]/7).");
|
|
|
|
}
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
return static_cast<size_type>(
|
|
|
|
_Fwd_prod_of_extents<extents_type>::_Calculate(this->_Map.extents(), extents_type::_Rank));
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD constexpr bool empty() const noexcept {
|
|
|
|
if constexpr (extents_type::_Multidim_index_space_size_is_always_zero) {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
const extents_type& _Exts = this->_Map.extents();
|
|
|
|
for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) {
|
|
|
|
if (_Exts.extent(_Idx) == 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
friend constexpr void swap(mdspan& _Left, mdspan& _Right) noexcept {
|
|
|
|
swap(_Left._Ptr, _Right._Ptr); // intentional ADL
|
|
|
|
|
|
|
|
if constexpr (!_Elidable_layout_mapping<layout_type, extents_type>) {
|
|
|
|
swap(_Left._Map, _Right._Map); // intentional ADL
|
|
|
|
}
|
|
|
|
|
|
|
|
if constexpr (!_Elidable_accessor_policy<accessor_type>) {
|
|
|
|
swap(_Left._Acc, _Right._Acc); // intentional ADL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD constexpr const extents_type& extents() const noexcept {
|
|
|
|
return this->_Map.extents();
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD constexpr const data_handle_type& data_handle() const noexcept {
|
|
|
|
return _Ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD constexpr const mapping_type& mapping() const noexcept {
|
|
|
|
return this->_Map;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD constexpr const accessor_type& accessor() const noexcept {
|
|
|
|
return this->_Acc;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD static constexpr bool is_always_unique() noexcept /* strengthened */ {
|
|
|
|
constexpr bool _Result = mapping_type::is_always_unique();
|
|
|
|
return _Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD static constexpr bool is_always_exhaustive() noexcept /* strengthened */ {
|
|
|
|
constexpr bool _Result = mapping_type::is_always_exhaustive();
|
|
|
|
return _Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD static constexpr bool is_always_strided() noexcept /* strengthened */ {
|
|
|
|
constexpr bool _Result = mapping_type::is_always_strided();
|
|
|
|
return _Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD constexpr bool is_unique() const noexcept(noexcept(this->_Map.is_unique())) /* strengthened */ {
|
|
|
|
return this->_Map.is_unique();
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD constexpr bool is_exhaustive() const noexcept(noexcept(this->_Map.is_exhaustive())) /* strengthened */ {
|
|
|
|
return this->_Map.is_exhaustive();
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD constexpr bool is_strided() const noexcept(noexcept(this->_Map.is_strided())) /* strengthened */ {
|
|
|
|
return this->_Map.is_strided();
|
|
|
|
}
|
|
|
|
|
|
|
|
_NODISCARD constexpr index_type stride(const rank_type _Idx) const
|
|
|
|
noexcept(noexcept(this->_Map.stride(_Idx))) /* strengthened */ {
|
|
|
|
return this->_Map.stride(_Idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
template <class... _OtherIndexTypes>
|
|
|
|
_NODISCARD constexpr reference _Access_impl(_OtherIndexTypes... _Indices) const
|
|
|
|
noexcept(noexcept(this->_Acc.access(_Ptr, static_cast<size_t>(this->_Map(_Indices...))))) {
|
|
|
|
_STL_INTERNAL_STATIC_ASSERT((same_as<_OtherIndexTypes, index_type> && ...));
|
|
|
|
#if _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
_STL_VERIFY(this->_Map.extents()._Contains_multidimensional_index(make_index_sequence<rank()>{}, _Indices...),
|
|
|
|
"I must be a multidimensional index in extents() (N4950 [mdspan.mdspan.members]/3).");
|
|
|
|
#endif // _CONTAINER_DEBUG_LEVEL > 0
|
|
|
|
|
|
|
|
return this->_Acc.access(_Ptr, static_cast<size_t>(this->_Map(_Indices...)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* [[no_unique_address]] */ data_handle_type _Ptr = data_handle_type();
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class _CArray>
|
|
|
|
requires (is_array_v<_CArray> && rank_v<_CArray> == 1)
|
|
|
|
mdspan(_CArray&) -> mdspan<remove_all_extents_t<_CArray>, extents<size_t, extent_v<_CArray, 0>>>;
|
|
|
|
|
|
|
|
template <class _Pointer>
|
|
|
|
requires (is_pointer_v<remove_reference_t<_Pointer>>)
|
|
|
|
mdspan(_Pointer&&) -> mdspan<remove_pointer_t<remove_reference_t<_Pointer>>, extents<size_t>>;
|
|
|
|
|
|
|
|
template <class _ElementType, class... _Integrals>
|
|
|
|
requires ((is_convertible_v<_Integrals, size_t> && ...) && sizeof...(_Integrals) > 0)
|
|
|
|
explicit mdspan(_ElementType*, _Integrals...) -> mdspan<_ElementType, dextents<size_t, sizeof...(_Integrals)>>;
|
|
|
|
|
|
|
|
template <class _ElementType, class _OtherIndexType, size_t _Nx>
|
|
|
|
mdspan(_ElementType*, span<_OtherIndexType, _Nx>) -> mdspan<_ElementType, dextents<size_t, _Nx>>;
|
|
|
|
|
|
|
|
template <class _ElementType, class _OtherIndexType, size_t _Nx>
|
|
|
|
mdspan(_ElementType*, const array<_OtherIndexType, _Nx>&) -> mdspan<_ElementType, dextents<size_t, _Nx>>;
|
|
|
|
|
|
|
|
template <class _ElementType, class _IndexType, size_t... _ExtentsPack>
|
|
|
|
mdspan(_ElementType*, const extents<_IndexType, _ExtentsPack...>&)
|
|
|
|
-> mdspan<_ElementType, extents<_IndexType, _ExtentsPack...>>;
|
|
|
|
|
|
|
|
template <class _ElementType, class _MappingType>
|
|
|
|
mdspan(_ElementType*, const _MappingType&)
|
|
|
|
-> mdspan<_ElementType, typename _MappingType::extents_type, typename _MappingType::layout_type>;
|
|
|
|
|
|
|
|
template <class _MappingType, class _AccessorType>
|
|
|
|
mdspan(const typename _AccessorType::data_handle_type&, const _MappingType&, const _AccessorType&)
|
|
|
|
-> mdspan<typename _AccessorType::element_type, typename _MappingType::extents_type,
|
|
|
|
typename _MappingType::layout_type, _AccessorType>;
|
|
|
|
|
|
|
|
_STD_END
|
|
|
|
|
|
|
|
// TRANSITION, non-_Ugly attribute tokens
|
|
|
|
#pragma pop_macro("empty_bases")
|
|
|
|
|
|
|
|
#pragma pop_macro("new")
|
|
|
|
_STL_RESTORE_CLANG_WARNINGS
|
|
|
|
#pragma warning(pop)
|
|
|
|
#pragma pack(pop)
|
|
|
|
#endif // ^^^ supported language mode ^^^
|
|
|
|
#endif // _STL_COMPILER_PREPROCESSOR
|
|
|
|
#endif // _MDSPAN_
|