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
7 changes: 7 additions & 0 deletions stl/inc/mdspan
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,13 @@ struct default_accessor {
using reference = _ElementType&;
using data_handle_type = _ElementType*;

static_assert(
sizeof(element_type) > 0, "ElementType must be a complete type (N4944 [mdspan.accessor.default.overview]/2).");
static_assert(!is_abstract_v<element_type>,
"ElementType cannot be an abstract type (N4944 [mdspan.accessor.default.overview]/2).");
static_assert(
!is_array_v<element_type>, "ElementType cannot be an array type (N4944 [mdspan.accessor.default.overview]/2).");

constexpr default_accessor() noexcept = default;

template <class _OtherElementType>
Expand Down
132 changes: 87 additions & 45 deletions tests/std/include/test_mdspan_support.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

enum class IsNothrow : bool { no, yes };

template <class Int, IsNothrow Nothrow = IsNothrow::yes>
template <std::integral Int, IsNothrow Nothrow = IsNothrow::yes>
struct ConvertibleToInt {
constexpr operator Int() const noexcept(std::to_underlying(Nothrow)) {
return Int{1};
Expand All @@ -20,53 +20,57 @@ struct ConvertibleToInt {

struct NonConvertibleToAnything {};

template <class T>
constexpr void check_implicit_conversion(T); // not defined
namespace detail {
template <class T>
constexpr void check_implicit_conversion(T); // not defined
}

// clang-format off
template <class T, class... Args>
concept NotImplicitlyConstructibleFrom =
std::constructible_from<T, Args...>
&& !requires(Args&&... args) { check_implicit_conversion<T>({std::forward<Args>(args)...}); };
&& !requires(Args&&... args) { detail::check_implicit_conversion<T>({std::forward<Args>(args)...}); };
// clang-format on

template <class T>
inline constexpr bool is_extents_v = false;

template <class T, size_t... E>
inline constexpr bool is_extents_v<std::extents<T, E...>> = true;

template <class Layout, class Mapping>
inline constexpr bool is_mapping_of_v =
std::is_same_v<typename Layout::template mapping<typename Mapping::extents_type>, Mapping>;

template <class M>
concept CheckNestedTypesOfLayoutMapping =
requires {
requires is_extents_v<typename M::extents_type>;
requires std::same_as<typename M::index_type, typename M::extents_type::index_type>;
requires std::same_as<typename M::rank_type, typename M::extents_type::rank_type>;
requires is_mapping_of_v<typename M::layout_type, M>;
};

template <class M>
concept CheckMemberFunctionsOfLayoutMapping = requires(const M m) {
{ m.extents() } -> std::same_as<const typename M::extents_type&>;
{ m.required_span_size() } -> std::same_as<typename M::index_type>;
{ m.is_unique() } -> std::same_as<bool>;
{ m.is_exhaustive() } -> std::same_as<bool>;
{ m.is_strided() } -> std::same_as<bool>;
};

template <class M>
concept CheckStaticFunctionsOfLayoutMapping = requires(const M m) {
{ M::is_always_strided() } -> std::same_as<bool>;
{ M::is_always_exhaustive() } -> std::same_as<bool>;
{ M::is_always_unique() } -> std::same_as<bool>;
std::bool_constant<M::is_always_strided()>::value;
std::bool_constant<M::is_always_exhaustive()>::value;
std::bool_constant<M::is_always_unique()>::value;
};
namespace detail {
template <class T>
inline constexpr bool is_extents_v = false;

template <class T, size_t... E>
inline constexpr bool is_extents_v<std::extents<T, E...>> = true;

template <class Layout, class Mapping>
inline constexpr bool is_mapping_of_v =
std::is_same_v<typename Layout::template mapping<typename Mapping::extents_type>, Mapping>;

template <class M>
concept CheckNestedTypesOfLayoutMapping = is_extents_v<typename M::extents_type>
&& std::same_as<typename M::index_type, typename M::extents_type::index_type>
&& std::same_as<typename M::rank_type, typename M::extents_type::rank_type>
&& is_mapping_of_v<typename M::layout_type, M>;

// clang-format off
template <class M>
concept CheckMemberFunctionsOfLayoutMapping =
requires(const M m) {
{ m.extents() } -> std::same_as<const typename M::extents_type&>;
{ m.required_span_size() } -> std::same_as<typename M::index_type>;
{ m.is_unique() } -> std::same_as<bool>;
{ m.is_exhaustive() } -> std::same_as<bool>;
{ m.is_strided() } -> std::same_as<bool>;
};
// clang-format on

template <class M>
concept CheckStaticFunctionsOfLayoutMapping = requires {
{ M::is_always_strided() } -> std::same_as<bool>;
{ M::is_always_exhaustive() } -> std::same_as<bool>;
{ M::is_always_unique() } -> std::same_as<bool>;
std::bool_constant<M::is_always_strided()>::value;
std::bool_constant<M::is_always_exhaustive()>::value;
std::bool_constant<M::is_always_unique()>::value;
};
} // namespace detail

// clang-format off
template <class M, class... Indices>
Expand All @@ -89,9 +93,9 @@ constexpr bool check_layout_mapping_requirements() {
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>);
static_assert(CheckNestedTypesOfLayoutMapping<M>);
static_assert(CheckMemberFunctionsOfLayoutMapping<M>);
static_assert(CheckStaticFunctionsOfLayoutMapping<M>);
static_assert(detail::CheckNestedTypesOfLayoutMapping<M>);
static_assert(detail::CheckMemberFunctionsOfLayoutMapping<M>);
static_assert(detail::CheckStaticFunctionsOfLayoutMapping<M>);

[]<size_t... Indices>(std::index_sequence<Indices...>) {
static_assert(CheckCallOperatorOfLayoutMapping<M, decltype(Indices)...>);
Expand All @@ -106,11 +110,49 @@ constexpr bool check_layout_mapping_requirements() {
}

template <class MP, class E>
requires is_extents_v<E>
requires detail::is_extents_v<E>
constexpr bool check_layout_mapping_policy_requirements() {
using X = typename MP::template mapping<E>;
static_assert(check_layout_mapping_requirements<X>());
static_assert(std::same_as<typename X::layout_type, MP>);
static_assert(std::same_as<typename X::extents_type, E>);
return true;
}

template <class A>
constexpr bool check_accessor_policy_requirements();

namespace detail {
template <class A>
concept CheckNestedTypesOfAccessorPolicy =
sizeof(typename A::element_type) > 0
&& (!std::is_abstract_v<typename A::element_type>) && std::copyable<typename A::data_handle_type>
&& std::is_nothrow_move_constructible_v<typename A::data_handle_type>
&& std::is_nothrow_move_assignable_v<typename A::data_handle_type>
&& std::is_nothrow_swappable_v<typename A::data_handle_type>
&& std::common_reference_with<typename A::reference, typename A::element_type&&>
&& (std::same_as<typename A::offset_policy, A>
|| check_accessor_policy_requirements<typename A::offset_policy>())
&& std::constructible_from<typename A::offset_policy, const A&>
&& std::is_same_v<typename A::offset_policy::element_type, typename A::element_type>;

// clang-format off
template <class A>
concept CheckMemberFunctionsOfAccessorPolicy =
requires(const A a, const typename A::data_handle_type p, size_t i) {
{ a.access(p, i) } -> std::same_as<typename A::reference>;
{ a.offset(p, i) } -> std::same_as<typename A::offset_policy::data_handle_type>;
};
// clang-format on
} // namespace detail

template <class A>
constexpr bool check_accessor_policy_requirements() {
static_assert(std::copyable<A>);
static_assert(std::is_nothrow_move_constructible_v<A>);
static_assert(std::is_nothrow_move_assignable_v<A>);
static_assert(std::is_nothrow_swappable_v<A>);
static_assert(detail::CheckNestedTypesOfAccessorPolicy<A>);
static_assert(detail::CheckMemberFunctionsOfAccessorPolicy<A>);
return true;
}
5 changes: 5 additions & 0 deletions tests/std/tests/P0009R18_mdspan_default_accessor/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@
#include <string>
#include <type_traits>

#include <test_mdspan_support.hpp>

using namespace std;

template <class ElementType>
constexpr void test_one(array<ElementType, 3> elems) {
using Accessor = default_accessor<ElementType>;

// default_accessor meets the accessor policy requirements
static_assert(check_accessor_policy_requirements<Accessor>());

// Check modeled concepts
static_assert(is_nothrow_move_constructible_v<Accessor>);
static_assert(is_nothrow_move_assignable_v<Accessor>);
Expand Down