[libc++] mdspan - implement layout_stride (#69650)

This implements layout_stride for C++23 and with that completes the
implementation of the C++23 mdspan header. The feature test macro is
added, and the status pages updated.

Co-authored-by: Damien L-G <dalg24@gmail.com>

Differential Revision: https://reviews.llvm.org/D157171
This commit is contained in:
Christian Trott 2023-10-20 08:13:52 -06:00 committed by GitHub
parent 2a32afddf5
commit 639a0986f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
68 changed files with 2835 additions and 108 deletions

View File

@ -332,7 +332,7 @@ Status
--------------------------------------------------- -----------------
``__cpp_lib_is_scoped_enum`` ``202011L``
--------------------------------------------------- -----------------
``__cpp_lib_mdspan`` *unimplemented*
``__cpp_lib_mdspan`` ``202207L``
--------------------------------------------------- -----------------
``__cpp_lib_move_only_function`` *unimplemented*
--------------------------------------------------- -----------------

View File

@ -40,7 +40,6 @@ Paper Status
.. note::
.. [#note-P0009R18] P0009R18: ``extents``, ``dextents``, ``layout_left``, ``layout_right``, and ``default_accessor`` are implemented.
.. [#note-P0533R9] P0533R9: ``isfinite``, ``isinf``, ``isnan`` and ``isnormal`` are implemented.
.. [#note-P1413R3] P1413R3: ``std::aligned_storage_t`` and ``std::aligned_union_t`` are marked deprecated, but
clang doesn't issue a diagnostic for deprecated using template declarations.

View File

@ -51,7 +51,7 @@
"`P2442R1 <https://wg21.link/P2442R1>`__","LWG","Windowing range adaptors: ``views::chunk`` and ``views::slide``","February 2022","","","|ranges|"
"`P2443R1 <https://wg21.link/P2443R1>`__","LWG","``views::chunk_by``","February 2022","|Complete|","18.0","|ranges|"
"","","","","","",""
"`P0009R18 <https://wg21.link/P0009R18>`__","LWG","mdspan: A Non-Owning Multidimensional Array Reference","July 2022","|In progress| [#note-P0009R18]_",""
"`P0009R18 <https://wg21.link/P0009R18>`__","LWG","mdspan: A Non-Owning Multidimensional Array Reference","July 2022","|Complete|","18.0"
"`P0429R9 <https://wg21.link/P0429R9>`__","LWG","A Standard ``flat_map``","July 2022","",""
"`P1169R4 <https://wg21.link/P1169R4>`__","LWG","``static operator()``","July 2022","|Complete|","16.0"
"`P1222R4 <https://wg21.link/P1222R4>`__","LWG","A Standard ``flat_set``","July 2022","",""
@ -89,9 +89,9 @@
"`P2549R1 <https://wg21.link/P2549R1>`__","LWG","``std::unexpected`` should have ``error()`` as member accessor","July 2022","|Complete|","16.0"
"`P2585R0 <https://wg21.link/P2585R0>`__","LWG","Improving default container formatting","July 2022","|Complete|","17.0"
"`P2590R2 <https://wg21.link/P2590R2>`__","LWG","Explicit lifetime management","July 2022","",""
"`P2599R2 <https://wg21.link/P2599R2>`__","LWG","``mdspan::size_type`` should be ``index_type``","July 2022","",""
"`P2604R0 <https://wg21.link/P2604R0>`__","LWG","mdspan: rename pointer and contiguous","July 2022","",""
"`P2613R1 <https://wg21.link/P2613R1>`__","LWG","Add the missing ``empty`` to ``mdspan``","July 2022","",""
"`P2599R2 <https://wg21.link/P2599R2>`__","LWG","``mdspan::size_type`` should be ``index_type``","July 2022","|Complete|","18.0"
"`P2604R0 <https://wg21.link/P2604R0>`__","LWG","mdspan: rename pointer and contiguous","July 2022","|Complete|","18.0"
"`P2613R1 <https://wg21.link/P2613R1>`__","LWG","Add the missing ``empty`` to ``mdspan``","July 2022","|Complete|","18.0"
"","","","","","",""
"`P1202R5 <https://wg21.link/P1202R5>`__","LWG", "Asymmetric Fences", "November 2022","","","|concurrency TS|"
"`P1264R2 <https://wg21.link/P1264R2>`__","LWG", "Revising the wording of ``stream`` input operations", "November 2022","|Complete|","9.0",""

Can't render this file because it has a wrong number of fields in line 2.

View File

@ -508,6 +508,7 @@ set(files
__mdspan/extents.h
__mdspan/layout_left.h
__mdspan/layout_right.h
__mdspan/layout_stride.h
__mdspan/mdspan.h
__memory/addressof.h
__memory/align.h

View File

@ -42,14 +42,11 @@ struct layout_right {
class mapping;
};
/*
// Will be implemented with follow on revision
// Layout policy with a unique mapping where strides are arbitrary
struct layout_stride {
template<class Extents>
class mapping;
template <class _Extents>
class mapping;
};
*/
#endif // _LIBCPP_STD_VER >= 23

View File

@ -110,12 +110,27 @@ public:
"layout_left::mapping converting ctor: other.required_span_size() must be representable as index_type.");
}
// FIXME: add when we add other layouts
# if 0
template<class _OtherExtents>
constexpr explicit(extents_type::rank() > 0)
mapping(const layout_stride::mapping_<OtherExtents>&) noexcept;
# endif
template <class _OtherExtents>
requires(is_constructible_v<extents_type, _OtherExtents>)
_LIBCPP_HIDE_FROM_ABI constexpr explicit(extents_type::rank() > 0)
mapping(const layout_stride::mapping<_OtherExtents>& __other) noexcept
: __extents_(__other.extents()) {
if constexpr (extents_type::rank() > 0) {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
([&]() {
using _CommonType = common_type_t<typename extents_type::index_type, typename _OtherExtents::index_type>;
for (rank_type __r = 0; __r < extents_type::rank(); __r++)
if (static_cast<_CommonType>(stride(__r)) != static_cast<_CommonType>(__other.stride(__r)))
return false;
return true;
}()),
"layout_left::mapping from layout_stride ctor: strides are not compatible with layout_left.");
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
__mdspan_detail::__is_representable_as<index_type>(__other.required_span_size()),
"layout_left::mapping from layout_stride ctor: other.required_span_size() must be representable as "
"index_type.");
}
}
_LIBCPP_HIDE_FROM_ABI constexpr mapping& operator=(const mapping&) noexcept = default;

View File

@ -109,12 +109,27 @@ public:
"layout_right::mapping converting ctor: other.required_span_size() must be representable as index_type.");
}
// FIXME: add when we add other layouts
# if 0
template<class _OtherExtents>
constexpr explicit(extents_type::rank() > 0)
mapping(const layout_stride::mapping_<OtherExtents>&) noexcept;
# endif
template <class _OtherExtents>
requires(is_constructible_v<extents_type, _OtherExtents>)
_LIBCPP_HIDE_FROM_ABI constexpr explicit(extents_type::rank() > 0)
mapping(const layout_stride::mapping<_OtherExtents>& __other) noexcept
: __extents_(__other.extents()) {
if constexpr (extents_type::rank() > 0) {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
([&]() {
using _CommonType = common_type_t<typename extents_type::index_type, typename _OtherExtents::index_type>;
for (rank_type __r = 0; __r < extents_type::rank(); __r++)
if (static_cast<_CommonType>(stride(__r)) != static_cast<_CommonType>(__other.stride(__r)))
return false;
return true;
}()),
"layout_right::mapping from layout_stride ctor: strides are not compatible with layout_right.");
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
__mdspan_detail::__is_representable_as<index_type>(__other.required_span_size()),
"layout_right::mapping from layout_stride ctor: other.required_span_size() must be representable as "
"index_type.");
}
}
_LIBCPP_HIDE_FROM_ABI constexpr mapping& operator=(const mapping&) noexcept = default;

View File

@ -0,0 +1,366 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// Kokkos v. 4.0
// Copyright (2022) National Technology & Engineering
// Solutions of Sandia, LLC (NTESS).
//
// Under the terms of Contract DE-NA0003525 with NTESS,
// the U.S. Government retains certain rights in this software.
//
//===---------------------------------------------------------------------===//
#ifndef _LIBCPP___MDSPAN_LAYOUT_STRIDE_H
#define _LIBCPP___MDSPAN_LAYOUT_STRIDE_H
#include <__assert>
#include <__config>
#include <__fwd/mdspan.h>
#include <__mdspan/extents.h>
#include <__type_traits/is_constructible.h>
#include <__type_traits/is_convertible.h>
#include <__type_traits/is_nothrow_constructible.h>
#include <__utility/as_const.h>
#include <__utility/integer_sequence.h>
#include <__utility/swap.h>
#include <array>
#include <cinttypes>
#include <cstddef>
#include <limits>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
_LIBCPP_PUSH_MACROS
#include <__undef_macros>
_LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER >= 23
namespace __mdspan_detail {
template <class _Layout, class _Mapping>
constexpr bool __is_mapping_of =
is_same_v<typename _Layout::template mapping<typename _Mapping::extents_type>, _Mapping>;
template <class _Mapping>
concept __layout_mapping_alike = requires {
requires __is_mapping_of<typename _Mapping::layout_type, _Mapping>;
requires __is_extents_v<typename _Mapping::extents_type>;
{ _Mapping::is_always_strided() } -> same_as<bool>;
{ _Mapping::is_always_exhaustive() } -> same_as<bool>;
{ _Mapping::is_always_unique() } -> same_as<bool>;
bool_constant<_Mapping::is_always_strided()>::value;
bool_constant<_Mapping::is_always_exhaustive()>::value;
bool_constant<_Mapping::is_always_unique()>::value;
};
} // namespace __mdspan_detail
template <class _Extents>
class layout_stride::mapping {
public:
static_assert(__mdspan_detail::__is_extents<_Extents>::value,
"layout_stride::mapping template argument must be a specialization of extents.");
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_stride;
private:
static constexpr rank_type __rank_ = extents_type::rank();
// Used for default construction check and mandates
_LIBCPP_HIDE_FROM_ABI static constexpr bool __required_span_size_is_representable(const extents_type& __ext) {
if constexpr (__rank_ == 0)
return true;
index_type __prod = __ext.extent(0);
for (rank_type __r = 1; __r < __rank_; __r++) {
bool __overflowed = __builtin_mul_overflow(__prod, __ext.extent(__r), &__prod);
if (__overflowed)
return false;
}
return true;
}
template <class _OtherIndexType>
_LIBCPP_HIDE_FROM_ABI static constexpr bool
__required_span_size_is_representable(const extents_type& __ext, span<_OtherIndexType, __rank_> __strides) {
if constexpr (__rank_ == 0)
return true;
index_type __size = 1;
for (rank_type __r = 0; __r < __rank_; __r++) {
// We can only check correct conversion of _OtherIndexType if it is an integral
if constexpr (is_integral_v<_OtherIndexType>) {
using _CommonType = common_type_t<index_type, _OtherIndexType>;
if (static_cast<_CommonType>(__strides[__r]) > static_cast<_CommonType>(numeric_limits<index_type>::max()))
return false;
}
if (__ext.extent(__r) == static_cast<index_type>(0))
return true;
index_type __prod = (__ext.extent(__r) - 1);
bool __overflowed_mul = __builtin_mul_overflow(__prod, static_cast<index_type>(__strides[__r]), &__prod);
if (__overflowed_mul)
return false;
bool __overflowed_add = __builtin_add_overflow(__size, __prod, &__size);
if (__overflowed_add)
return false;
}
return true;
}
// compute offset of a strided layout mapping
template <class _StridedMapping>
_LIBCPP_HIDE_FROM_ABI static constexpr index_type __offset(const _StridedMapping& __mapping) {
if constexpr (_StridedMapping::extents_type::rank() == 0) {
return static_cast<index_type>(__mapping());
} else if (__mapping.required_span_size() == static_cast<typename _StridedMapping::index_type>(0)) {
return static_cast<index_type>(0);
} else {
return [&]<size_t... _Pos>(index_sequence<_Pos...>) {
return static_cast<index_type>(__mapping((_Pos ? 0 : 0)...));
}(make_index_sequence<__rank_>());
}
}
// compute the permutation for sorting the stride array
// we never actually sort the stride array
_LIBCPP_HIDE_FROM_ABI constexpr void __bubble_sort_by_strides(array<rank_type, __rank_>& __permute) const {
for (rank_type __i = __rank_ - 1; __i > 0; __i--) {
for (rank_type __r = 0; __r < __i; __r++) {
if (__strides_[__permute[__r]] > __strides_[__permute[__r + 1]]) {
swap(__permute[__r], __permute[__r + 1]);
} else {
// if two strides are the same then one of the associated extents must be 1 or 0
// both could be, but you can't have one larger than 1 come first
if ((__strides_[__permute[__r]] == __strides_[__permute[__r + 1]]) &&
(__extents_.extent(__permute[__r]) > static_cast<index_type>(1)))
swap(__permute[__r], __permute[__r + 1]);
}
}
}
}
static_assert((extents_type::rank_dynamic() > 0) || __required_span_size_is_representable(extents_type()),
"layout_stride::mapping product of static extents must be representable as index_type.");
public:
// [mdspan.layout.stride.cons], constructors
_LIBCPP_HIDE_FROM_ABI constexpr mapping() noexcept : __extents_(extents_type()) {
// Note the nominal precondition is covered by above static assert since
// if rank_dynamic is != 0 required_span_size is zero for default construction
if constexpr (__rank_ > 0) {
index_type __stride = 1;
for (rank_type __r = __rank_ - 1; __r > static_cast<rank_type>(0); __r--) {
__strides_[__r] = __stride;
__stride *= __extents_.extent(__r);
}
__strides_[0] = __stride;
}
}
_LIBCPP_HIDE_FROM_ABI constexpr mapping(const mapping&) noexcept = default;
template <class _OtherIndexType>
requires(is_convertible_v<const _OtherIndexType&, index_type> &&
is_nothrow_constructible_v<index_type, const _OtherIndexType&>)
_LIBCPP_HIDE_FROM_ABI constexpr mapping(const extents_type& __ext, span<_OtherIndexType, __rank_> __strides) noexcept
: __extents_(__ext), __strides_([&]<size_t... _Pos>(index_sequence<_Pos...>) {
return __mdspan_detail::__possibly_empty_array<index_type, __rank_>{
static_cast<index_type>(std::as_const(__strides[_Pos]))...};
}(make_index_sequence<__rank_>())) {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
([&]<size_t... _Pos>(index_sequence<_Pos...>) {
// For integrals we can do a pre-conversion check, for other types not
if constexpr (is_integral_v<_OtherIndexType>) {
return ((__strides[_Pos] > static_cast<_OtherIndexType>(0)) && ... && true);
} else {
return ((static_cast<index_type>(__strides[_Pos]) > static_cast<index_type>(0)) && ... && true);
}
}(make_index_sequence<__rank_>())),
"layout_stride::mapping ctor: all strides must be greater than 0");
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
__required_span_size_is_representable(__ext, __strides),
"layout_stride::mapping ctor: required span size is not representable as index_type.");
if constexpr (__rank_ > 1) {
_LIBCPP_ASSERT_UNCATEGORIZED(
([&]<size_t... _Pos>(index_sequence<_Pos...>) {
// basically sort the dimensions based on strides and extents, sorting is represented in permute array
array<rank_type, __rank_> __permute{_Pos...};
__bubble_sort_by_strides(__permute);
// check that this permutations represents a growing set
for (rank_type __i = 1; __i < __rank_; __i++)
if (static_cast<index_type>(__strides[__permute[__i]]) <
static_cast<index_type>(__strides[__permute[__i - 1]]) * __extents_.extent(__permute[__i - 1]))
return false;
return true;
}(make_index_sequence<__rank_>())),
"layout_stride::mapping ctor: the provided extents and strides lead to a non-unique mapping");
}
}
template <class _OtherIndexType>
requires(is_convertible_v<const _OtherIndexType&, index_type> &&
is_nothrow_constructible_v<index_type, const _OtherIndexType&>)
_LIBCPP_HIDE_FROM_ABI constexpr mapping(const extents_type& __ext,
const array<_OtherIndexType, __rank_>& __strides) noexcept
: mapping(__ext, span(__strides)) {}
template <class _StridedLayoutMapping>
requires(__mdspan_detail::__layout_mapping_alike<_StridedLayoutMapping> &&
is_constructible_v<extents_type, typename _StridedLayoutMapping::extents_type> &&
_StridedLayoutMapping::is_always_unique() && _StridedLayoutMapping::is_always_strided())
_LIBCPP_HIDE_FROM_ABI constexpr explicit(
!(is_convertible_v<typename _StridedLayoutMapping::extents_type, extents_type> &&
(__mdspan_detail::__is_mapping_of<layout_left, _StridedLayoutMapping> ||
__mdspan_detail::__is_mapping_of<layout_right, _StridedLayoutMapping> ||
__mdspan_detail::__is_mapping_of<layout_stride, _StridedLayoutMapping>)))
mapping(const _StridedLayoutMapping& __other) noexcept
: __extents_(__other.extents()), __strides_([&]<size_t... _Pos>(index_sequence<_Pos...>) {
// stride() only compiles for rank > 0
if constexpr (__rank_ > 0) {
return __mdspan_detail::__possibly_empty_array<index_type, __rank_>{
static_cast<index_type>(__other.stride(_Pos))...};
} else {
return __mdspan_detail::__possibly_empty_array<index_type, 0>{};
}
}(make_index_sequence<__rank_>())) {
// stride() only compiles for rank > 0
if constexpr (__rank_ > 0) {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
([&]<size_t... _Pos>(index_sequence<_Pos...>) {
return ((static_cast<index_type>(__other.stride(_Pos)) > static_cast<index_type>(0)) && ... && true);
}(make_index_sequence<__rank_>())),
"layout_stride::mapping converting ctor: all strides must be greater than 0");
}
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
__mdspan_detail::__is_representable_as<index_type>(__other.required_span_size()),
"layout_stride::mapping converting ctor: other.required_span_size() must be representable as index_type.");
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(static_cast<index_type>(0) == __offset(__other),
"layout_stride::mapping converting ctor: base offset of mapping must be zero.");
}
_LIBCPP_HIDE_FROM_ABI constexpr mapping& operator=(const mapping&) noexcept = default;
// [mdspan.layout.stride.obs], observers
_LIBCPP_HIDE_FROM_ABI constexpr const extents_type& extents() const noexcept { return __extents_; }
_LIBCPP_HIDE_FROM_ABI constexpr array<index_type, __rank_> strides() const noexcept {
return [&]<size_t... _Pos>(index_sequence<_Pos...>) {
return array<index_type, __rank_>{__strides_[_Pos]...};
}(make_index_sequence<__rank_>());
}
_LIBCPP_HIDE_FROM_ABI constexpr index_type required_span_size() const noexcept {
if constexpr (__rank_ == 0) {
return static_cast<index_type>(1);
} else {
return [&]<size_t... _Pos>(index_sequence<_Pos...>) {
if ((__extents_.extent(_Pos) * ... * 1) == 0)
return static_cast<index_type>(0);
else
return static_cast<index_type>(
static_cast<index_type>(1) +
(((__extents_.extent(_Pos) - static_cast<index_type>(1)) * __strides_[_Pos]) + ... +
static_cast<index_type>(0)));
}(make_index_sequence<__rank_>());
}
}
template <class... _Indices>
requires((sizeof...(_Indices) == __rank_) && (is_convertible_v<_Indices, index_type> && ...) &&
(is_nothrow_constructible_v<index_type, _Indices> && ...))
_LIBCPP_HIDE_FROM_ABI constexpr index_type operator()(_Indices... __idx) const noexcept {
// Mappings are generally meant to be used for accessing allocations and are meant to guarantee to never
// return a value exceeding required_span_size(), which is used to know how large an allocation one needs
// Thus, this is a canonical point in multi-dimensional data structures to make invalid element access checks
// However, mdspan does check this on its own, so for now we avoid double checking in hardened mode
_LIBCPP_ASSERT_UNCATEGORIZED(__mdspan_detail::__is_multidimensional_index_in(__extents_, __idx...),
"layout_stride::mapping: out of bounds indexing");
return [&]<size_t... _Pos>(index_sequence<_Pos...>) {
return ((static_cast<index_type>(__idx) * __strides_[_Pos]) + ... + index_type(0));
}(make_index_sequence<sizeof...(_Indices)>());
}
_LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_unique() noexcept { return true; }
_LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_exhaustive() noexcept { return false; }
_LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_strided() noexcept { return true; }
_LIBCPP_HIDE_FROM_ABI static constexpr bool is_unique() noexcept { return true; }
// The answer of this function is fairly complex in the case where one or more
// extents are zero.
// Technically it is meaningless to query is_exhaustive() in that case, but unfortunately
// the way the standard defines this function, we can't give a simple true or false then.
_LIBCPP_HIDE_FROM_ABI constexpr bool is_exhaustive() const noexcept {
if constexpr (__rank_ == 0)
return true;
else {
index_type __span_size = required_span_size();
if (__span_size == static_cast<index_type>(0)) {
if constexpr (__rank_ == 1)
return __strides_[0] == 1;
else {
rank_type __r_largest = 0;
for (rank_type __r = 1; __r < __rank_; __r++)
if (__strides_[__r] > __strides_[__r_largest])
__r_largest = __r;
for (rank_type __r = 0; __r < __rank_; __r++)
if (__extents_.extent(__r) == 0 && __r != __r_largest)
return false;
return true;
}
} else {
return required_span_size() == [&]<size_t... _Pos>(index_sequence<_Pos...>) {
return (__extents_.extent(_Pos) * ... * static_cast<index_type>(1));
}(make_index_sequence<__rank_>());
}
}
}
_LIBCPP_HIDE_FROM_ABI static constexpr bool is_strided() noexcept { return true; }
// according to the standard layout_stride does not have a constraint on stride(r) for rank>0
// it still has the precondition though
_LIBCPP_HIDE_FROM_ABI constexpr index_type stride(rank_type __r) const noexcept {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__r < __rank_, "layout_stride::mapping::stride(): invalid rank index");
return __strides_[__r];
}
template <class _OtherMapping>
requires(__mdspan_detail::__layout_mapping_alike<_OtherMapping> &&
(_OtherMapping::extents_type::rank() == __rank_) && _OtherMapping::is_always_strided())
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const mapping& __lhs, const _OtherMapping& __rhs) noexcept {
if (__offset(__rhs))
return false;
if constexpr (__rank_ == 0)
return true;
else {
return __lhs.extents() == __rhs.extents() && [&]<size_t... _Pos>(index_sequence<_Pos...>) {
// avoid warning when comparing signed and unsigner integers and pick the wider of two types
using _CommonType = common_type_t<index_type, typename _OtherMapping::index_type>;
return ((static_cast<_CommonType>(__lhs.stride(_Pos)) == static_cast<_CommonType>(__rhs.stride(_Pos))) && ... &&
true);
}(make_index_sequence<__rank_>());
}
}
private:
_LIBCPP_NO_UNIQUE_ADDRESS extents_type __extents_{};
_LIBCPP_NO_UNIQUE_ADDRESS __mdspan_detail::__possibly_empty_array<index_type, __rank_> __strides_{};
};
#endif // _LIBCPP_STD_VER >= 23
_LIBCPP_END_NAMESPACE_STD
_LIBCPP_POP_MACROS
#endif // _LIBCPP___MDSPAN_LAYOUT_STRIDE_H

View File

@ -190,6 +190,63 @@ namespace std {
};
}
// layout_stride synopsis
namespace std {
template<class Extents>
class layout_stride::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_stride;
private:
static constexpr rank_type rank_ = extents_type::rank(); // exposition only
public:
// [mdspan.layout.stride.cons], constructors
constexpr mapping() noexcept;
constexpr mapping(const mapping&) noexcept = default;
template<class OtherIndexType>
constexpr mapping(const extents_type&, span<OtherIndexType, rank_>) noexcept;
template<class OtherIndexType>
constexpr mapping(const extents_type&, const array<OtherIndexType, rank_>&) noexcept;
template<class StridedLayoutMapping>
constexpr explicit(see below) mapping(const StridedLayoutMapping&) noexcept;
constexpr mapping& operator=(const mapping&) noexcept = default;
// [mdspan.layout.stride.obs], observers
constexpr const extents_type& extents() const noexcept { return extents_; }
constexpr array<index_type, rank_> strides() const noexcept { return strides_; }
constexpr index_type required_span_size() const noexcept;
template<class... Indices>
constexpr index_type operator()(Indices...) const noexcept;
static constexpr bool is_always_unique() noexcept { return true; }
static constexpr bool is_always_exhaustive() noexcept { return false; }
static constexpr bool is_always_strided() noexcept { return true; }
static constexpr bool is_unique() noexcept { return true; }
constexpr bool is_exhaustive() const noexcept;
static constexpr bool is_strided() noexcept { return true; }
constexpr index_type stride(rank_type i) const noexcept { return strides_[i]; }
template<class OtherMapping>
friend constexpr bool operator==(const mapping&, const OtherMapping&) noexcept;
private:
extents_type extents_{}; // exposition only
array<index_type, rank_> strides_{}; // exposition only
};
}
// default_accessor synopsis
namespace std {
@ -348,7 +405,9 @@ namespace std {
#include <__mdspan/extents.h>
#include <__mdspan/layout_left.h>
#include <__mdspan/layout_right.h>
#include <__mdspan/layout_stride.h>
#include <__mdspan/mdspan.h>
#include <version>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header

View File

@ -1510,6 +1510,7 @@ module std_private_mdspan_extents [system] {
}
module std_private_mdspan_layout_left [system] { header "__mdspan/layout_left.h" }
module std_private_mdspan_layout_right [system] { header "__mdspan/layout_right.h" }
module std_private_mdspan_layout_stride [system] { header "__mdspan/layout_stride.h" }
module std_private_mdspan_mdspan [system] { header "__mdspan/mdspan.h" }
module std_private_mdspan_mdspan_fwd [system] { header "__fwd/mdspan.h" }

View File

@ -427,7 +427,7 @@ __cpp_lib_within_lifetime 202306L <type_traits>
# define __cpp_lib_forward_like 202207L
# define __cpp_lib_invoke_r 202106L
# define __cpp_lib_is_scoped_enum 202011L
// # define __cpp_lib_mdspan 202207L
# define __cpp_lib_mdspan 202207L
// # define __cpp_lib_move_only_function 202110L
# undef __cpp_lib_optional
# define __cpp_lib_optional 202110L

View File

@ -18,7 +18,7 @@ export namespace std {
// [mdspan.layout], layout mapping
using std::layout_left;
using std::layout_right;
// using std::layout_stride;
using std::layout_stride;
// [mdspan.accessor.default], class template default_accessor
using std::default_accessor;

View File

@ -0,0 +1,83 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// REQUIRES: has-unix-headers
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-hardening-mode=unchecked
// XFAIL: availability-verbose_abort-missing
// ADDITIONAL_COMPILE_FLAGS: -Wno-ctad-maybe-unsupported
// FIXME: https://github.com/llvm/llvm-project/issues/64719
// There appear to be some issues around ctad which make it
// currently impossible to get this code warning free.
// Thus added the additional compile flag above
// <mdspan>
// template<class OtherExtents>
// constexpr explicit(extents_type::rank() > 0)
// mapping(const layout_stride::mapping<OtherExtents>& other);
//
// Constraints: is_constructible_v<extents_type, OtherExtents> is true.
//
// Preconditions:
// - If extents_type::rank() > 0 is true, then for all r in the range [0, extents_type::rank()),
// other.stride(r) equals other.extents().fwd-prod-of-extents(r), and
// - other.required_span_size() is representable as a value of type index_type ([basic.fundamental]).
//
// Effects: Direct-non-list-initializes extents_ with other.extents().
#include <mdspan>
#include <cassert>
#include "check_assertion.h"
int main(int, char**) {
constexpr size_t D = std::dynamic_extent;
// working case
{
std::layout_stride::mapping arg(std::extents<int, D>(5), std::array<int, 1>{1});
[[maybe_unused]] std::layout_left::mapping<std::extents<size_t, 5>> m(arg); // should work
}
// mismatch of static extent
{
std::layout_stride::mapping arg(std::extents<int, D>(5), std::array<int, 1>{1});
TEST_LIBCPP_ASSERT_FAILURE(([=] { std::layout_left::mapping<std::extents<int, 3>> m(arg); }()),
"extents construction: mismatch of provided arguments with static extents.");
}
// non-representability of extents itself
{
std::layout_stride::mapping arg(std::extents<int, D>(500), std::array<int, 1>{1});
TEST_LIBCPP_ASSERT_FAILURE(([=] { std::layout_left::mapping<std::extents<char, D>> m(arg); }()),
"extents ctor: arguments must be representable as index_type and nonnegative");
}
// non-representability of required span size
{
std::layout_stride::mapping arg(std::extents<int, D, D>(100, 3), std::array<int, 2>{1, 100});
TEST_LIBCPP_ASSERT_FAILURE(
([=] { std::layout_left::mapping<std::extents<char, D, D>> m(arg); }()),
"layout_left::mapping from layout_stride ctor: other.required_span_size() must be "
"representable as index_type.");
}
// strides are not layout_left compatible
{
std::layout_stride::mapping arg(std::extents<int, D>(5), std::array<int, 1>{2});
TEST_LIBCPP_ASSERT_FAILURE(
([=] { std::layout_left::mapping<std::extents<size_t, 5>> m(arg); }()),
"layout_left::mapping from layout_stride ctor: strides are not compatible with layout_left.");
}
{
std::layout_stride::mapping arg(std::extents<int, D, D>(100, 3), std::array<int, 2>{2, 200});
TEST_LIBCPP_ASSERT_FAILURE(
([=] { std::layout_left::mapping<std::extents<int, D, D>> m(arg); }()),
"layout_left::mapping from layout_stride ctor: strides are not compatible with layout_left.");
}
return 0;
}

View File

@ -0,0 +1,83 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// REQUIRES: has-unix-headers
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-hardening-mode=unchecked
// XFAIL: availability-verbose_abort-missing
// ADDITIONAL_COMPILE_FLAGS: -Wno-ctad-maybe-unsupported
// FIXME: https://github.com/llvm/llvm-project/issues/64719
// There appear to be some issues around ctad which make it
// currently impossible to get this code warning free.
// Thus added the additional compile flag above
// <mdspan>
// template<class OtherExtents>
// constexpr explicit(extents_type::rank() > 0)
// mapping(const layout_stride::mapping<OtherExtents>& other);
//
// Constraints: is_constructible_v<extents_type, OtherExtents> is true.
//
// Preconditions:
// - If extents_type::rank() > 0 is true, then for all r in the range [0, extents_type::rank()),
// other.stride(r) equals other.extents().fwd-prod-of-extents(r), and
// - other.required_span_size() is representable as a value of type index_type ([basic.fundamental]).
//
// Effects: Direct-non-list-initializes extents_ with other.extents().
#include <mdspan>
#include <cassert>
#include "check_assertion.h"
int main(int, char**) {
constexpr size_t D = std::dynamic_extent;
// working case
{
std::layout_stride::mapping arg(std::extents<int, D>(5), std::array<int, 1>{1});
[[maybe_unused]] std::layout_right::mapping<std::extents<size_t, 5>> m(arg); // should work
}
// mismatch of static extent
{
std::layout_stride::mapping arg(std::extents<int, D>(5), std::array<int, 1>{1});
TEST_LIBCPP_ASSERT_FAILURE(([=] { std::layout_right::mapping<std::extents<int, 3>> m(arg); }()),
"extents construction: mismatch of provided arguments with static extents.");
}
// non-representability of extents itself
{
std::layout_stride::mapping arg(std::extents<int, D>(500), std::array<int, 1>{1});
TEST_LIBCPP_ASSERT_FAILURE(([=] { std::layout_right::mapping<std::extents<char, D>> m(arg); }()),
"extents ctor: arguments must be representable as index_type and nonnegative");
}
// non-representability of required span size
{
std::layout_stride::mapping arg(std::extents<int, D, D>(100, 3), std::array<int, 2>{3, 1});
TEST_LIBCPP_ASSERT_FAILURE(
([=] { std::layout_right::mapping<std::extents<char, D, D>> m(arg); }()),
"layout_right::mapping from layout_stride ctor: other.required_span_size() must be "
"representable as index_type.");
}
// strides are not layout_right compatible
{
std::layout_stride::mapping arg(std::extents<int, D>(5), std::array<int, 1>{2});
TEST_LIBCPP_ASSERT_FAILURE(
([=] { std::layout_right::mapping<std::extents<size_t, 5>> m(arg); }()),
"layout_right::mapping from layout_stride ctor: strides are not compatible with layout_right.");
}
{
std::layout_stride::mapping arg(std::extents<int, D, D>(100, 3), std::array<int, 2>{6, 2});
TEST_LIBCPP_ASSERT_FAILURE(
([=] { std::layout_right::mapping<std::extents<int, D, D>> m(arg); }()),
"layout_right::mapping from layout_stride ctor: strides are not compatible with layout_right.");
}
return 0;
}

View File

@ -0,0 +1,112 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// REQUIRES: has-unix-headers
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-hardening-mode=unchecked
// XFAIL: availability-verbose_abort-missing
// <mdspan>
// template<class StridedLayoutMapping>
// constexpr explicit(see below)
// mapping(const StridedLayoutMapping& other) noexcept;
//
// Constraints:
// - layout-mapping-alike<StridedLayoutMapping> is satisfied.
// - is_constructible_v<extents_type, typename StridedLayoutMapping::extents_type> is true.
// - StridedLayoutMapping::is_always_unique() is true.
// - StridedLayoutMapping::is_always_strided() is true.
//
// Preconditions:
// - StridedLayoutMapping meets the layout mapping requirements ([mdspan.layout.policy.reqmts]),
// - other.stride(r) > 0 is true for every rank index r of extents(),
// - other.required_span_size() is representable as a value of type index_type ([basic.fundamental]), and
// - OFFSET(other) == 0 is true.
//
// Effects: Direct-non-list-initializes extents_ with other.extents(), and for all d in the range [0, rank_),
// direct-non-list-initializes strides_[d] with other.stride(d).
//
// Remarks: The expression inside explicit is equivalent to:
// - !(is_convertible_v<typename StridedLayoutMapping::extents_type, extents_type> &&
// (is-mapping-of<layout_left, LayoutStrideMapping> ||
// is-mapping-of<layout_right, LayoutStrideMapping> ||
// is-mapping-of<layout_stride, LayoutStrideMapping>))
#include <mdspan>
#include <cassert>
#include "check_assertion.h"
#include "../../../../../std/containers/views/mdspan/CustomTestLayouts.h"
int main(int, char**) {
constexpr size_t D = std::dynamic_extent;
// working case
{
std::extents<int, D, D> arg_exts{100, 5};
std::layout_stride::mapping<std::extents<int, D, D>> arg(arg_exts, std::array<int, 2>{1, 100});
[[maybe_unused]] std::layout_stride::mapping<std::extents<size_t, D, 5>> m(arg); // should work
}
// mismatch of static extent
{
std::extents<int, D, D> arg_exts{100, 5};
std::layout_stride::mapping<std::extents<int, D, D>> arg(arg_exts, std::array<int, 2>{1, 100});
TEST_LIBCPP_ASSERT_FAILURE(([=] { std::layout_stride::mapping<std::extents<int, D, 3>> m(arg); }()),
"extents construction: mismatch of provided arguments with static extents.");
}
// non-representability of extents itself
{
TEST_LIBCPP_ASSERT_FAILURE(
([=] {
std::layout_stride::mapping<std::extents<char, D>> m(
std::layout_stride::mapping<std::extents<int, D>>(std::extents<int, D>(500), std::array<int, 1>{1}));
}()),
"extents ctor: arguments must be representable as index_type and nonnegative");
}
// all strides must be larger than zero
{
always_convertible_layout::mapping<std::dextents<int, 2>> offset_map(std::dextents<int, 2>{10, 10}, 100, -1);
TEST_LIBCPP_ASSERT_FAILURE(
([=] { std::layout_stride::template mapping<std::extents<char, D, D>> m(offset_map); }()),
"layout_stride::mapping converting ctor: all strides must be greater than 0");
}
// required_span_size not representable, while individual extents are
{
std::extents<int, D, D> arg_exts{100, 5};
std::layout_stride::mapping<std::extents<int, D, D>> arg(arg_exts, std::array<int, 2>{1, 100});
// check extents would be constructible
[[maybe_unused]] std::extents<char, D, 5> e(arg_exts);
// but the product is not, so we can't use it for layout_stride
TEST_LIBCPP_ASSERT_FAILURE(
([=] { std::layout_stride::template mapping<std::extents<char, D, 5>> m(arg); }()),
"layout_stride::mapping converting ctor: other.required_span_size() must be representable as index_type.");
}
// required_span_size not representable, while individual extents are, edge case
{
// required span size = (3-1)*50 + (10-1) * 3 + 1 = 128
std::extents<int, D, D> arg_exts{3, 10};
std::layout_stride::mapping<std::extents<int, D, D>> arg(arg_exts, std::array<int, 2>{50, 3});
// sanity check:
assert(arg.required_span_size() == 128);
// check extents would be constructible
[[maybe_unused]] std::extents<signed char, D, 10> e(arg_exts);
// but the product is not, so we can't use it for layout_stride
TEST_LIBCPP_ASSERT_FAILURE(
([=] { std::layout_stride::template mapping<std::extents<signed char, D, 10>> m(arg); }()),
"layout_stride::mapping converting ctor: other.required_span_size() must be representable as index_type.");
}
// base offset must be 0 (i.e. mapping(0,...,0)==0) for a strided layout with positiv strides
{
always_convertible_layout::mapping<std::dextents<int, 2>> offset_map(std::dextents<int, 2>{10, 10}, 3);
TEST_LIBCPP_ASSERT_FAILURE(
([=] { std::layout_stride::template mapping<std::extents<char, D, D>> m(offset_map); }()),
"layout_stride::mapping converting ctor: base offset of mapping must be zero.");
}
return 0;
}

View File

@ -0,0 +1,67 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// REQUIRES: has-unix-headers
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: !libcpp-hardening-mode=debug
// XFAIL: availability-verbose_abort-missing
// <mdspan>
// template<class OtherIndexType>
// constexpr mapping(const extents_type& e, const array<OtherIndexType, rank_>& s) noexcept;
//
// Constraints:
// - is_convertible_v<const OtherIndexType&, index_type> is true, and
// - is_nothrow_constructible_v<index_type, const OtherIndexType&> is true.
//
// Preconditions:
// - s[i] > 0 is true for all i in the range [0, rank_).
// - REQUIRED-SPAN-SIZE(e, s) is representable as a value of type index_type ([basic.fundamental]).
// - If rank_ is greater than 0, then there exists a permutation P of the integers in the range [0, rank_),
// such that s[pi] >= s[pi_1] * e.extent(pi_1) is true for all i in the range [1, rank_), where
// pi is the ith element of P.
// [Note 1: For layout_stride, this condition is necessary and sufficient for is_unique() to be true. end note]
//
// Effects: Direct-non-list-initializes extents_ with e, and for all d in the range [0, rank_), direct-non-list-initializes strides_[d] with as_const(s[d]).
#include <mdspan>
#include <cassert>
#include "check_assertion.h"
int main(int, char**) {
constexpr size_t D = std::dynamic_extent;
// overlapping strides
{
TEST_LIBCPP_ASSERT_FAILURE(
([=] {
std::layout_stride::template mapping<std::extents<unsigned, D, 5, 7>> m(
std::extents<unsigned, D, 5, 7>(20), std::array<unsigned, 3>{4, 1, 200});
}()),
"layout_stride::mapping ctor: the provided extents and strides lead to a non-unique mapping");
}
// equal strides
{
// should work because one of the equal strides is associated with an extent of 1
[[maybe_unused]] std::layout_stride::template mapping<std::extents<unsigned, D, 5, 1>> m1(
std::extents<unsigned, D, 5, 1>(2), std::array<unsigned, 3>{5, 1, 5});
[[maybe_unused]] std::layout_stride::template mapping<std::extents<unsigned, D, 5, 2>> m2(
std::extents<unsigned, D, 5, 2>(1), std::array<unsigned, 3>{5, 1, 5});
// will fail because neither of the equal strides is associated with an extent of 1
TEST_LIBCPP_ASSERT_FAILURE(
([=] {
std::layout_stride::template mapping<std::extents<unsigned, D, 5, 2>> m3(
std::extents<unsigned, D, 5, 2>(2), std::array<unsigned, 3>{5, 1, 5});
}()),
"layout_stride::mapping ctor: the provided extents and strides lead to a non-unique mapping");
}
return 0;
}

View File

@ -0,0 +1,73 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// REQUIRES: has-unix-headers
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-hardening-mode=unchecked
// XFAIL: availability-verbose_abort-missing
// <mdspan>
// template<class OtherIndexType>
// constexpr mapping(const extents_type& e, const array<OtherIndexType, rank_>& s) noexcept;
//
// Constraints:
// - is_convertible_v<const OtherIndexType&, index_type> is true, and
// - is_nothrow_constructible_v<index_type, const OtherIndexType&> is true.
//
// Preconditions:
// - s[i] > 0 is true for all i in the range [0, rank_).
// - REQUIRED-SPAN-SIZE(e, s) is representable as a value of type index_type ([basic.fundamental]).
// - If rank_ is greater than 0, then there exists a permutation P of the integers in the range [0, rank_),
// such that s[pi] >= s[pi_1] * e.extent(pi_1) is true for all i in the range [1, rank_), where
// pi is the ith element of P.
// [Note 1: For layout_stride, this condition is necessary and sufficient for is_unique() to be true. end note]
//
// Effects: Direct-non-list-initializes extents_ with e, and for all d in the range [0, rank_), direct-non-list-initializes strides_[d] with as_const(s[d]).
#include <mdspan>
#include <cassert>
#include "check_assertion.h"
int main(int, char**) {
constexpr size_t D = std::dynamic_extent;
// the extents are representable but the product with strides is not, so we can't use it for layout_stride
TEST_LIBCPP_ASSERT_FAILURE(
([=] {
std::layout_stride::template mapping<std::extents<char, D, 5>> m(
std::extents<char, D, 5>(20), std::array<int, 2>{20, 1});
}()),
"layout_stride::mapping ctor: required span size is not representable as index_type.");
// check that if we first overflow in strides conversion we also fail
static_assert(static_cast<unsigned char>(257u) == 1);
TEST_LIBCPP_ASSERT_FAILURE(
([=] {
std::layout_stride::template mapping<std::extents<unsigned char, D, 5>> m(
std::extents<unsigned char, D, 5>(20), std::array<unsigned, 2>{257, 1});
}()),
"layout_stride::mapping ctor: required span size is not representable as index_type.");
// negative strides are not allowed, check with unsigned index_type so we make sure we catch that
TEST_LIBCPP_ASSERT_FAILURE(
([=] {
std::layout_stride::template mapping<std::extents<unsigned, D, 5>> m(
std::extents<unsigned, D, 5>(20), std::array<int, 2>{20, -1});
}()),
"layout_stride::mapping ctor: all strides must be greater than 0");
// zero strides are not allowed, check with unsigned index_type so we make sure we catch that
TEST_LIBCPP_ASSERT_FAILURE(
([=] {
std::layout_stride::template mapping<std::extents<unsigned, D, 5>> m(
std::extents<unsigned, D, 5>(20), std::array<unsigned, 2>{20, 0});
}()),
"layout_stride::mapping ctor: all strides must be greater than 0");
return 0;
}

View File

@ -0,0 +1,70 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// REQUIRES: has-unix-headers
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: !libcpp-hardening-mode=debug
// XFAIL: availability-verbose_abort-missing
// <mdspan>
// template<class OtherIndexType>
// constexpr mapping(const extents_type& e, span<OtherIndexType, rank_> s) noexcept;
//
// Constraints:
// - is_convertible_v<const OtherIndexType&, index_type> is true, and
// - is_nothrow_constructible_v<index_type, const OtherIndexType&> is true.
//
// Preconditions:
// - s[i] > 0 is true for all i in the range [0, rank_).
// - REQUIRED-SPAN-SIZE(e, s) is representable as a value of type index_type ([basic.fundamental]).
// - If rank_ is greater than 0, then there exists a permutation P of the integers in the range [0, rank_),
// such that s[pi] >= s[pi_1] * e.extent(pi_1) is true for all i in the range [1, rank_), where
// pi is the ith element of P.
// [Note 1: For layout_stride, this condition is necessary and sufficient for is_unique() to be true. end note]
//
// Effects: Direct-non-list-initializes extents_ with e, and for all d in the range [0, rank_), direct-non-list-initializes strides_[d] with as_const(s[d]).
#include <mdspan>
#include <cassert>
#include "check_assertion.h"
int main(int, char**) {
constexpr size_t D = std::dynamic_extent;
// overlapping strides
{
TEST_LIBCPP_ASSERT_FAILURE(
([=] {
std::array<unsigned, 3> strides{4, 1, 200};
std::layout_stride::template mapping<std::extents<unsigned, D, 5, 7>> m(
std::extents<unsigned, D, 5, 7>(20), std::span(strides));
}()),
"layout_stride::mapping ctor: the provided extents and strides lead to a non-unique mapping");
}
// equal strides
{
// should work because one of the equal strides is associated with an extent of 1
std::array<unsigned, 3> strides{5, 1, 5};
[[maybe_unused]] std::layout_stride::template mapping<std::extents<unsigned, D, 5, 1>> m1(
std::extents<unsigned, D, 5, 1>(2), std::span(strides));
[[maybe_unused]] std::layout_stride::template mapping<std::extents<unsigned, D, 5, 2>> m2(
std::extents<unsigned, D, 5, 2>(1), std::span(strides));
// will fail because neither of the equal strides is associated with an extent of 1
TEST_LIBCPP_ASSERT_FAILURE(
([=] {
std::layout_stride::template mapping<std::extents<unsigned, D, 5, 2>> m3(
std::extents<unsigned, D, 5, 2>(2), std::span(strides));
}()),
"layout_stride::mapping ctor: the provided extents and strides lead to a non-unique mapping");
}
return 0;
}

View File

@ -0,0 +1,80 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// REQUIRES: has-unix-headers
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-hardening-mode=unchecked
// XFAIL: availability-verbose_abort-missing
// <mdspan>
// template<class OtherIndexType>
// constexpr mapping(const extents_type& e, span<OtherIndexType, rank_> s) noexcept;
//
// Constraints:
// - is_convertible_v<const OtherIndexType&, index_type> is true, and
// - is_nothrow_constructible_v<index_type, const OtherIndexType&> is true.
//
// Preconditions:
// - s[i] > 0 is true for all i in the range [0, rank_).
// - REQUIRED-SPAN-SIZE(e, s) is representable as a value of type index_type ([basic.fundamental]).
// - If rank_ is greater than 0, then there exists a permutation P of the integers in the range [0, rank_),
// such that s[pi] >= s[pi_1] * e.extent(pi_1) is true for all i in the range [1, rank_), where
// pi is the ith element of P.
// [Note 1: For layout_stride, this condition is necessary and sufficient for is_unique() to be true. end note]
//
// Effects: Direct-non-list-initializes extents_ with e, and for all d in the range [0, rank_), direct-non-list-initializes strides_[d] with as_const(s[d]).
#include <mdspan>
#include <cassert>
#include "check_assertion.h"
int main(int, char**) {
constexpr size_t D = std::dynamic_extent;
// value out of range
{
// the extents are representable but the product with strides is not, so we can't use it for layout_stride
TEST_LIBCPP_ASSERT_FAILURE(
([=] {
std::array<int, 2> strides{20, 1};
std::layout_stride::template mapping<std::extents<char, D, 5>> m(
std::extents<char, D, 5>(20), std::span(strides));
}()),
"layout_stride::mapping ctor: required span size is not representable as index_type.");
// check that if we first overflow in strides conversion we also fail
static_assert(static_cast<unsigned char>(257u) == 1);
TEST_LIBCPP_ASSERT_FAILURE(
([=] {
std::array<unsigned, 2> strides{257, 1};
std::layout_stride::template mapping<std::extents<unsigned char, D, 5>> m(
std::extents<unsigned char, D, 5>(20), std::span(strides));
}()),
"layout_stride::mapping ctor: required span size is not representable as index_type.");
// negative strides are not allowed, check with unsigned index_type so we make sure we catch that
TEST_LIBCPP_ASSERT_FAILURE(
([=] {
std::array<int, 2> strides{20, -1};
std::layout_stride::template mapping<std::extents<unsigned, D, 5>> m(
std::extents<unsigned, D, 5>(20), std::span(strides));
}()),
"layout_stride::mapping ctor: all strides must be greater than 0");
// zero strides are not allowed, check with unsigned index_type so we make sure we catch that
TEST_LIBCPP_ASSERT_FAILURE(
([=] {
std::array<unsigned, 2> strides{20, 0};
std::layout_stride::template mapping<std::extents<unsigned, D, 5>> m(
std::extents<unsigned, D, 5>(20), std::span(strides));
}()),
"layout_stride::mapping ctor: all strides must be greater than 0");
}
return 0;
}

View File

@ -0,0 +1,88 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// REQUIRES: has-unix-headers
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: !libcpp-hardening-mode=debug
// XFAIL: availability-verbose_abort-missing
// <mdspan>
// template<class... Indices>
// constexpr index_type operator()(Indices... i) const noexcept;
//
// Constraints:
// - sizeof...(Indices) == extents_type::rank() is true,
// - (is_convertible_v<Indices, index_type> && ...) is true, and
// - (is_nothrow_constructible_v<index_type, Indices> && ...) is true.
//
// Preconditions: extents_type::index-cast(i) is a multidimensional index in extents_ ([mdspan.overview]).
#include <mdspan>
#include <cassert>
#include "check_assertion.h"
int main(int, char**) {
// value out of range
{
std::layout_stride::template mapping<std::extents<unsigned char, 5>> m;
TEST_LIBCPP_ASSERT_FAILURE(m(-1), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(-130), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(5), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(1000), "layout_stride::mapping: out of bounds indexing");
}
{
std::layout_stride::template mapping<std::extents<signed char, 5>> m;
TEST_LIBCPP_ASSERT_FAILURE(m(-1), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(-130), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(5), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(1000), "layout_stride::mapping: out of bounds indexing");
}
{
std::layout_stride::template mapping<std::dextents<unsigned char, 1>> m(
std::dextents<unsigned char, 1>(5), std::array<int, 1>{1});
TEST_LIBCPP_ASSERT_FAILURE(m(-1), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(-130), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(5), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(1000), "layout_stride::mapping: out of bounds indexing");
}
{
std::layout_stride::template mapping<std::dextents<signed char, 1>> m(
std::dextents<signed char, 1>(5), std::array<int, 1>{1});
TEST_LIBCPP_ASSERT_FAILURE(m(-1), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(-130), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(5), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(1000), "layout_stride::mapping: out of bounds indexing");
}
{
std::layout_stride::template mapping<std::dextents<int, 3>> m(
std::dextents<int, 3>(5, 7, 9), std::array<int, 3>{1, 10, 100});
TEST_LIBCPP_ASSERT_FAILURE(m(-1, -1, -1), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(-1, 0, 0), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(0, -1, 0), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(0, 0, -1), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(5, 3, 3), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(3, 7, 3), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(3, 3, 9), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(5, 7, 9), "layout_stride::mapping: out of bounds indexing");
}
{
std::layout_stride::template mapping<std::dextents<unsigned, 3>> m(
std::dextents<int, 3>(5, 7, 9), std::array<int, 3>{1, 10, 100});
TEST_LIBCPP_ASSERT_FAILURE(m(-1, -1, -1), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(-1, 0, 0), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(0, -1, 0), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(0, 0, -1), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(5, 3, 3), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(3, 7, 3), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(3, 3, 9), "layout_stride::mapping: out of bounds indexing");
TEST_LIBCPP_ASSERT_FAILURE(m(5, 7, 9), "layout_stride::mapping: out of bounds indexing");
}
return 0;
}

View File

@ -0,0 +1,36 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// REQUIRES: has-unix-headers
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-hardening-mode=unchecked
// XFAIL: availability-verbose_abort-missing
// <mdspan>
// constexpr index_type stride(rank_type i) const noexcept { return strides_[i]; }
// We intercept this inside layout_stride to give a consistent error message with
// layout_left and layout_right, technically the precondition is coming from the
// array access.
#include <mdspan>
#include <cassert>
#include "check_assertion.h"
int main(int, char**) {
// value out of range
{
std::layout_stride::template mapping<std::dextents<int, 3>> m(
std::dextents<int, 3>(100, 100, 100), std::array<int, 3>{1, 100, 10000});
TEST_LIBCPP_ASSERT_FAILURE(m.stride(4), "invalid rank index");
}
return 0;
}

View File

@ -42,7 +42,7 @@
#include <mdspan>
#include "check_assertion.h"
#include "../../../../../std/containers/views/mdspan/mdspan/CustomTestLayouts.h"
#include "../../../../../std/containers/views/mdspan/CustomTestLayouts.h"
// We use a funky mapping in this test that doesn't check the dynamic/static extents mismatch itself
int main(int, char**) {

View File

@ -22,7 +22,7 @@
#include <mdspan>
#include "check_assertion.h"
#include "../../../../../std/containers/views/mdspan/mdspan/CustomTestLayouts.h"
#include "../../../../../std/containers/views/mdspan/CustomTestLayouts.h"
// We use a funky mapping in this test where required_span_size is much smaller than the size of the index space
int main(int, char**) {

View File

@ -526,6 +526,7 @@ mdspan concepts
mdspan cstddef
mdspan limits
mdspan span
mdspan version
memory atomic
memory compare
memory concepts

1 algorithm atomic
526 mdspan cstddef
527 mdspan limits
528 mdspan span
529 mdspan version
530 memory atomic
531 memory compare
532 memory concepts

View File

@ -530,6 +530,7 @@ mdspan concepts
mdspan cstddef
mdspan limits
mdspan span
mdspan version
memory atomic
memory compare
memory concepts

1 algorithm atomic
530 mdspan cstddef
531 mdspan limits
532 mdspan span
533 mdspan version
534 memory atomic
535 memory compare
536 memory concepts

View File

@ -532,6 +532,7 @@ mdspan concepts
mdspan cstddef
mdspan limits
mdspan span
mdspan version
memory atomic
memory compare
memory concepts

1 algorithm atomic
532 mdspan cstddef
533 mdspan limits
534 mdspan span
535 mdspan version
536 memory atomic
537 memory compare
538 memory concepts

View File

@ -532,6 +532,7 @@ mdspan concepts
mdspan cstddef
mdspan limits
mdspan span
mdspan version
memory atomic
memory compare
memory concepts

1 algorithm atomic
532 mdspan cstddef
533 mdspan limits
534 mdspan span
535 mdspan version
536 memory atomic
537 memory compare
538 memory concepts

View File

@ -537,6 +537,7 @@ mdspan concepts
mdspan cstddef
mdspan limits
mdspan span
mdspan version
memory atomic
memory compare
memory concepts

1 algorithm atomic
537 mdspan cstddef
538 mdspan limits
539 mdspan span
540 mdspan version
541 memory atomic
542 memory compare
543 memory concepts

View File

@ -386,6 +386,7 @@ mdspan concepts
mdspan cstddef
mdspan limits
mdspan span
mdspan version
memory compare
memory cstddef
memory cstdint

1 algorithm climits
386 mdspan cstddef
387 mdspan limits
388 mdspan span
389 mdspan version
390 memory compare
391 memory cstddef
392 memory cstdint

View File

@ -386,6 +386,7 @@ mdspan concepts
mdspan cstddef
mdspan limits
mdspan span
mdspan version
memory compare
memory cstddef
memory cstdint

1 algorithm climits
386 mdspan cstddef
387 mdspan limits
388 mdspan span
389 mdspan version
390 memory compare
391 memory cstddef
392 memory cstdint

View File

@ -14,8 +14,8 @@
//
//===---------------------------------------------------------------------===//
#ifndef TEST_STD_CONTAINERS_VIEWS_MDSPAN_MDSPAN_CUSTOM_TEST_LAYOUTS_H
#define TEST_STD_CONTAINERS_VIEWS_MDSPAN_MDSPAN_CUSTOM_TEST_LAYOUTS_H
#ifndef TEST_STD_CONTAINERS_VIEWS_MDSPAN_CUSTOM_TEST_LAYOUTS_H
#define TEST_STD_CONTAINERS_VIEWS_MDSPAN_CUSTOM_TEST_LAYOUTS_H
#include <algorithm>
#include <array>
@ -205,9 +205,9 @@ constexpr auto construct_mapping(layout_wrapping_integral<Wraps>, Extents exts)
return typename layout_wrapping_integral<Wraps>::template mapping<Extents>(exts, not_extents_constructible_tag{});
}
// This layout does not check convertibility of extents for its conversion ctor
// Allows triggering mdspan's ctor static assertion on convertibility of extents
// It also allows for negative strides and offsets via runtime arguments
class always_convertible_layout {
public:
template <class Extents>
@ -239,8 +239,10 @@ private:
public:
constexpr mapping() noexcept = delete;
constexpr mapping(const mapping& other) noexcept : extents_(other.extents()){};
constexpr mapping(const extents_type& ext) noexcept : extents_(ext){};
constexpr mapping(const mapping& other) noexcept
: extents_(other.extents_), offset_(other.offset_), scaling_(other.scaling_){};
constexpr mapping(const extents_type& ext, index_type offset = 0, index_type scaling = 1) noexcept
: extents_(ext), offset_(offset), scaling_(scaling){};
template <class OtherExtents>
constexpr mapping(const mapping<OtherExtents>& other) noexcept {
@ -256,10 +258,14 @@ public:
} else {
extents_ = extents_type();
}
offset_ = other.offset_;
scaling_ = other.scaling_;
}
constexpr mapping& operator=(const mapping& other) noexcept {
extents_ = other.extents_;
offset_ = other.offset_;
scaling_ = other.scaling_;
return *this;
};
@ -269,7 +275,7 @@ public:
index_type size = 1;
for (size_t r = 0; r < extents_type::rank(); r++)
size *= extents_.extent(r);
return size;
return std::max(size * scaling_ + offset_, offset_);
}
template <std::integral... Indices>
@ -277,16 +283,18 @@ public:
(std::is_nothrow_constructible_v<index_type, Indices> && ...))
constexpr index_type operator()(Indices... idx) const noexcept {
std::array<index_type, extents_type::rank()> idx_a{static_cast<index_type>(static_cast<index_type>(idx))...};
return [&]<size_t... Pos>(std::index_sequence<Pos...>) {
index_type res = 0;
((res = idx_a[extents_type::rank() - 1 - Pos] + extents_.extent(extents_type::rank() - 1 - Pos) * res), ...);
return res;
}(std::make_index_sequence<sizeof...(Indices)>());
return offset_ +
scaling_ * ([&]<size_t... Pos>(std::index_sequence<Pos...>) {
index_type res = 0;
((res = idx_a[extents_type::rank() - 1 - Pos] + extents_.extent(extents_type::rank() - 1 - Pos) * res),
...);
return res;
}(std::make_index_sequence<sizeof...(Indices)>()));
}
static constexpr bool is_always_unique() noexcept { return false; }
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 false; }
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; }
@ -296,15 +304,15 @@ public:
requires(extents_type::rank() > 0)
{
index_type s = 1;
for (rank_type i = extents_type::rank() - 1; i > r; i--)
for (rank_type i = 0; i < r; i++)
s *= extents_.extent(i);
return s;
return s * scaling_;
}
template <class OtherExtents>
requires(OtherExtents::rank() == extents_type::rank())
friend constexpr bool operator==(const mapping& lhs, const mapping<OtherExtents>& rhs) noexcept {
return lhs.extents() == rhs.extents();
return lhs.extents() == rhs.extents() && lhs.offset_ == rhs.offset && lhs.scaling_ == rhs.scaling_;
}
friend constexpr void swap(mapping& x, mapping& y) noexcept {
@ -320,6 +328,11 @@ public:
}
private:
template <class>
friend class mapping;
extents_type extents_{};
index_type offset_{};
index_type scaling_{};
};
#endif // TEST_STD_CONTAINERS_VIEWS_MDSPAN_MDSPAN_CUSTOM_TEST_LAYOUTS_H
#endif // TEST_STD_CONTAINERS_VIEWS_MDSPAN_CUSTOM_TEST_LAYOUTS_H

View File

@ -0,0 +1,114 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// <mdspan>
// template<class OtherExtents>
// constexpr explicit(extents_type::rank() > 0)
// mapping(const layout_stride::mapping<OtherExtents>& other);
//
// Constraints: is_constructible_v<extents_type, OtherExtents> is true.
//
// Preconditions:
// - If extents_type::rank() > 0 is true, then for all r in the range [0, extents_type::rank()),
// other.stride(r) equals other.extents().fwd-prod-of-extents(r), and
// - other.required_span_size() is representable as a value of type index_type ([basic.fundamental]).
//
// Effects: Direct-non-list-initializes extents_ with other.extents().
#include <mdspan>
#include <type_traits>
#include <cassert>
#include <limits>
#include "test_macros.h"
template <bool implicit, class ToE, class FromE>
constexpr void test_conversion(FromE src_exts) {
using To = std::layout_left::mapping<ToE>;
using From = std::layout_stride::mapping<FromE>;
std::array<typename FromE::index_type, FromE::rank()> strides;
if constexpr (FromE::rank() > 0) {
strides[0] = 1;
for (size_t r = 1; r < FromE::rank(); r++)
strides[r] = src_exts.extent(r - 1) * strides[r - 1];
}
From src(src_exts, strides);
ASSERT_NOEXCEPT(To(src));
To dest(src);
assert(dest == src);
if constexpr (implicit) {
To dest_implicit = src;
assert(dest_implicit == src);
} else {
assert((!std::is_convertible_v<From, To>));
}
}
template <class T1, class T2>
constexpr void test_conversion() {
constexpr size_t D = std::dynamic_extent;
// clang-format off
test_conversion<true, std::extents<T1>>(std::extents<T2>());
test_conversion<false, std::extents<T1, D>>(std::extents<T2, D>(5));
test_conversion<false, std::extents<T1, 5>>(std::extents<T2, D>(5));
test_conversion<false, std::extents<T1, 5>>(std::extents<T2, 5>());
test_conversion<false, std::extents<T1, 5, D>>(std::extents<T2, D, D>(5, 5));
test_conversion<false, std::extents<T1, D, D>>(std::extents<T2, D, D>(5, 5));
test_conversion<false, std::extents<T1, D, D>>(std::extents<T2, D, 7>(5));
test_conversion<false, std::extents<T1, 5, 7>>(std::extents<T2, 5, 7>());
test_conversion<false, std::extents<T1, 5, D, 8, D, D>>(std::extents<T2, D, D, 8, 9, 1>(5, 7));
test_conversion<false, std::extents<T1, D, D, D, D, D>>(
std::extents<T2, D, D, D, D, D>(5, 7, 8, 9, 1));
test_conversion<false, std::extents<T1, D, D, 8, 9, D>>(std::extents<T2, D, 7, 8, 9, 1>(5));
test_conversion<false, std::extents<T1, 5, 7, 8, 9, 1>>(std::extents<T2, 5, 7, 8, 9, 1>());
// clang-format on
}
template <class IdxT, size_t... Extents>
using ll_mapping_t = typename std::layout_left::template mapping<std::extents<IdxT, Extents...>>;
template <class IdxT, size_t... Extents>
using ls_mapping_t = typename std::layout_stride::template mapping<std::extents<IdxT, Extents...>>;
constexpr void test_rank_mismatch() {
constexpr size_t D = std::dynamic_extent;
static_assert(!std::is_constructible_v<ll_mapping_t<int, D>, ls_mapping_t<int>>);
static_assert(!std::is_constructible_v<ll_mapping_t<int>, ls_mapping_t<int, D, D>>);
static_assert(!std::is_constructible_v<ll_mapping_t<int, D>, ls_mapping_t<int, D, D>>);
static_assert(!std::is_constructible_v<ll_mapping_t<int, D, D, D>, ls_mapping_t<int, D, D>>);
}
constexpr void test_static_extent_mismatch() {
constexpr size_t D = std::dynamic_extent;
static_assert(!std::is_constructible_v<ll_mapping_t<int, D, 5>, ls_mapping_t<int, D, 4>>);
static_assert(!std::is_constructible_v<ll_mapping_t<int, 5>, ls_mapping_t<int, 4>>);
static_assert(!std::is_constructible_v<ll_mapping_t<int, 5, D>, ls_mapping_t<int, 4, D>>);
}
constexpr bool test() {
test_conversion<int, int>();
test_conversion<int, size_t>();
test_conversion<size_t, int>();
test_conversion<size_t, long>();
test_rank_mismatch();
test_static_extent_mismatch();
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,114 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// <mdspan>
// template<class OtherExtents>
// constexpr explicit(extents_type::rank() > 0)
// mapping(const layout_stride::mapping<OtherExtents>& other);
//
// Constraints: is_constructible_v<extents_type, OtherExtents> is true.
//
// Preconditions:
// - If extents_type::rank() > 0 is true, then for all r in the range [0, extents_type::rank()),
// other.stride(r) equals other.extents().fwd-prod-of-extents(r), and
// - other.required_span_size() is representable as a value of type index_type ([basic.fundamental]).
//
// Effects: Direct-non-list-initializes extents_ with other.extents().
#include <mdspan>
#include <type_traits>
#include <cassert>
#include <limits>
#include "test_macros.h"
template <bool implicit, class ToE, class FromE>
constexpr void test_conversion(FromE src_exts) {
using To = std::layout_left::mapping<ToE>;
using From = std::layout_stride::mapping<FromE>;
std::array<typename FromE::index_type, FromE::rank()> strides;
if constexpr (FromE::rank() > 0) {
strides[0] = 1;
for (size_t r = 1; r < FromE::rank(); r++)
strides[r] = src_exts.extent(r - 1) * strides[r - 1];
}
From src(src_exts, strides);
ASSERT_NOEXCEPT(To(src));
To dest(src);
assert(dest == src);
if constexpr (implicit) {
To dest_implicit = src;
assert(dest_implicit == src);
} else {
assert((!std::is_convertible_v<From, To>));
}
}
template <class T1, class T2>
constexpr void test_conversion() {
constexpr size_t D = std::dynamic_extent;
// clang-format off
test_conversion<true, std::extents<T1>>(std::extents<T2>());
test_conversion<false, std::extents<T1, D>>(std::extents<T2, D>(5));
test_conversion<false, std::extents<T1, 5>>(std::extents<T2, D>(5));
test_conversion<false, std::extents<T1, 5>>(std::extents<T2, 5>());
test_conversion<false, std::extents<T1, 5, D>>(std::extents<T2, D, D>(5, 5));
test_conversion<false, std::extents<T1, D, D>>(std::extents<T2, D, D>(5, 5));
test_conversion<false, std::extents<T1, D, D>>(std::extents<T2, D, 7>(5));
test_conversion<false, std::extents<T1, 5, 7>>(std::extents<T2, 5, 7>());
test_conversion<false, std::extents<T1, 5, D, 8, D, D>>(std::extents<T2, D, D, 8, 9, 1>(5, 7));
test_conversion<false, std::extents<T1, D, D, D, D, D>>(
std::extents<T2, D, D, D, D, D>(5, 7, 8, 9, 1));
test_conversion<false, std::extents<T1, D, D, 8, 9, D>>(std::extents<T2, D, 7, 8, 9, 1>(5));
test_conversion<false, std::extents<T1, 5, 7, 8, 9, 1>>(std::extents<T2, 5, 7, 8, 9, 1>());
// clang-format on
}
template <class IdxT, size_t... Extents>
using lr_mapping_t = typename std::layout_right::template mapping<std::extents<IdxT, Extents...>>;
template <class IdxT, size_t... Extents>
using ls_mapping_t = typename std::layout_stride::template mapping<std::extents<IdxT, Extents...>>;
constexpr void test_rank_mismatch() {
constexpr size_t D = std::dynamic_extent;
static_assert(!std::is_constructible_v<lr_mapping_t<int, D>, ls_mapping_t<int>>);
static_assert(!std::is_constructible_v<lr_mapping_t<int>, ls_mapping_t<int, D, D>>);
static_assert(!std::is_constructible_v<lr_mapping_t<int, D>, ls_mapping_t<int, D, D>>);
static_assert(!std::is_constructible_v<lr_mapping_t<int, D, D, D>, ls_mapping_t<int, D, D>>);
}
constexpr void test_static_extent_mismatch() {
constexpr size_t D = std::dynamic_extent;
static_assert(!std::is_constructible_v<lr_mapping_t<int, D, 5>, ls_mapping_t<int, D, 4>>);
static_assert(!std::is_constructible_v<lr_mapping_t<int, 5>, ls_mapping_t<int, 4>>);
static_assert(!std::is_constructible_v<lr_mapping_t<int, 5, D>, ls_mapping_t<int, 4, D>>);
}
constexpr bool test() {
test_conversion<int, int>();
test_conversion<int, size_t>();
test_conversion<size_t, int>();
test_conversion<size_t, long>();
test_rank_mismatch();
test_static_extent_mismatch();
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,198 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// <mdspan>
// template<class OtherMapping>
// friend constexpr bool operator==(const mapping& x, const OtherMapping& y) noexcept;
//
// Constraints:
// - layout-mapping-alike<OtherMapping> is satisfied.
// - rank_ == OtherMapping::extents_type::rank() is true.
// - OtherMapping::is_always_strided() is true.
//
// Preconditions: OtherMapping meets the layout mapping requirements ([mdspan.layout.policy.reqmts]).
//
// Returns: true if x.extents() == y.extents() is true, OFFSET(y) == 0 is true, and each of x.stride(r) == y.stride(r) is true for r in the range [0, x.extents().rank()). Otherwise, false.
#include <mdspan>
#include <type_traits>
#include <concepts>
#include <cassert>
#include "test_macros.h"
#include "../CustomTestLayouts.h"
template <class E1, class E2>
concept layout_mapping_comparable = requires(
E1 e1,
E2 e2,
std::array<typename E1::index_type, E1::rank()> s1,
std::array<typename E1::index_type, E1::rank()> s2) {
std::layout_stride::mapping<E1>(e1, s1) == std::layout_stride::mapping<E2>(e2, s2);
};
template <class T1, class T2>
constexpr void test_comparison_different_rank() {
constexpr size_t D = std::dynamic_extent;
// sanity check same rank
static_assert(layout_mapping_comparable<std::extents<T1, D>, std::extents<T2, D>>);
static_assert(layout_mapping_comparable<std::extents<T1, 5>, std::extents<T2, D>>);
static_assert(layout_mapping_comparable<std::extents<T1, D>, std::extents<T2, 5>>);
static_assert(layout_mapping_comparable<std::extents<T1, 5>, std::extents<T2, 5>>);
// not equality comparable when rank is not the same
static_assert(!layout_mapping_comparable<std::extents<T1>, std::extents<T2, D>>);
static_assert(!layout_mapping_comparable<std::extents<T1>, std::extents<T2, 1>>);
static_assert(!layout_mapping_comparable<std::extents<T1, D>, std::extents<T2>>);
static_assert(!layout_mapping_comparable<std::extents<T1, 1>, std::extents<T2>>);
static_assert(!layout_mapping_comparable<std::extents<T1, D>, std::extents<T2, D, D>>);
static_assert(!layout_mapping_comparable<std::extents<T1, 5>, std::extents<T2, 5, D>>);
static_assert(!layout_mapping_comparable<std::extents<T1, 5>, std::extents<T2, 5, 1>>);
static_assert(!layout_mapping_comparable<std::extents<T1, D, D>, std::extents<T2, D>>);
static_assert(!layout_mapping_comparable<std::extents<T1, 5, D>, std::extents<T2, 5>>);
static_assert(!layout_mapping_comparable<std::extents<T1, 5, 1>, std::extents<T2, 5>>);
}
template <class To, class From>
constexpr void test_comparison(
bool equal,
To dest_exts,
From src_exts,
std::array<int, To::rank()> dest_strides,
std::array<int, From::rank()> src_strides) {
std::layout_stride::mapping<To> dest(dest_exts, dest_strides);
std::layout_stride::mapping<From> src(src_exts, src_strides);
ASSERT_NOEXCEPT(dest == src);
assert((dest == src) == equal);
assert((dest != src) == !equal);
}
template <class T1, class T2>
constexpr void test_comparison_same_rank() {
constexpr size_t D = std::dynamic_extent;
test_comparison(true, std::extents<T1>(), std::extents<T2>(), std::array<int, 0>{}, std::array<int, 0>{});
test_comparison(true, std::extents<T1, D>(5), std::extents<T2, D>(5), std::array<int, 1>{1}, std::array<int, 1>{1});
test_comparison(true, std::extents<T1, D>(0), std::extents<T2, D>(0), std::array<int, 1>{1}, std::array<int, 1>{1});
test_comparison(true, std::extents<T1, 5>(), std::extents<T2, D>(5), std::array<int, 1>{3}, std::array<int, 1>{3});
test_comparison(true, std::extents<T1, D>(5), std::extents<T2, 5>(), std::array<int, 1>{1}, std::array<int, 1>{1});
test_comparison(true, std::extents<T1, 5>(), std::extents< T2, 5>(), std::array<int, 1>{1}, std::array<int, 1>{1});
test_comparison(false, std::extents<T1, 5>(), std::extents<T2, D>(5), std::array<int, 1>{2}, std::array<int, 1>{1});
test_comparison(false, std::extents<T1, D>(5), std::extents<T2, D>(5), std::array<int, 1>{2}, std::array<int, 1>{1});
test_comparison(false, std::extents<T1, D>(5), std::extents<T2, D>(7), std::array<int, 1>{1}, std::array<int, 1>{1});
test_comparison(false, std::extents<T1, 5>(), std::extents<T2, D>(7), std::array<int, 1>{1}, std::array<int, 1>{1});
test_comparison(false, std::extents<T1, D>(5), std::extents<T2, 7>(), std::array<int, 1>{1}, std::array<int, 1>{1});
test_comparison(false, std::extents<T1, 5>(), std::extents<T2, 7>(), std::array<int, 1>{1}, std::array<int, 1>{1});
test_comparison(
true,
std::extents<T1, D, D, D, D, D>(5, 6, 7, 8, 9),
std::extents<T2, D, D, D, D, D>(5, 6, 7, 8, 9),
std::array<int, 5>{2, 20, 200, 2000, 20000},
std::array<int, 5>{2, 20, 200, 2000, 20000});
test_comparison(
true,
std::extents<T1, D, 6, D, 8, D>(5, 7, 9),
std::extents<T2, 5, D, D, 8, 9>(6, 7),
std::array<int, 5>{2, 20, 200, 2000, 20000},
std::array<int, 5>{2, 20, 200, 2000, 20000});
test_comparison(
true,
std::extents<T1, 5, 6, 7, 8, 9>(5, 6, 7, 8, 9),
std::extents<T2, 5, 6, 7, 8, 9>(),
std::array<int, 5>{2, 20, 200, 2000, 20000},
std::array<int, 5>{2, 20, 200, 2000, 20000});
test_comparison(
false,
std::extents<T1, 5, 6, 7, 8, 9>(5, 6, 7, 8, 9),
std::extents<T2, 5, 6, 7, 8, 9>(),
std::array<int, 5>{2, 20, 200, 20000, 2000},
std::array<int, 5>{2, 20, 200, 2000, 20000});
test_comparison(
false,
std::extents<T1, D, D, D, D, D>(5, 6, 7, 8, 9),
std::extents<T2, D, D, D, D, D>(5, 6, 3, 8, 9),
std::array<int, 5>{2, 20, 200, 2000, 20000},
std::array<int, 5>{2, 20, 200, 2000, 20000});
test_comparison(
false,
std::extents<T1, D, 6, D, 8, D>(5, 7, 9),
std::extents<T2, 5, D, D, 3, 9>(6, 7),
std::array<int, 5>{2, 20, 200, 2000, 20000},
std::array<int, 5>{2, 20, 200, 2000, 20000});
test_comparison(
false,
std::extents<T1, 5, 6, 7, 8, 9>(5, 6, 7, 8, 9),
std::extents<T2, 5, 6, 7, 3, 9>(),
std::array<int, 5>{2, 20, 200, 2000, 20000},
std::array<int, 5>{2, 20, 200, 2000, 20000});
}
template <class OtherLayout, class E1, class E2, class... OtherArgs>
constexpr void test_comparison_with(
bool expect_equal, E1 e1, std::array<typename E1::index_type, E1::rank()> strides, E2 e2, OtherArgs... other_args) {
typename std::layout_stride::template mapping<E1> map(e1, strides);
typename OtherLayout::template mapping<E2> other_map(e2, other_args...);
assert((map == other_map) == expect_equal);
}
template <class OtherLayout>
constexpr void test_comparison_with() {
constexpr size_t D = std::dynamic_extent;
bool is_left_based =
std::is_same_v<OtherLayout, std::layout_left> || std::is_same_v<OtherLayout, always_convertible_layout>;
test_comparison_with<OtherLayout>(true, std::extents<int>(), std::array<int, 0>{}, std::extents<unsigned>());
test_comparison_with<OtherLayout>(true, std::extents<int, 5>(), std::array<int, 1>{1}, std::extents<unsigned, 5>());
test_comparison_with<OtherLayout>(true, std::extents<int, D>(5), std::array<int, 1>{1}, std::extents<unsigned, 5>());
test_comparison_with<OtherLayout>(false, std::extents<int, D>(5), std::array<int, 1>{2}, std::extents<unsigned, 5>());
test_comparison_with<OtherLayout>(
is_left_based, std::extents<int, D, D>(5, 7), std::array<int, 2>{1, 5}, std::extents<unsigned, D, D>(5, 7));
test_comparison_with<OtherLayout>(
!is_left_based, std::extents<int, D, D>(5, 7), std::array<int, 2>{7, 1}, std::extents<unsigned, D, D>(5, 7));
test_comparison_with<OtherLayout>(
false, std::extents<int, D, D>(5, 7), std::array<int, 2>{8, 1}, std::extents<unsigned, D, D>(5, 7));
if constexpr (std::is_same_v<OtherLayout, always_convertible_layout>) {
// test layout with strides not equal to product of extents
test_comparison_with<OtherLayout>(
true, std::extents<int, D, D>(5, 7), std::array<int, 2>{2, 10}, std::extents<unsigned, D, D>(5, 7), 0, 2);
// make sure that offset != 0 results in false
test_comparison_with<OtherLayout>(
false, std::extents<int, D, D>(5, 7), std::array<int, 2>{2, 10}, std::extents<unsigned, D, D>(5, 7), 1, 2);
}
}
template <class T1, class T2>
constexpr void test_comparison_index_type() {
test_comparison_same_rank<T1, T2>();
test_comparison_different_rank<T1, T2>();
test_comparison_with<std::layout_right>();
test_comparison_with<std::layout_left>();
test_comparison_with<always_convertible_layout>();
}
constexpr bool test() {
test_comparison_index_type<int, int>();
test_comparison_index_type<int, size_t>();
test_comparison_index_type<size_t, int>();
test_comparison_index_type<size_t, long>();
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,73 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// <mdspan>
// Test default construction:
//
// constexpr mapping() noexcept;
//
//
// Preconditions: layout_right::mapping<extents_type>().required_span_size() is representable as a value of type index_type ([basic.fundamental]).
//
// Effects: Direct-non-list-initializes extents_ with extents_type(), and for all d in the range [0, rank_),
// direct-non-list-initializes strides_[d] with layout_right::mapping<extents_type>().stride(d).
#include <mdspan>
#include <cassert>
#include <cstdint>
#include "test_macros.h"
template <class E>
constexpr void test_construction() {
using M = std::layout_stride::mapping<E>;
ASSERT_NOEXCEPT(M{});
M m;
E e;
// check correct extents are returned
ASSERT_NOEXCEPT(m.extents());
assert(m.extents() == e);
// check required_span_size()
typename E::index_type expected_size = 1;
for (typename E::rank_type r = 0; r < E::rank(); r++)
expected_size *= e.extent(r);
assert(m.required_span_size() == expected_size);
// check strides: node stride function is constrained on rank>0, e.extent(r) is not
auto strides = m.strides();
ASSERT_NOEXCEPT(m.strides());
if constexpr (E::rank() > 0) {
std::layout_right::mapping<E> m_right;
for (typename E::rank_type r = 0; r < E::rank(); r++) {
assert(m.stride(r) == m_right.stride(r));
assert(strides[r] == m.stride(r));
}
}
}
constexpr bool test() {
constexpr size_t D = std::dynamic_extent;
test_construction<std::extents<int>>();
test_construction<std::extents<unsigned, D>>();
test_construction<std::extents<unsigned, 7>>();
test_construction<std::extents<unsigned, 0>>();
test_construction<std::extents<unsigned, 7, 8>>();
test_construction<std::extents<int64_t, D, 8, D, D>>();
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,138 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// <mdspan>
// template<class OtherIndexType>
// constexpr mapping(const extents_type& e, array<OtherIndexType, rank_> s) noexcept;
//
// Constraints:
// - is_convertible_v<const OtherIndexType&, index_type> is true, and
// - is_nothrow_constructible_v<index_type, const OtherIndexType&> is true.
//
// Preconditions:
// - s[i] > 0 is true for all i in the range [0, rank_).
// - REQUIRED-SPAN-SIZE(e, s) is representable as a value of type index_type ([basic.fundamental]).
// - If rank_ is greater than 0, then there exists a permutation P of the integers in the range [0, rank_),
// such that s[pi] >= s[pi_1] * e.extent(pi_1) is true for all i in the range [1, rank_), where pi is the ith element of P.
// Note 1: For layout_stride, this condition is necessary and sufficient for is_unique() to be true.
//
// Effects: Direct-non-list-initializes extents_ with e, and for all d in the range [0, rank_),
// direct-non-list-initializes strides_[d] with as_const(s[d]).
#include <mdspan>
#include <cassert>
#include <cstdint>
#include "test_macros.h"
#include "../ConvertibleToIntegral.h"
template <class E, class S>
constexpr void test_construction(E e, S s) {
using M = std::layout_stride::mapping<E>;
ASSERT_NOEXCEPT(M{e, s});
M m(e, s);
// check correct extents are returned
ASSERT_NOEXCEPT(m.extents());
assert(m.extents() == e);
// check required_span_size()
typename E::index_type expected_size = 1;
for (typename E::rank_type r = 0; r < E::rank(); r++) {
if (e.extent(r) == 0) {
expected_size = 0;
break;
}
expected_size += (e.extent(r) - 1) * static_cast<typename E::index_type>(s[r]);
}
assert(m.required_span_size() == expected_size);
// check strides: node stride function is constrained on rank>0, e.extent(r) is not
auto strides = m.strides();
ASSERT_NOEXCEPT(m.strides());
if constexpr (E::rank() > 0) {
for (typename E::rank_type r = 0; r < E::rank(); r++) {
assert(m.stride(r) == static_cast<typename E::index_type>(s[r]));
assert(strides[r] == m.stride(r));
}
}
}
constexpr bool test() {
constexpr size_t D = std::dynamic_extent;
{
std::array<int, 0> s{};
test_construction(std::extents<int>(), s);
}
{
std::array<int, 1> s{1};
test_construction(std::extents<unsigned, D>(7), s);
}
{
std::array<int, 1> s{1};
test_construction(std::extents<unsigned, D>(0), s);
}
{
std::array<int, 1> s{2};
test_construction(std::extents<unsigned, 7>(), s);
}
{
std::array<IntType, 1> s{1};
test_construction(std::extents<int, D>(7), s);
}
{
std::array<int, 2> s{3, 30};
test_construction(std::extents<unsigned, 7, 8>(), s);
}
{
std::array<int, 4> s{20, 2, 200, 2000};
test_construction(std::extents<int64_t, D, 8, D, D>(7, 9, 10), s);
test_construction(std::extents<int64_t, D, 8, D, D>(0, 9, 10), s);
test_construction(std::extents<int64_t, D, 8, D, D>(0, 8, 0), s);
}
{
std::array<int, 4> s{200, 20, 20, 2000};
test_construction(std::extents<int64_t, D, D, D, D>(7, 0, 8, 9), s);
test_construction(std::extents<int64_t, D, D, D, D>(7, 8, 0, 9), s);
test_construction(std::extents<int64_t, D, D, D, D>(7, 1, 8, 9), s);
test_construction(std::extents<int64_t, D, D, D, D>(7, 8, 1, 9), s);
test_construction(std::extents<int64_t, D, D, D, D>(7, 1, 1, 9), s);
test_construction(std::extents<int64_t, D, D, D, D>(7, 0, 0, 9), s);
test_construction(std::extents<int64_t, D, D, D, D>(7, 1, 1, 9), s);
test_construction(std::extents<int64_t, D, D, D, D>(7, 1, 0, 9), s);
test_construction(std::extents<int64_t, D, D, D, D>(7, 0, 1, 9), s);
}
{
using mapping_t = std::layout_stride::mapping<std::dextents<unsigned, 2>>;
// wrong strides size
static_assert(!std::is_constructible_v<mapping_t, std::dextents<int, 2>, std::array<int, 3>>);
static_assert(!std::is_constructible_v<mapping_t, std::dextents<int, 2>, std::array<int, 1>>);
// wrong extents rank
static_assert(!std::is_constructible_v<mapping_t, std::dextents<int, 3>, std::array<int, 2>>);
// none-convertible strides
static_assert(!std::is_constructible_v<mapping_t, std::dextents<int, 2>, std::array<IntType, 2>>);
}
{
// not no-throw constructible index_type from stride
using mapping_t = std::layout_stride::mapping<std::dextents<unsigned char, 2>>;
static_assert(std::is_convertible_v<IntType, unsigned char>);
static_assert(!std::is_constructible_v<mapping_t, std::dextents<int, 2>, std::array<IntType, 2>>);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,141 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// <mdspan>
// template<class OtherIndexType>
// constexpr mapping(const extents_type& e, span<OtherIndexType, rank_> s) noexcept;
//
// Constraints:
// - is_convertible_v<const OtherIndexType&, index_type> is true, and
// - is_nothrow_constructible_v<index_type, const OtherIndexType&> is true.
//
// Preconditions:
// - s[i] > 0 is true for all i in the range [0, rank_).
// - REQUIRED-SPAN-SIZE(e, s) is representable as a value of type index_type ([basic.fundamental]).
// - If rank_ is greater than 0, then there exists a permutation P of the integers in the range [0, rank_),
// such that s[pi] >= s[pi_1] * e.extent(pi_1) is true for all i in the range [1, rank_), where pi is the ith element of P.
// Note 1: For layout_stride, this condition is necessary and sufficient for is_unique() to be true.
//
// Effects: Direct-non-list-initializes extents_ with e, and for all d in the range [0, rank_),
// direct-non-list-initializes strides_[d] with as_const(s[d]).
#include <mdspan>
#include <cassert>
#include <cstdint>
#include "test_macros.h"
#include "../ConvertibleToIntegral.h"
template <class E, class S>
constexpr void test_construction(E e, S s) {
using M = std::layout_stride::mapping<E>;
ASSERT_NOEXCEPT(M{e, s});
M m(e, s);
// check correct extents are returned
ASSERT_NOEXCEPT(m.extents());
assert(m.extents() == e);
// check required_span_size()
typename E::index_type expected_size = 1;
for (typename E::rank_type r = 0; r < E::rank(); r++) {
if (e.extent(r) == 0) {
expected_size = 0;
break;
}
expected_size += (e.extent(r) - 1) * static_cast<typename E::index_type>(s[r]);
}
assert(m.required_span_size() == expected_size);
// check strides: node stride function is constrained on rank>0, e.extent(r) is not
auto strides = m.strides();
ASSERT_NOEXCEPT(m.strides());
if constexpr (E::rank() > 0) {
for (typename E::rank_type r = 0; r < E::rank(); r++) {
assert(m.stride(r) == static_cast<typename E::index_type>(s[r]));
assert(strides[r] == m.stride(r));
}
}
}
constexpr bool test() {
constexpr size_t D = std::dynamic_extent;
{
std::array<int, 0> s{};
test_construction(std::extents<int>(), std::span(s));
}
{
std::array<int, 1> s{1};
test_construction(std::extents<unsigned, D>(7), std::span(s));
}
{
std::array<int, 1> s{1};
test_construction(std::extents<unsigned, D>(0), std::span(s));
}
{
std::array<int, 1> s{2};
test_construction(std::extents<unsigned, 7>(), std::span(s));
}
{
std::array<IntType, 1> s{1};
test_construction(std::extents<int, D>(7), std::span(s));
}
{
std::array<int, 2> s{3, 30};
test_construction(std::extents<unsigned, 7, 8>(), std::span(s));
}
{
std::array<int, 4> s{20, 2, 200, 2000};
test_construction(std::extents<int64_t, D, 8, D, D>(7, 9, 10), std::span(s));
}
{
std::array<int, 4> s{20, 2, 200, 2000};
test_construction(std::extents<int64_t, D, 8, D, D>(7, 0, 10), std::span(s));
test_construction(std::extents<int64_t, D, 8, D, D>(0, 9, 10), std::span(s));
test_construction(std::extents<int64_t, D, 8, D, D>(0, 8, 0), std::span(s));
}
{
std::array<int, 4> s{200, 20, 20, 2000};
test_construction(std::extents<int64_t, D, D, D, D>(7, 0, 8, 9), std::span(s));
test_construction(std::extents<int64_t, D, D, D, D>(7, 8, 0, 9), std::span(s));
test_construction(std::extents<int64_t, D, D, D, D>(7, 1, 8, 9), std::span(s));
test_construction(std::extents<int64_t, D, D, D, D>(7, 8, 1, 9), std::span(s));
test_construction(std::extents<int64_t, D, D, D, D>(7, 1, 1, 9), std::span(s));
test_construction(std::extents<int64_t, D, D, D, D>(7, 0, 0, 9), std::span(s));
test_construction(std::extents<int64_t, D, D, D, D>(7, 1, 1, 9), std::span(s));
test_construction(std::extents<int64_t, D, D, D, D>(7, 1, 0, 9), std::span(s));
test_construction(std::extents<int64_t, D, D, D, D>(7, 0, 1, 9), std::span(s));
}
{
using mapping_t = std::layout_stride::mapping<std::dextents<unsigned, 2>>;
// wrong strides size
static_assert(!std::is_constructible_v<mapping_t, std::dextents<int, 2>, std::span<int, 3>>);
static_assert(!std::is_constructible_v<mapping_t, std::dextents<int, 2>, std::span<int, 1>>);
// wrong extents rank
static_assert(!std::is_constructible_v<mapping_t, std::dextents<int, 3>, std::span<int, 2>>);
// none-convertible strides
static_assert(!std::is_constructible_v<mapping_t, std::dextents<int, 2>, std::span<IntType, 2>>);
}
{
// not no-throw constructible index_type from stride
using mapping_t = std::layout_stride::mapping<std::dextents<unsigned char, 2>>;
static_assert(std::is_convertible_v<IntType, unsigned char>);
static_assert(!std::is_constructible_v<mapping_t, std::dextents<int, 2>, std::span<IntType, 2>>);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,187 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// <mdspan>
// template<class StridedLayoutMapping>
// constexpr explicit(see below)
// mapping(const StridedLayoutMapping& other) noexcept;
//
// Constraints:
// - layout-mapping-alike<StridedLayoutMapping> is satisfied.
// - is_constructible_v<extents_type, typename StridedLayoutMapping::extents_type> is true.
// - StridedLayoutMapping::is_always_unique() is true.
// - StridedLayoutMapping::is_always_strided() is true.
//
// Preconditions:
// - StridedLayoutMapping meets the layout mapping requirements ([mdspan.layout.policy.reqmts]),
// - other.stride(r) > 0 is true for every rank index r of extents(),
// - other.required_span_size() is representable as a value of type index_type ([basic.fundamental]), and
// - OFFSET(other) == 0 is true.
//
// Effects: Direct-non-list-initializes extents_ with other.extents(), and for all d in the range [0, rank_),
// direct-non-list-initializes strides_[d] with other.stride(d).
//
// Remarks: The expression inside explicit is equivalent to:
// - !(is_convertible_v<typename StridedLayoutMapping::extents_type, extents_type> &&
// (is-mapping-of<layout_left, LayoutStrideMapping> ||
// is-mapping-of<layout_right, LayoutStrideMapping> ||
// is-mapping-of<layout_stride, LayoutStrideMapping>))
#include <mdspan>
#include <type_traits>
#include <cassert>
#include <limits>
#include "test_macros.h"
#include "../CustomTestLayouts.h"
template <bool implicit, class FromL, class ToE, class FromE>
constexpr void test_conversion(FromE src_exts) {
using To = std::layout_stride::mapping<ToE>;
using From = typename FromL::template mapping<FromE>;
From src([&]() {
if constexpr (std::is_same_v<FromL, std::layout_stride>) {
// just construct some strides which aren't layout_left/layout_right
std::array<size_t, FromE::rank()> strides;
size_t stride = 2;
for (size_t r = 0; r < FromE::rank(); r++) {
strides[r] = stride;
stride *= src_exts.extent(r);
}
return From(src_exts, strides);
} else {
return From(src_exts);
}
}());
ASSERT_NOEXCEPT(To(src));
To dest(src);
assert(dest == src);
if constexpr (implicit) {
To dest_implicit = src;
assert(dest_implicit == src);
} else {
assert((!std::is_convertible_v<From, To>));
}
}
template <class FromL, class T1, class T2>
constexpr void test_conversion() {
constexpr size_t D = std::dynamic_extent;
constexpr bool idx_convertible =
static_cast<size_t>(std::numeric_limits<T1>::max()) >= static_cast<size_t>(std::numeric_limits<T2>::max());
constexpr bool l_convertible =
std::is_same_v<FromL, std::layout_right> || std::is_same_v<FromL, std::layout_left> ||
std::is_same_v<FromL, std::layout_stride>;
constexpr bool idx_l_convertible = idx_convertible && l_convertible;
// clang-format off
// adding extents convertibility expectation
test_conversion<idx_l_convertible && true, FromL, std::extents<T1>>(std::extents<T2>());
test_conversion<idx_l_convertible && true, FromL, std::extents<T1, D>>(std::extents<T2, D>(0));
test_conversion<idx_l_convertible && true, FromL, std::extents<T1, D>>(std::extents<T2, D>(5));
test_conversion<idx_l_convertible && false, FromL, std::extents<T1, 5>>(std::extents<T2, D>(5));
test_conversion<idx_l_convertible && true, FromL, std::extents<T1, 5>>(std::extents<T2, 5>());
test_conversion<idx_l_convertible && false, FromL, std::extents<T1, 5, D>>(std::extents<T2, D, D>(5, 5));
test_conversion<idx_l_convertible && true, FromL, std::extents<T1, D, D>>(std::extents<T2, D, D>(5, 5));
test_conversion<idx_l_convertible && true, FromL, std::extents<T1, D, D>>(std::extents<T2, D, 7>(5));
test_conversion<idx_l_convertible && true, FromL, std::extents<T1, 5, 7>>(std::extents<T2, 5, 7>());
test_conversion<idx_l_convertible && false, FromL, std::extents<T1, 5, D, 8, D, D>>(std::extents<T2, D, D, 8, 9, 1>(5, 7));
test_conversion<idx_l_convertible && true, FromL, std::extents<T1, D, D, D, D, D>>(
std::extents<T2, D, D, D, D, D>(5, 7, 8, 9, 1));
test_conversion<idx_l_convertible && true, FromL, std::extents<T1, D, D, 8, 9, D>>(std::extents<T2, D, 7, 8, 9, 1>(5));
test_conversion<idx_l_convertible && true, FromL, std::extents<T1, 5, 7, 8, 9, 1>>(std::extents<T2, 5, 7, 8, 9, 1>());
// clang-format on
}
template <class IdxT, size_t... Extents>
using ToM = typename std::layout_stride::template mapping<std::extents<IdxT, Extents...>>;
template <class FromL, class IdxT, size_t... Extents>
using FromM = typename FromL::template mapping<std::extents<IdxT, Extents...>>;
template <class FromL>
constexpr void test_no_implicit_conversion() {
constexpr size_t D = std::dynamic_extent;
// Sanity check that one static to dynamic conversion works
static_assert(std::is_constructible_v<ToM<int, D>, FromM<FromL, int, 5>>);
static_assert(std::is_convertible_v<FromM<FromL, int, 5>, ToM<int, D>>);
// Check that dynamic to static conversion only works explicitly
static_assert(std::is_constructible_v<ToM<int, 5>, FromM<FromL, int, D>>);
static_assert(!std::is_convertible_v<FromM<FromL, int, D>, ToM<int, 5>>);
// Sanity check that one static to dynamic conversion works
static_assert(std::is_constructible_v<ToM<int, D, 7>, FromM<FromL, int, 5, 7>>);
static_assert(std::is_convertible_v<FromM<FromL, int, 5, 7>, ToM<int, D, 7>>);
// Check that dynamic to static conversion only works explicitly
static_assert(std::is_constructible_v<ToM<int, 5, 7>, FromM<FromL, int, D, 7>>);
static_assert(!std::is_convertible_v<FromM<FromL, int, D, 7>, ToM<int, 5, 7>>);
// Sanity check that smaller index_type to larger index_type conversion works
static_assert(std::is_constructible_v<ToM<size_t, 5>, FromM<FromL, int, 5>>);
static_assert(std::is_convertible_v<FromM<FromL, int, 5>, ToM<size_t, 5>>);
// Check that larger index_type to smaller index_type conversion works explicitly only
static_assert(std::is_constructible_v<ToM<int, 5>, FromM<FromL, size_t, 5>>);
static_assert(!std::is_convertible_v<FromM<FromL, size_t, 5>, ToM<int, 5>>);
}
template <class FromL>
constexpr void test_rank_mismatch() {
constexpr size_t D = std::dynamic_extent;
static_assert(!std::is_constructible_v<ToM<int, D>, FromM<FromL, int>>);
static_assert(!std::is_constructible_v<ToM<int>, FromM<FromL, int, D, D>>);
static_assert(!std::is_constructible_v<ToM<int, D>, FromM<FromL, int, D, D>>);
static_assert(!std::is_constructible_v<ToM<int, D, D, D>, FromM<FromL, int, D, D>>);
}
template <class FromL>
constexpr void test_static_extent_mismatch() {
constexpr size_t D = std::dynamic_extent;
static_assert(!std::is_constructible_v<ToM<int, D, 5>, FromM<FromL, int, D, 4>>);
static_assert(!std::is_constructible_v<ToM<int, 5>, FromM<FromL, int, 4>>);
static_assert(!std::is_constructible_v<ToM<int, 5, D>, FromM<FromL, int, 4, D>>);
}
template <class FromL>
constexpr void test_layout() {
test_conversion<FromL, int, int>();
test_conversion<FromL, int, size_t>();
test_conversion<FromL, size_t, int>();
test_conversion<FromL, size_t, long>();
// the implicit convertibility test doesn't apply to non std::layouts
if constexpr (!std::is_same_v<FromL, always_convertible_layout>)
test_no_implicit_conversion<FromL>();
test_rank_mismatch<FromL>();
test_static_extent_mismatch<FromL>();
}
constexpr bool test() {
test_layout<std::layout_right>();
test_layout<std::layout_left>();
test_layout<std::layout_stride>();
test_layout<always_convertible_layout>();
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,55 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// ADDITIONAL_COMPILE_FLAGS: -Wno-ctad-maybe-unsupported
// <mdspan>
#include <mdspan>
#include <type_traits>
#include <concepts>
#include <cassert>
#include "test_macros.h"
// mdspan
// layout_stride::mapping does not have explicit deduction guides,
// but implicit deduction guides for constructor taking extents and strides
// should work
constexpr bool test() {
constexpr size_t D = std::dynamic_extent;
ASSERT_SAME_TYPE(decltype(std::layout_stride::mapping(std::extents<int>(), std::array<unsigned, 0>())),
std::layout_stride::template mapping<std::extents<int>>);
ASSERT_SAME_TYPE(decltype(std::layout_stride::mapping(std::extents<int, 4>(), std::array<char, 1>{1})),
std::layout_stride::template mapping<std::extents<int, 4>>);
ASSERT_SAME_TYPE(decltype(std::layout_stride::mapping(std::extents<int, D>(), std::array<char, 1>{1})),
std::layout_stride::template mapping<std::extents<int, D>>);
ASSERT_SAME_TYPE(
decltype(std::layout_stride::mapping(std::extents<unsigned, D, 3>(), std::array<int64_t, 2>{3, 100})),
std::layout_stride::template mapping<std::extents<unsigned, D, 3>>);
ASSERT_SAME_TYPE(decltype(std::layout_stride::mapping(std::extents<int>(), std::span<unsigned, 0>())),
std::layout_stride::template mapping<std::extents<int>>);
ASSERT_SAME_TYPE(decltype(std::layout_stride::mapping(std::extents<int, 4>(), std::declval<std::span<char, 1>>())),
std::layout_stride::template mapping<std::extents<int, 4>>);
ASSERT_SAME_TYPE(decltype(std::layout_stride::mapping(std::extents<int, D>(), std::declval<std::span<char, 1>>())),
std::layout_stride::template mapping<std::extents<int, D>>);
ASSERT_SAME_TYPE(
decltype(std::layout_stride::mapping(std::extents<unsigned, D, 3>(), std::declval<std::span<int64_t, 2>>())),
std::layout_stride::template mapping<std::extents<unsigned, D, 3>>);
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,33 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// <mdspan>
// template<class Extents>
// class layout_stride::mapping;
// If Extents is not a specialization of extents, then the program is
// ill-formed.
// Mandates: If Extents::rank_dynamic() == 0 is true, then the size of the
// multidimensional index space Extents() is representable as a value of type
// typename Extents::index_type.
#include <mdspan>
void not_extents() {
// expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}layout_stride::mapping template argument must be a specialization of extents}}
[[maybe_unused]] std::layout_stride::mapping<void> mapping;
}
void representable() {
// expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}layout_stride::mapping product of static extents must be representable as index_type.}}
[[maybe_unused]] std::layout_stride::mapping<std::extents<char, 20, 20>> mapping;
}

View File

@ -0,0 +1,121 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// <mdspan>
// Test default iteration:
//
// template<class... Indices>
// constexpr index_type operator()(Indices...) const noexcept;
//
// Constraints:
// * sizeof...(Indices) == extents_type::rank() is true,
// * (is_convertible_v<Indices, index_type> && ...) is true, and
// * (is_nothrow_constructible_v<index_type, Indices> && ...) is true.
//
// Preconditions:
// * extents_type::index-cast(i) is a multidimensional index in extents_.
#include <mdspan>
#include <cassert>
#include <cstdint>
#include "test_macros.h"
#include "../ConvertibleToIntegral.h"
template <class Mapping, class... Indices>
concept operator_constraints = requires(Mapping m, Indices... idxs) {
{ std::is_same_v<decltype(m(idxs...)), typename Mapping::index_type> };
};
template <class Mapping, class... Indices>
requires(operator_constraints<Mapping, Indices...>)
constexpr bool check_operator_constraints(Mapping m, Indices... idxs) {
(void)m(idxs...);
return true;
}
template <class Mapping, class... Indices>
constexpr bool check_operator_constraints(Mapping, Indices...) {
return false;
}
template <class M, class... Args>
constexpr void iterate_stride(M m, const std::array<int, M::extents_type::rank()>& strides, Args... args) {
constexpr int r = static_cast<int>(M::extents_type::rank()) - 1 - static_cast<int>(sizeof...(Args));
if constexpr (-1 == r) {
ASSERT_NOEXCEPT(m(args...));
size_t expected_val = [&]<size_t... Pos>(std::index_sequence<Pos...>) {
return ((args * strides[Pos]) + ... + 0);
}(std::make_index_sequence<M::extents_type::rank()>());
assert(expected_val == static_cast<size_t>(m(args...)));
} else {
for (typename M::index_type i = 0; i < m.extents().extent(r); i++) {
iterate_stride(m, strides, i, args...);
}
}
}
template <class E, class... Args>
constexpr void test_iteration(std::array<int, E::rank()> strides, Args... args) {
using M = std::layout_stride::mapping<E>;
M m(E(args...), strides);
iterate_stride(m, strides);
}
constexpr bool test() {
constexpr size_t D = std::dynamic_extent;
test_iteration<std::extents<int>>(std::array<int, 0>{});
test_iteration<std::extents<unsigned, D>>(std::array<int, 1>{2}, 1);
test_iteration<std::extents<unsigned, D>>(std::array<int, 1>{3}, 7);
test_iteration<std::extents<unsigned, 7>>(std::array<int, 1>{4});
test_iteration<std::extents<unsigned, 7, 8>>(std::array<int, 2>{25, 3});
test_iteration<std::extents<char, D, D, D, D>>(std::array<int, 4>{1, 1, 1, 1}, 1, 1, 1, 1);
// Check operator constraint for number of arguments
static_assert(check_operator_constraints(
std::layout_stride::mapping<std::extents<int, D>>(std::extents<int, D>(1), std::array{1}), 0));
static_assert(!check_operator_constraints(
std::layout_stride::mapping<std::extents<int, D>>(std::extents<int, D>(1), std::array{1}), 0, 0));
// Check operator constraint for convertibility of arguments to index_type
static_assert(check_operator_constraints(
std::layout_stride::mapping<std::extents<int, D>>(std::extents<int, D>(1), std::array{1}), IntType(0)));
static_assert(!check_operator_constraints(
std::layout_stride::mapping<std::extents<unsigned, D>>(std::extents<unsigned, D>(1), std::array{1}), IntType(0)));
// Check operator constraint for no-throw-constructibility of index_type from arguments
static_assert(!check_operator_constraints(
std::layout_stride::mapping<std::extents<unsigned char, D>>(std::extents<unsigned char, D>(1), std::array{1}),
IntType(0)));
return true;
}
constexpr bool test_large() {
constexpr size_t D = std::dynamic_extent;
test_iteration<std::extents<int64_t, D, 8, D, D>>(std::array<int, 4>{2000, 2, 20, 200}, 7, 9, 10);
test_iteration<std::extents<int64_t, D, 8, 1, D>>(std::array<int, 4>{2000, 20, 20, 200}, 7, 10);
return true;
}
int main(int, char**) {
test();
static_assert(test());
// The large test iterates over ~10k loop indices.
// With assertions enabled this triggered the maximum default limit
// for steps in consteval expressions. Assertions roughly double the
// total number of instructions, so this was already close to the maximum.
test_large();
return 0;
}

View File

@ -0,0 +1,54 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// <mdspan>
// constexpr bool is_exhaustive() const noexcept;
//
// Returns:
// - true if rank_ is 0.
// - Otherwise, true if there is a permutation P of the integers in the range [0, rank_) such that
// stride(p0) equals 1, and stride(pi) equals stride(pi_1) * extents().extent(pi_1) for i in the
// range [1, rank_), where pi is the ith element of P.
// - Otherwise, false.
#include <mdspan>
#include <type_traits>
#include <concepts>
#include <cassert>
#include "test_macros.h"
template <class E>
constexpr void
test_layout_mapping_stride(E ext, std::array<typename E::index_type, E::rank()> strides, bool exhaustive) {
using M = std::layout_stride::template mapping<E>;
M m(ext, strides);
assert(m.is_exhaustive() == exhaustive);
}
constexpr bool test() {
constexpr size_t D = std::dynamic_extent;
test_layout_mapping_stride(std::extents<int, 0>(), std::array<int, 1>{1}, true);
test_layout_mapping_stride(std::extents<unsigned, D>(0), std::array<unsigned, 1>{3}, false);
test_layout_mapping_stride(std::extents<int, 0, 3>(), std::array<int, 2>{6, 2}, true);
test_layout_mapping_stride(std::extents<int, D, D>(3, 0), std::array<int, 2>{6, 2}, false);
test_layout_mapping_stride(std::extents<int, D, D>(0, 0), std::array<int, 2>{6, 2}, false);
test_layout_mapping_stride(
std::extents<unsigned, D, D, D, D>(3, 3, 0, 3), std::array<unsigned, 4>{3, 1, 27, 9}, true);
test_layout_mapping_stride(std::extents<int, D, D, D, D>(0, 3, 3, 3), std::array<int, 4>{3, 1, 27, 9}, false);
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,116 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// <mdspan>
// namespace std {
// template<class Extents>
// class layout_stride::mapping {
//
// ...
// static constexpr bool is_always_unique() noexcept { return true; }
// static constexpr bool is_always_exhaustive() noexcept { return false; }
// static constexpr bool is_always_strided() noexcept { return true; }
//
// static constexpr bool is_unique() noexcept { return true; }
// static constexpr bool is_exhaustive() noexcept;
// static constexpr bool is_strided() noexcept { return true; }
// ...
// };
// }
//
//
// layout_stride::mapping<E> is a trivially copyable type that models regular for each E.
//
// constexpr bool is_exhaustive() const noexcept;
//
// Returns:
// - true if rank_ is 0.
// - Otherwise, true if there is a permutation P of the integers in the range [0, rank_) such that
// stride(p0) equals 1, and stride(pi) equals stride(pi_1) * extents().extent(pi_1) for i in the
// range [1, rank_), where pi is the ith element of P.
// - Otherwise, false.
#include <mdspan>
#include <type_traits>
#include <concepts>
#include <cassert>
#include "test_macros.h"
template <class E>
constexpr void
test_layout_mapping_stride(E ext, std::array<typename E::index_type, E::rank()> strides, bool exhaustive) {
using M = std::layout_stride::template mapping<E>;
M m(ext, strides);
const M c_m = m;
assert(m.strides() == strides);
assert(c_m.strides() == strides);
assert(m.extents() == ext);
assert(c_m.extents() == ext);
assert(M::is_unique() == true);
assert(m.is_exhaustive() == exhaustive);
assert(c_m.is_exhaustive() == exhaustive);
assert(M::is_strided() == true);
assert(M::is_always_unique() == true);
assert(M::is_always_exhaustive() == false);
assert(M::is_always_strided() == true);
ASSERT_NOEXCEPT(m.strides());
ASSERT_NOEXCEPT(c_m.strides());
ASSERT_NOEXCEPT(m.extents());
ASSERT_NOEXCEPT(c_m.extents());
ASSERT_NOEXCEPT(M::is_unique());
ASSERT_NOEXCEPT(m.is_exhaustive());
ASSERT_NOEXCEPT(c_m.is_exhaustive());
ASSERT_NOEXCEPT(M::is_strided());
ASSERT_NOEXCEPT(M::is_always_unique());
ASSERT_NOEXCEPT(M::is_always_exhaustive());
ASSERT_NOEXCEPT(M::is_always_strided());
for (typename E::rank_type r = 0; r < E::rank(); r++) {
assert(m.stride(r) == strides[r]);
assert(c_m.stride(r) == strides[r]);
ASSERT_NOEXCEPT(m.stride(r));
ASSERT_NOEXCEPT(c_m.stride(r));
}
typename E::index_type expected_size = 1;
for (typename E::rank_type r = 0; r < E::rank(); r++) {
if (ext.extent(r) == 0) {
expected_size = 0;
break;
}
expected_size += (ext.extent(r) - 1) * static_cast<typename E::index_type>(strides[r]);
}
assert(m.required_span_size() == expected_size);
assert(c_m.required_span_size() == expected_size);
ASSERT_NOEXCEPT(m.required_span_size());
ASSERT_NOEXCEPT(c_m.required_span_size());
static_assert(std::is_trivially_copyable_v<M>);
static_assert(std::regular<M>);
}
constexpr bool test() {
constexpr size_t D = std::dynamic_extent;
test_layout_mapping_stride(std::extents<int>(), std::array<int, 0>{}, true);
test_layout_mapping_stride(std::extents<char, 4, 5>(), std::array<char, 2>{1, 4}, true);
test_layout_mapping_stride(std::extents<char, 4, 5>(), std::array<char, 2>{1, 5}, false);
test_layout_mapping_stride(std::extents<unsigned, D, 4>(7), std::array<unsigned, 2>{20, 2}, false);
test_layout_mapping_stride(std::extents<size_t, D, D, D, D>(3, 3, 3, 3), std::array<size_t, 4>{3, 1, 9, 27}, true);
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,56 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// <mdspan>
// Let REQUIRED-SPAN-SIZE(e, strides) be:
// - 1, if e.rank() == 0 is true,
// - otherwise 0, if the size of the multidimensional index space e is 0,
// - otherwise 1 plus the sum of products of (e.extent(r) - 1) and strides[r] for all r in the range [0, e.rank()).
// constexpr index_type required_span_size() const noexcept;
//
// Returns: REQUIRED-SPAN-SIZE(extents(), strides_).
#include <mdspan>
#include <cassert>
#include <cstdint>
#include "test_macros.h"
template <class E>
constexpr void test_required_span_size(E e, std::array<int, E::rank()> strides, typename E::index_type expected_size) {
using M = std::layout_stride::mapping<E>;
const M m(e, strides);
ASSERT_NOEXCEPT(m.required_span_size());
assert(m.required_span_size() == expected_size);
}
constexpr bool test() {
constexpr size_t D = std::dynamic_extent;
test_required_span_size(std::extents<int>(), std::array<int, 0>{}, 1);
test_required_span_size(std::extents<unsigned, D>(0), std::array<int, 1>{5}, 0);
test_required_span_size(std::extents<unsigned, D>(1), std::array<int, 1>{5}, 1);
test_required_span_size(std::extents<unsigned, D>(7), std::array<int, 1>{5}, 31);
test_required_span_size(std::extents<unsigned, 7>(), std::array<int, 1>{5}, 31);
test_required_span_size(std::extents<unsigned, 7, 8>(), std::array<int, 2>{20, 2}, 135);
test_required_span_size(
std::extents<int64_t, D, 8, D, D>(7, 9, 10), std::array<int, 4>{1, 7, 7 * 8, 7 * 8 * 9}, 5040);
test_required_span_size(std::extents<int64_t, 1, 8, D, D>(9, 10), std::array<int, 4>{1, 7, 7 * 8, 7 * 8 * 9}, 5034);
test_required_span_size(std::extents<int64_t, 1, 0, D, D>(9, 10), std::array<int, 4>{1, 7, 7 * 8, 7 * 8 * 9}, 0);
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,131 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// <mdspan>
// A type M meets the layout mapping requirements if
// - M models copyable and equality_comparable,
// - is_nothrow_move_constructible_v<M> is true,
// - is_nothrow_move_assignable_v<M> is true,
// - is_nothrow_swappable_v<M> is true, and
//
// the following types and expressions are well-formed and have the specified semantics.
//
// typename M::extents_type
// Result: A type that is a specialization of extents.
//
// typename M::index_type
// Result: typename M::extents_type::index_type.
//
// typename M::rank_type
// Result: typename M::extents_type::rank_type.
//
// typename M::layout_type
// Result: A type MP that meets the layout mapping policy requirements ([mdspan.layout.policy.reqmts]) and for which is-mapping-of<MP, M> is true.
//
// m.extents()
// Result: const typename M::extents_type&
//
// m(i...)
// Result: typename M::index_type
// Returns: A nonnegative integer less than numeric_limits<typename M::index_type>::max() and less than or equal to numeric_limits<size_t>::max().
//
// m(i...) == m(static_cast<typename M::index_type>(i)...)
// Result: bool
// Returns: true
//
// m.required_span_size()
// Result: typename M::index_type
// Returns: If the size of the multidimensional index space m.extents() is 0, then 0, else 1 plus the maximum value of m(i...) for all i.
//
// m.is_unique()
// Result: bool
// Returns: true only if for every i and j where (i != j || ...) is true, m(i...) != m(j...) is true.
//
// m.is_exhaustive()
// Result: bool
// Returns: true only if for all k in the range [0, m.required_span_size()) there exists an i such that m(i...) equals k.
//
// m.is_strided()
// Result: bool
// Returns: true only if for every rank index r of m.extents() there exists an integer
// sr such that, for all i where (i+dr) is a multidimensional index in m.extents() ([mdspan.overview]),
// m((i + dr)...) - m(i...) equals sr
//
// m.stride(r)
// Preconditions: m.is_strided() is true.
// Result: typename M::index_type
// Returns: sr as defined in m.is_strided() above.
//
// M::is_always_unique()
// Result: A constant expression ([expr.const]) of type bool.
// Returns: true only if m.is_unique() is true for all possible objects m of type M.
//
// M::is_always_exhaustive()
// Result: A constant expression ([expr.const]) of type bool.
// Returns: true only if m.is_exhaustive() is true for all possible objects m of type M.
//
// M::is_always_strided()
// Result: A constant expression ([expr.const]) of type bool.
// Returns: true only if m.is_strided() is true for all possible objects m of type M.
#include <mdspan>
#include <type_traits>
#include <concepts>
#include <cassert>
#include "test_macros.h"
// Common requirements of all layout mappings
template <class M, size_t... Idxs>
void test_mapping_requirements(std::index_sequence<Idxs...>) {
using E = typename M::extents_type;
static_assert(std::__mdspan_detail::__is_extents_v<E>);
static_assert(std::is_copy_constructible_v<M>);
static_assert(std::is_nothrow_move_constructible_v<M>);
static_assert(std::is_nothrow_move_assignable_v<M>);
static_assert(std::is_nothrow_swappable_v<M>);
ASSERT_SAME_TYPE(typename M::index_type, typename E::index_type);
ASSERT_SAME_TYPE(typename M::size_type, typename E::size_type);
ASSERT_SAME_TYPE(typename M::rank_type, typename E::rank_type);
ASSERT_SAME_TYPE(typename M::layout_type, std::layout_stride);
ASSERT_SAME_TYPE(typename M::layout_type::template mapping<E>, M);
static_assert(std::is_same_v<decltype(std::declval<M>().extents()), const E&>);
static_assert(std::is_same_v<decltype(std::declval<M>().strides()), std::array<typename M::index_type, E::rank()>>);
static_assert(std::is_same_v<decltype(std::declval<M>()(Idxs...)), typename M::index_type>);
static_assert(std::is_same_v<decltype(std::declval<M>().required_span_size()), typename M::index_type>);
static_assert(std::is_same_v<decltype(std::declval<M>().is_unique()), bool>);
static_assert(std::is_same_v<decltype(std::declval<M>().is_exhaustive()), bool>);
static_assert(std::is_same_v<decltype(std::declval<M>().is_strided()), bool>);
static_assert(std::is_same_v<decltype(std::declval<M>().stride(0)), typename M::index_type>);
static_assert(std::is_same_v<decltype(M::is_always_unique()), bool>);
static_assert(std::is_same_v<decltype(M::is_always_exhaustive()), bool>);
static_assert(std::is_same_v<decltype(M::is_always_strided()), bool>);
}
template <class L, class E>
void test_layout_mapping_requirements() {
using M = typename L::template mapping<E>;
test_mapping_requirements<M>(std::make_index_sequence<E::rank()>());
}
template <class E>
void test_layout_mapping_stride() {
test_layout_mapping_requirements<std::layout_stride, E>();
}
int main(int, char**) {
constexpr size_t D = std::dynamic_extent;
test_layout_mapping_stride<std::extents<int>>();
test_layout_mapping_stride<std::extents<char, 4, 5>>();
test_layout_mapping_stride<std::extents<unsigned, D, 4>>();
test_layout_mapping_stride<std::extents<size_t, D, D, D, D>>();
return 0;
}

View File

@ -0,0 +1,57 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// <mdspan>
// constexpr index_type stride(rank_type i) const noexcept;
//
// Constraints: extents_type::rank() > 0 is true.
//
// Preconditions: i < extents_type::rank() is true.
//
// Returns: extents().rev-prod-of-extents(i).
#include <mdspan>
#include <cassert>
#include <array>
#include <cstdint>
#include <cstdio>
#include "test_macros.h"
template <class E, class... Args>
constexpr void test_stride(std::array<typename E::index_type, E::rank()> strides, Args... args) {
using M = std::layout_stride::mapping<E>;
M m(E(args...), strides);
ASSERT_NOEXCEPT(m.stride(0));
for (size_t r = 0; r < E::rank(); r++)
assert(strides[r] == m.stride(r));
ASSERT_NOEXCEPT(m.strides());
auto strides_out = m.strides();
static_assert(std::is_same_v<decltype(strides_out), std::array<typename E::index_type, E::rank()>>);
for (size_t r = 0; r < E::rank(); r++)
assert(strides[r] == strides_out[r]);
}
constexpr bool test() {
constexpr size_t D = std::dynamic_extent;
test_stride<std::extents<unsigned, D>>(std::array<unsigned, 1>{1}, 7);
test_stride<std::extents<unsigned, 7>>(std::array<unsigned, 1>{1});
test_stride<std::extents<unsigned, 7, 8>>(std::array<unsigned, 2>{8, 1});
test_stride<std::extents<int64_t, D, 8, D, D>>(std::array<int64_t, 4>{720, 90, 10, 1}, 7, 9, 10);
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -19,7 +19,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
#include "CustomTestLayouts.h"
#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <class H, class M, class A>

View File

@ -43,7 +43,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
#include "CustomTestLayouts.h"
#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <class ToMDS, class FromMDS>

View File

@ -37,7 +37,7 @@
#include <mdspan>
#include "CustomTestAccessors.h"
#include "CustomTestLayouts.h"
#include "../CustomTestLayouts.h"
void cant_construct_data_handle_type() {
int data;

View File

@ -21,7 +21,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
#include "CustomTestLayouts.h"
#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <class H, class M, class A>

View File

@ -29,7 +29,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
#include "CustomTestLayouts.h"
#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <bool hc, bool mc, bool ac, class H, class M, class A>

View File

@ -38,7 +38,7 @@
#include "../ConvertibleToIntegral.h"
#include "../MinimalElementType.h"
#include "CustomTestLayouts.h"
#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <class Extents, size_t... Idxs>

View File

@ -31,7 +31,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
#include "CustomTestLayouts.h"
#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <bool mec, bool ac, class H, class M, class A>

View File

@ -38,7 +38,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
#include "CustomTestLayouts.h"
#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <class MDS, class... Args>

View File

@ -29,7 +29,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
#include "CustomTestLayouts.h"
#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <bool ac, class H, class M, class A>

View File

@ -26,7 +26,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
#include "CustomTestLayouts.h"
#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <class H, class M, class A>

View File

@ -38,7 +38,7 @@
#include "../ConvertibleToIntegral.h"
#include "../MinimalElementType.h"
#include "CustomTestLayouts.h"
#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <class Extents, size_t... Idxs>

View File

@ -21,7 +21,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
#include "CustomTestLayouts.h"
#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <class H, class M, class A>

View File

@ -55,7 +55,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
#include "CustomTestLayouts.h"
#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <class H, class M, class A>

View File

@ -36,7 +36,7 @@
#include "test_macros.h"
#include "../ConvertibleToIntegral.h"
#include "CustomTestLayouts.h"
#include "../CustomTestLayouts.h"
// Clang 16 does not support argument packs as input to operator []
#if defined(__clang_major__) && __clang_major__ < 17

View File

@ -21,7 +21,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
#include "CustomTestLayouts.h"
#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <class H, class M, class A>

View File

@ -58,7 +58,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
#include "CustomTestLayouts.h"
#include "../CustomTestLayouts.h"
template <class H, class M, class A>
constexpr void test_mdspan_types(const H& handle, const M& map, const A& acc) {

View File

@ -24,7 +24,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
#include "CustomTestLayouts.h"
#include "../CustomTestLayouts.h"
template <class MDS>
constexpr void test_swap(MDS a, MDS b) {

View File

@ -36,7 +36,7 @@
#include "../MinimalElementType.h"
#include "CustomTestAccessors.h"
#include "CustomTestLayouts.h"
#include "../CustomTestLayouts.h"
// Calculated expected size of an mdspan
// Note this expectes that only default_accessor is empty

View File

@ -65,17 +65,11 @@
#elif TEST_STD_VER == 23
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_mdspan
# error "__cpp_lib_mdspan should be defined in c++23"
# endif
# if __cpp_lib_mdspan != 202207L
# error "__cpp_lib_mdspan should have the value 202207L in c++23"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_mdspan
# error "__cpp_lib_mdspan should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_mdspan
# error "__cpp_lib_mdspan should be defined in c++23"
# endif
# if __cpp_lib_mdspan != 202207L
# error "__cpp_lib_mdspan should have the value 202207L in c++23"
# endif
# ifdef __cpp_lib_submdspan
@ -84,17 +78,11 @@
#elif TEST_STD_VER > 23
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_mdspan
# error "__cpp_lib_mdspan should be defined in c++26"
# endif
# if __cpp_lib_mdspan != 202207L
# error "__cpp_lib_mdspan should have the value 202207L in c++26"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_mdspan
# error "__cpp_lib_mdspan should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_mdspan
# error "__cpp_lib_mdspan should be defined in c++26"
# endif
# if __cpp_lib_mdspan != 202207L
# error "__cpp_lib_mdspan should have the value 202207L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)

View File

@ -4804,17 +4804,11 @@
# endif
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_mdspan
# error "__cpp_lib_mdspan should be defined in c++23"
# endif
# if __cpp_lib_mdspan != 202207L
# error "__cpp_lib_mdspan should have the value 202207L in c++23"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_mdspan
# error "__cpp_lib_mdspan should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_mdspan
# error "__cpp_lib_mdspan should be defined in c++23"
# endif
# if __cpp_lib_mdspan != 202207L
# error "__cpp_lib_mdspan should have the value 202207L in c++23"
# endif
# if !defined(_LIBCPP_AVAILABILITY_HAS_NO_PMR)
@ -6345,17 +6339,11 @@
# endif
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_mdspan
# error "__cpp_lib_mdspan should be defined in c++26"
# endif
# if __cpp_lib_mdspan != 202207L
# error "__cpp_lib_mdspan should have the value 202207L in c++26"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_mdspan
# error "__cpp_lib_mdspan should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_mdspan
# error "__cpp_lib_mdspan should be defined in c++26"
# endif
# if __cpp_lib_mdspan != 202207L
# error "__cpp_lib_mdspan should have the value 202207L in c++26"
# endif
# if !defined(_LIBCPP_AVAILABILITY_HAS_NO_PMR)

View File

@ -2654,7 +2654,6 @@ libcxx/test/std/containers/views/mdspan/layout_left/index_operator.pass.cpp
libcxx/test/std/containers/views/mdspan/layout_left/required_span_size.pass.cpp
libcxx/test/std/containers/views/mdspan/layout_right/index_operator.pass.cpp
libcxx/test/std/containers/views/mdspan/layout_right/required_span_size.pass.cpp
libcxx/test/std/containers/views/mdspan/mdspan/CustomTestLayouts.h
libcxx/test/std/containers/views/mdspan/mdspan/conversion.pass.cpp
libcxx/test/std/containers/views/mdspan/mdspan/types.pass.cpp
libcxx/test/std/containers/views/views.span/range_concept_conformance.compile.pass.cpp

View File

@ -689,7 +689,6 @@ feature_test_macros = [
"name": "__cpp_lib_mdspan",
"values": {"c++23": 202207},
"headers": ["mdspan"],
"unimplemented": True,
},
{
"name": "__cpp_lib_memory_resource",