Skip to content

Commit

Permalink
<mdspan>: Improve accessor testing (#3616)
Browse files Browse the repository at this point in the history
  • Loading branch information
JMazurkiewicz authored Apr 4, 2023
1 parent 74a6e2f commit 32853a6
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 45 deletions.
7 changes: 7 additions & 0 deletions stl/inc/mdspan
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,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

0 comments on commit 32853a6

Please sign in to comment.