Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion stl/inc/mdspan
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,8 @@ public:

template <class _OtherExtents>
requires is_constructible_v<extents_type, _OtherExtents>
constexpr explicit(extents_type::rank() > 0) mapping(const layout_stride::template mapping<_OtherExtents>& _Other)
constexpr explicit(extents_type::rank() > 0)
mapping(const layout_stride::template mapping<_OtherExtents>& _Other) noexcept
: _Exts(_Other.extents()) {
if constexpr (extents_type::rank() > 0) {
const bool _Verify = [&]<size_t... _Indices>(index_sequence<_Indices...>) {
Expand Down
131 changes: 128 additions & 3 deletions tests/std/include/test_mdspan_support.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,48 @@
#pragma once

#include <algorithm>
#include <array>
#include <concepts>
#include <cstddef>
#include <functional>
#include <mdspan>
#include <optional>
#include <ranges>
#include <span>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>

enum class IsExplicit : bool { no, yes };
enum class IsNothrow : bool { no, yes };

template <std::integral Int, IsNothrow Nothrow = IsNothrow::yes>
template <std::integral Int, IsNothrow Nothrow = IsNothrow::yes, IsExplicit Explicit = IsExplicit::no>
struct ConvertibleToInt {
constexpr operator Int() const noexcept(std::to_underlying(Nothrow)) {
return Int{1};
Int val = 1;

constexpr explicit(std::to_underlying(Explicit)) operator Int() const noexcept(std::to_underlying(Nothrow)) {
return val;
}
};

static_assert(std::is_aggregate_v<ConvertibleToInt<int>>);
static_assert(std::is_convertible_v<ConvertibleToInt<int>, int>);
static_assert(std::is_nothrow_convertible_v<ConvertibleToInt<int>, int>);
static_assert(!std::is_nothrow_convertible_v<ConvertibleToInt<int, IsNothrow::no>, int>);
static_assert(std::is_convertible_v<ConvertibleToInt<int, IsNothrow::no>, int>);
static_assert(!std::is_convertible_v<ConvertibleToInt<int, IsNothrow::no, IsExplicit::yes>, int>);

template <std::integral Int>
struct NonConstConvertibleToInt {
constexpr operator Int() noexcept; // not defined
};

static_assert(std::is_convertible_v<NonConstConvertibleToInt<int>, int>);
static_assert(!std::is_convertible_v<const NonConstConvertibleToInt<int>, int>);
static_assert(std::is_nothrow_convertible_v<NonConstConvertibleToInt<int>, int>);
static_assert(!std::is_nothrow_convertible_v<const NonConstConvertibleToInt<int>, int>);

struct NonConvertibleToAnything {};

namespace detail {
Expand Down Expand Up @@ -207,3 +233,102 @@ constexpr void check_members_with_various_extents(Fn&& fn) {
details::check_members_with_various_extents_impl(std::forward<Fn>(fn), std::make_index_sequence<16>{});
#endif // _PREFAST_
}

namespace details {
static constexpr bool permissive() {
return false;
}

template <class>
struct PermissiveTestBase {
static constexpr bool permissive() {
return true;
}
};

template <class T>
struct PermissiveTest : PermissiveTestBase<T> {
static constexpr bool test() {
return permissive();
}
};
} // namespace details

inline constexpr bool is_permissive = details::PermissiveTest<int>::test();

template <class Mapping>
struct MappingProperties {
typename Mapping::index_type req_span_size;
bool uniqueness;
bool exhaustiveness;
bool strideness;
};

template <class Mapping>
requires (!details::PermissiveTest<Mapping>::test())
MappingProperties<Mapping> get_mapping_properties(const Mapping& mapping) {
using IndexType = typename Mapping::index_type;
constexpr IndexType zero = 0;
constexpr auto rank = Mapping::extents_type::rank();
constexpr std::make_index_sequence<rank> rank_indices;

auto get_extent = [&](size_t i) { return mapping.extents().extent(i); };
auto multidim_indices = [&]<size_t... Indices>(std::index_sequence<Indices...>) {
return std::views::cartesian_product(std::views::iota(zero, get_extent(Indices))...);
}(rank_indices);

auto map_index = [&](const auto& tpl) { return std::apply([&](auto... i) { return mapping(i...); }, tpl); };
Comment thread
StephanTLavavej marked this conversation as resolved.
auto mapped_indices = multidim_indices | std::views::transform(map_index) | std::ranges::to<std::vector>();
std::ranges::sort(mapped_indices);

MappingProperties<Mapping> props{};

// Find required span size (N4950 [mdspan.layout.reqmts]/12)
if (std::ranges::contains(std::views::iota(0u, rank) | std::views::transform(get_extent), zero)) {
props.req_span_size = 0;
} else {
props.req_span_size = static_cast<IndexType>(1 + mapped_indices.back());
}

// Is mapping unique? (N4950 [mdspan.layout.reqmts]/14)
props.uniqueness = !std::ranges::contains(std::views::pairwise_transform(mapped_indices, std::minus{}), zero);
Comment thread
StephanTLavavej marked this conversation as resolved.

{ // Is mapping exhaustive? (N4950 [mdspan.layout.reqmts]/16)
const auto diffs = std::views::pairwise_transform(mapped_indices, [](auto x, auto y) { return y - x; });
props.exhaustiveness = std::ranges::find_if_not(diffs, [](auto x) { return x == 1; }) == diffs.end();
}

{ // Is mapping strided? (N4950 [mdspan.layout.reqmts]/18)
props.strideness = true; // assumption
for (auto r : std::views::iota(0u, rank)) {
std::optional<IndexType> sr;
for (auto i : multidim_indices) {
const auto i_plus_dr = [&]<size_t... Indices>(std::index_sequence<Indices...>) {
return std::array{static_cast<IndexType>(std::get<Indices>(i) + (Indices == r ? 1 : 0))...};
}(rank_indices);

if (i_plus_dr[r] < get_extent(r)) {
const auto diff = static_cast<IndexType>(map_index(i_plus_dr) - map_index(i));
if (!sr.has_value()) {
sr = diff;
} else if (*sr != diff) {
props.strideness = false;
break;
}
}
}

if (!props.strideness) {
break;
}
}
}

return props;
}

template <class Mapping>
requires (details::PermissiveTest<Mapping>::test())
constexpr MappingProperties<Mapping> get_mapping_properties(const Mapping&) {
return {}; // we cannot get properties in '/permissive' mode
}
1 change: 0 additions & 1 deletion tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,6 @@ tests\LWG3422_seed_seq_ctors
tests\LWG3480_directory_iterator_range
tests\LWG3545_pointer_traits_sfinae
tests\LWG3610_iota_view_size_and_integer_class
tests\P0009R18_mdspan
tests\P0009R18_mdspan_default_accessor
tests\P0009R18_mdspan_extents
tests\P0009R18_mdspan_extents_death
Expand Down
4 changes: 0 additions & 4 deletions tests/std/tests/P0009R18_mdspan/env.lst

This file was deleted.

Loading