// { dg-do run { target c++23 } } #include #include #include "extents/int_like.h" #include "layout_like.h" constexpr auto dyn = std::dynamic_extent; template> constexpr void assert_typedefs() { static_assert(std::same_as); static_assert(std::same_as); static_assert(std::same_as); static_assert(std::same_as>); static_assert(std::same_as); static_assert(std::same_as>); static_assert(std::same_as); static_assert(std::same_as); static_assert(std::same_as); static_assert(std::same_as); static_assert(std::same_as); } template typename A> constexpr void test_typedefs() { assert_typedefs>, T, E, L, A>(); } constexpr void test_typedefs_all() { using E = std::extents; using L = std::layout_left; test_typedefs(); test_typedefs(); } template constexpr void test_rank() { using Extents = typename MDSpan::extents_type; static_assert(MDSpan::rank() == Extents::rank()); static_assert(MDSpan::rank_dynamic() == Extents::rank_dynamic()); } constexpr bool test_rank_all() { test_rank>>(); test_rank>>(); test_rank>>(); return true; } template constexpr void test_extent(Extents exts) { double data = 1.0; auto md = std::mdspan(&data, exts); using MDSpan = decltype(md); for(size_t i = 0; i < MDSpan::rank(); ++i) { VERIFY(MDSpan::static_extent(i) == Extents::static_extent(i)); VERIFY(md.extent(i) == exts.extent(i)); } } constexpr bool test_extent_all() { // For rank == 0, check existence of the methods without calling them. test_extent(std::extents{}); test_extent(std::extents{}); test_extent(std::extents{}); return true; } template constexpr void test_class_properties() { static_assert(std::copyable); static_assert(std::is_nothrow_move_constructible_v); static_assert(std::is_nothrow_move_assignable_v); static_assert(std::is_nothrow_swappable_v); constexpr bool trivially_copyable = std::is_trivially_copyable_v && std::is_trivially_copyable_v && std::is_trivially_copyable_v; static_assert(std::is_trivially_copyable_v == trivially_copyable); } constexpr bool test_class_properties_all() { test_class_properties>>(); test_class_properties>>(); test_class_properties>>(); return true; } constexpr bool test_default_ctor() { static_assert(!std::is_default_constructible_v>>); static_assert(!std::is_default_constructible_v>>); static_assert(std::is_default_constructible_v>>); std::mdspan> md; VERIFY(md.data_handle() == nullptr); VERIFY(md.empty()); return true; } constexpr bool test_from_other() { using Extents = std::extents; auto exts = Extents{}; auto mapping = std::layout_right::mapping(exts); constexpr size_t n = mapping.required_span_size(); std::array storage{}; auto md1 = std::mdspan(storage.data(), exts); auto md2 = std::mdspan>(md1); VERIFY(md1.data_handle() == md2.data_handle()); VERIFY(md1.size() == md2.size()); static_assert(!std::is_convertible_v< std::mdspan>, std::mdspan>>); static_assert(std::is_convertible_v< std::mdspan>, std::mdspan>>); static_assert(!std::is_constructible_v< std::mdspan>, std::mdspan>>); return true; } template> constexpr void assert_deduced_typedefs(auto md) { assert_typedefs(); } constexpr bool test_from_carray() { constexpr size_t n = 5; double data[n] = {1.1, 2.2, 3.3, 4.4, 5.5}; auto md = std::mdspan(data); assert_deduced_typedefs>(md); VERIFY(md.rank() == 1); VERIFY(md.rank_dynamic() == 0); VERIFY(md[2] == data[2]); return true; } constexpr bool test_from_pointer() { double value = 12.3; auto md = std::mdspan(&value); assert_deduced_typedefs>(md); VERIFY(md.rank() == 0); VERIFY(md.rank_dynamic() == 0); VERIFY(md[] == value); return true; } constexpr bool test_from_pointer_and_shape() { constexpr size_t n = 6; std::array data{1.1, 2.2, 3.3, 4.4, 5.5, 6.6}; std::array shape{2, 3}; std::span shape_view(shape); auto verify = [&data](auto md) { assert_deduced_typedefs>(md); VERIFY(md.rank() == 2); VERIFY(md.rank_dynamic() == 2); VERIFY((md[0, 0]) == data[0]); VERIFY((md[0, 1]) == data[1]); VERIFY((md[1, 0]) == data[3]); }; verify(std::mdspan(data.data(), shape[0], shape[1])); verify(std::mdspan(data.data(), shape)); verify(std::mdspan(data.data(), shape_view)); std::mdspan> md1 = {data.data(), shape}; verify(md1); std::mdspan> md2 = {data.data(), shape_view}; verify(md2); static_assert(std::is_constructible_v< std::mdspan>, float*>); static_assert(!std::is_constructible_v< std::mdspan>, float*, int>); static_assert(std::is_constructible_v< std::mdspan>, float*, int, int>); static_assert(std::is_constructible_v< std::mdspan>, float*, std::span>); static_assert(std::is_constructible_v< std::mdspan>, float*, std::span>); static_assert(!std::is_convertible_v< float*, std::mdspan>>); static_assert(std::is_constructible_v< std::mdspan>, float*, std::span>); static_assert(!std::is_constructible_v< std::mdspan>, float*, std::span>); static_assert(!std::is_constructible_v< std::mdspan>, float*, std::span>); static_assert(!std::is_constructible_v< std::mdspan>, float*, std::span>); return true; } constexpr bool test_from_pointer_and_integral_constant() { std::array buffer{}; double * ptr = buffer.data(); auto verify = [ptr](auto actual, auto exts) { auto expected = std::mdspan(ptr, exts); static_assert(std::same_as); VERIFY(actual.extents() == expected.extents()); }; auto c3 = std::integral_constant{}; auto c6 = std::integral_constant{}; verify(std::mdspan(ptr, 6), std::extents(6)); verify(std::mdspan(ptr, c6), std::extents(c6)); verify(std::mdspan(ptr, 2, c3), std::extents(2, c3)); return true; } constexpr bool test_from_extents() { constexpr size_t n = 3*5*7; std::array storage{}; using Extents = std::extents; auto exts = Extents{}; auto md = std::mdspan(storage.data(), exts); assert_deduced_typedefs(md); VERIFY(md.data_handle() == storage.data()); VERIFY(md.extents() == exts); return true; } constexpr bool test_from_mapping() { constexpr size_t n = 3*5*7; std::array storage{}; using Extents = std::extents; auto exts = Extents{}; auto m = std::layout_left::mapping(exts); auto md = std::mdspan(storage.data(), m); assert_deduced_typedefs(md); VERIFY(md.data_handle() == storage.data()); VERIFY(md.mapping() == m); return true; } constexpr bool test_from_accessor() { constexpr size_t n = 3*5*7; std::array storage{}; using Extents = std::extents; auto exts = Extents{}; auto m = std::layout_left::mapping(exts); auto a = std::default_accessor{}; auto md = std::mdspan(storage.data(), m, a); assert_deduced_typedefs(md); VERIFY(md.data_handle() == storage.data()); VERIFY(md.mapping() == m); return true; } void test_from_int_like() { constexpr size_t n = 3*5*7; std::array storage{}; auto verify = [&](auto md) { VERIFY(md.data_handle() == storage.data()); VERIFY(md.extent(0) == 3); VERIFY(md.extent(1) == 5); VERIFY(md.extent(2) == 7); VERIFY((md[IntLike(0), 0, IntLike(0)]) == 0.0); auto zero = std::array{IntLike(0), IntLike(0), IntLike(0)}; auto zero_view = std::span{zero}; VERIFY((md[zero]) == 0.0); VERIFY((md[zero_view]) == 0.0); }; auto shape = std::array{IntLike(3), IntLike(5), IntLike(7)}; auto shape_view = std::span{shape}; verify(std::mdspan(storage.data(), IntLike(3), 5, IntLike(7))); verify(std::mdspan(storage.data(), shape)); verify(std::mdspan(storage.data(), shape_view)); } template class OpaqueAccessor { struct Handle { constexpr Handle(T * other) : ptr(other) { } constexpr Handle(const Handle&) noexcept(NothrowConstructible) = default; constexpr Handle(Handle&&) noexcept(NothrowConstructible) = default; constexpr Handle& operator=(const Handle&) noexcept(NothrowAssignable) = default; constexpr Handle& operator=(Handle&&) noexcept(NothrowAssignable) = default; T * ptr; }; public: using element_type = T; using reference = T&; using data_handle_type = Handle; using offset_policy = OpaqueAccessor; reference access(data_handle_type p, size_t i) const { ++access_count; return p.ptr[i]; } typename offset_policy::data_handle_type offset(data_handle_type p, size_t i) const { ++offset_count; return typename offset_policy::data_handle_type{(void*)(p.ptr + i)}; } mutable size_t access_count = 0; mutable size_t offset_count = 0; }; void test_from_opaque_accessor() { constexpr size_t n = 3*5*7; std::array storage{}; using Extents = std::extents; auto exts = Extents{}; auto m = std::layout_left::mapping(exts); auto a = OpaqueAccessor{}; auto handle = OpaqueAccessor::data_handle_type{storage.data()}; auto md = std::mdspan(handle, m, a); using MDSpan = decltype(md); static_assert(std::same_as); VERIFY((md[0, 0, 0]) == 0.0); VERIFY(md.accessor().access_count == 1); VERIFY((md[2, 4, 6]) == 0.0); VERIFY(md.accessor().access_count == 2); } template class BaseClassAccessor { public: using element_type = T; using reference = Base&; using data_handle_type = T*; using offset_policy = BaseClassAccessor; static_assert(std::common_reference_with); reference access(data_handle_type p, size_t i) const { return p[i]; } typename offset_policy::data_handle_type offset(data_handle_type p, size_t i) const { return typename offset_policy::data_handle_type{p + i}; } }; struct Base { double value = 1.0; }; struct Derived : Base { double value = 2.0; }; void test_from_base_class_accessor() { constexpr size_t n = 3*5*7; std::array storage{}; using Extents = std::extents; auto exts = Extents{}; auto m = std::layout_left::mapping(exts); auto a = BaseClassAccessor{}; auto md = std::mdspan(storage.data(), m, a); using MDSpan = decltype(md); static_assert(std::same_as); static_assert(std::same_as); VERIFY((md[0, 0, 0].value) == 1.0); VERIFY((md[2, 4, 6].value) == 1.0); } constexpr bool test_from_mapping_like() { double data = 1.1; auto m = LayoutLike::mapping>{}; auto md = std::mdspan(&data, m); VERIFY((md[0, 0, 0]) == data); VERIFY((md[0, 1, 2]) == data); return true; } template constexpr void test_empty(MDSpan md) { VERIFY(md.empty() == (md.size() == 0)); } constexpr bool test_empty_all() { test_empty(std::mdspan>{}); return true; } constexpr bool test_access() { using Extents = std::extents; auto exts = Extents{}; auto mapping = std::layout_left::mapping(exts); constexpr size_t n = mapping.required_span_size(); std::array storage{}; auto md = std::mdspan(storage.data(), mapping); static_assert(std::__mdspan::__mapping_alike); for(int i = 0; i < exts.extent(0); ++i) for(int j = 0; j < exts.extent(1); ++j) for(int k = 0; k < exts.extent(2); ++k) { std::array ijk{i, j, k}; storage[mapping(i, j, k)] = 1.0; VERIFY((md[i, j, k]) == 1.0); VERIFY((md[ijk]) == 1.0); VERIFY((md[std::span(ijk)]) == 1.0); storage[mapping(i, j, k)] = 0.0; } return true; } constexpr bool test_swap() { using Extents = std::dextents; auto e1 = Extents{3, 5}; auto e2 = Extents{7, 11}; std::array s1{}; std::array s2{}; auto md1 = std::mdspan(s1.data(), e1); auto md2 = std::mdspan(s2.data(), e2); std::swap(md1, md2); VERIFY(md1.data_handle() == s2.data()); VERIFY(md2.data_handle() == s1.data()); VERIFY(md1.size() == s2.size()); VERIFY(md2.size() == s1.size()); return true; } namespace adl { template struct SwappableAccessor { using element_type = T; using reference = T&; using data_handle_type = T*; using offset_policy = SwappableAccessor; reference access(data_handle_type p, size_t i) const { return p[i]; } typename offset_policy::data_handle_type offset(data_handle_type p, size_t i) const { return p + i; } friend void swap(SwappableAccessor&, SwappableAccessor&) { ++swap_count; } static inline size_t swap_count = 0; }; } void test_swap_adl() { using Extents = std::extents; using Layout = std::layout_left; using Accessor = adl::SwappableAccessor; Accessor::swap_count = 0; std::mdspan m1, m2; swap(m1, m2); VERIFY(Accessor::swap_count == 1); } template constexpr void test_nothrow_movable() { using Layout = std::layout_left; using Extents = std::dextents; using Accessor = OpaqueAccessor; using Handle = Accessor::data_handle_type; static_assert(std::is_nothrow_move_assignable_v); static_assert(std::is_nothrow_move_constructible_v); static_assert(std::is_nothrow_move_assignable_v == Assignable); static_assert(std::is_nothrow_move_constructible_v == Constructible); using MDSpan = std::mdspan; static_assert(std::is_nothrow_move_assignable_v == Assignable); static_assert(std::is_nothrow_move_constructible_v == Constructible); } constexpr void test_nothrow_movable_all() { using MDSpan = std::mdspan>; static_assert(std::is_nothrow_move_assignable_v); static_assert(std::is_nothrow_move_constructible_v); test_nothrow_movable(); test_nothrow_movable(); test_nothrow_movable(); test_nothrow_movable(); } int main() { test_typedefs_all(); test_rank_all(); test_extent_all(); static_assert(test_extent_all()); test_class_properties_all(); static_assert(test_class_properties_all()); test_empty_all(); static_assert(test_empty_all()); test_default_ctor(); static_assert(test_default_ctor()); test_from_other(); static_assert(test_from_other()); test_from_carray(); static_assert(test_from_carray()); test_from_pointer_and_shape(); static_assert(test_from_pointer_and_shape()); test_from_pointer_and_integral_constant(); static_assert(test_from_pointer_and_integral_constant()); test_from_extents(); static_assert(test_from_extents()); test_from_mapping(); static_assert(test_from_mapping()); test_from_accessor(); static_assert(test_from_accessor()); test_from_int_like(); test_from_opaque_accessor(); test_from_base_class_accessor(); test_from_mapping_like(); static_assert(test_from_mapping_like()); test_access(); static_assert(test_access()); test_swap(); static_assert(test_swap()); test_swap_adl(); test_nothrow_movable_all(); return 0; }