On Wed, Jun 4, 2025 at 2:19 PM Tomasz Kaminski <tkami...@redhat.com> wrote:
> > > On Wed, Jun 4, 2025 at 2:05 PM Luc Grosheintz <luc.groshei...@gmail.com> > wrote: > >> >> >> On 6/4/25 13:19, Tomasz Kaminski wrote: >> > Ah, sorry I got confused in the review suggestions, and latter when >> > checking the code. >> > What I meant is: >> > >> > Because the incoming strided_layout is required to be unique, >> > that implies that for each of strides are greater or equal to the >> extents. >> > As a consequence, other.required_span_size() is always greater than >> equal >> > to size of index_space, >> > so we can have static assert like: >> > static_assert(__mdspan::__representable_size<_OExtents, index_type>, >> > "other.required_span_size() must be representable as index_type"); >> > In here: >> > if constexpr (cmp_greater( >> > numeric_limits<typename >> _StridedMapping::index_type>::max(), >> > numeric_limits<index_type>::max())) >> > { >> > static_assert(__mdspan::__representable_size<_OExtents, >> > index_type>, >> > "other.required_span_size() must be representable >> as >> > index_type") >> > >> __glibcxx_assert(!cmp_less(numeric_limits<index_type>::max(), >> > >> __other.required_span_size())); >> > } >> > >> >> I would lift the static assertion above the `if constexpr` because >> that way we have to trust the other mapping less. >> > Sure, make sense. > Maybe we should add a comment, explaning why !__representable_size && !is_unique implies __other.required_span_size() > index_type. > > >> > >> > >> > On Wed, Jun 4, 2025 at 1:09 PM Luc Grosheintz <luc.groshei...@gmail.com >> > >> > wrote: >> > >> >> >> >> >> >> On 6/3/25 15:24, Tomasz Kaminski wrote: >> >>> On Fri, May 30, 2025 at 6:44 PM Luc Grosheintz < >> luc.groshei...@gmail.com >> >>> >> >>> wrote: >> >>> >> >>>> Implements the tests for layout_stride and for the features of the >> other >> >>>> two layouts that depend on layout_stride. >> >>>> >> >>>> libstdc++-v3/ChangeLog: >> >>>> >> >>>> * >> testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc: >> >> Add >> >>>> tests for layout_stride. >> >>>> * testsuite/23_containers/mdspan/layouts/ctors.cc: Add >> test for >> >>>> layout_stride and the interaction with other layouts. >> >>>> * testsuite/23_containers/mdspan/layouts/mapping.cc: Ditto. >> >>>> * testsuite/23_containers/mdspan/layouts/stride.cc: New >> test. >> >>>> >> >>>> Signed-off-by: Luc Grosheintz <luc.groshei...@gmail.com> >> >>>> --- >> >>>> >> >>> LTGM. Only one comment regarding the tests that we could add. >> >>> >> >>>> .../mdspan/layouts/class_mandate_neg.cc | 3 + >> >>>> .../23_containers/mdspan/layouts/ctors.cc | 99 ++++ >> >>>> .../23_containers/mdspan/layouts/empty.cc | 1 + >> >>>> .../23_containers/mdspan/layouts/mapping.cc | 75 ++- >> >>>> .../23_containers/mdspan/layouts/stride.cc | 526 >> ++++++++++++++++++ >> >>>> 5 files changed, 703 insertions(+), 1 deletion(-) >> >>>> create mode 100644 >> >>>> libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride.cc >> >>>> >> >>>> diff --git >> >>>> >> >> >> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc >> >>>> >> >> >> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc >> >>>> index 6469fa601e9..6862ed0c0ae 100644 >> >>>> --- >> >>>> >> >> >> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc >> >>>> +++ >> >>>> >> >> >> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc >> >>>> @@ -31,9 +31,12 @@ template<size_t Count, typename Layout, typename >> >>>> OLayout> >> >>>> >> >>>> A<std::layout_left> a_left; // { dg-error >> >> "required >> >>>> from" } >> >>>> A<std::layout_right> a_right; // { dg-error >> >> "required >> >>>> from" } >> >>>> +A<std::layout_stride> a_stride; // { dg-error >> >> "required >> >>>> from" } >> >>>> >> >>>> B<1, std::layout_left, std::layout_left> bll; // { dg-error >> >> "required >> >>>> here" } >> >>>> +B<2, std::layout_left, std::layout_stride> bls; // { dg-error >> >> "required >> >>>> here" } >> >>>> >> >>> With the message that we added to static assert to layout_stride >> >>> constructor: >> >>> >> "other.required_span_size() must be representable as >> index_type" >> >>> I believe you could add here: >> >>> B<2, std::layout_stride, std::layout_left> >> >>> And similarly for layout_right. >> >> >> >> I've tried (even before) and always failed miserably. Only good news >> >> is that the final >> >> >> >> // { dg-prune-output "must be representable as index_type" } >> >> >> >> isn't superfluous. I seem like the only string I can match against >> >> is the one associated with that line of code, in this case that's >> >> >> >> mdspan:773: error: static assertion failed: The size of >> OtherExtents >> >> must be representable as index_type >> >> [ ... ] >> >> class_mandate_neg.cc:40: note: synthesized method 'constexpr B<4, >> >> std::layout_right, std::layout_stride>::B()' first required here >> >> >> >> I unsure why you're saying that we added the message: >> >> >> >> "other.required_span_size() must be representable as index_type" >> >> >> >> to a static_assert, we added it to a __glibcxx_assert. The tests here >> >> are designed to trigger different assertions. >> >> >> >> "A" triggers: >> >> >> >> template<typename _Extents> >> >> class layout_*::mapping >> >> { >> >> public: >> >> // ... >> >> >> >> static_assert(__mdspan::__representable_size<extents_type, >> >> index_type>, >> >> "The size of extents_type must be representable as >> index_type"); >> >> >> >> >> >> "B" triggers: >> >> >> >> template<typename _OExtents> >> >> constexpr explicit >> >> mapping(const _OExtents& __oexts, __mdspan::__internal_ctor) >> noexcept >> >> : _M_extents(__oexts) >> >> { >> >> static_assert(__mdspan::__representable_size<_OExtents, >> index_type>, >> >> "The size of OtherExtents must be representable as >> index_type"); >> >> >> >> We don't have tests that make __glibcxx_assert's fail. We could >> probably >> >> do it with constexpr variables with "dynamic" extents. >> >> >> >>> >> >>>> >> >>>> B<3, std::layout_right, std::layout_right> brr; // { dg-error >> >> "required >> >>>> here" } >> >>>> +B<4, std::layout_right, std::layout_stride> brs; // { dg-error >> >> "required >> >>>> here" } >> >>>> >> >>>> // { dg-prune-output "must be representable as index_type" } >> >>>> diff --git >> >> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc >> >>>> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc >> >>>> index b0e03161874..695e887ca87 100644 >> >>>> --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc >> >>>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc >> >>>> @@ -323,6 +323,104 @@ namespace from_left_or_right >> >>>> } >> >>>> } >> >>>> >> >>>> +// ctor: mapping(layout_stride::mapping<OExtents>) >> >>>> +namespace from_stride >> >>>> +{ >> >>>> + template<typename Mapping> >> >>>> + constexpr auto >> >>>> + strides(Mapping m) >> >>>> + { >> >>>> + constexpr auto rank = Mapping::extents_type::rank(); >> >>>> + std::array<typename Mapping::index_type, rank> s; >> >>>> + >> >>>> + if constexpr (rank > 0) >> >>>> + for(size_t i = 0; i < rank; ++i) >> >>>> + s[i] = m.stride(i); >> >>>> + return s; >> >>>> + } >> >>>> + >> >>>> + template<typename Layout, typename Extents, typename OExtents> >> >>>> + constexpr void >> >>>> + verify_convertible(OExtents oexts) >> >>>> + { >> >>>> + using Mapping = typename Layout::mapping<Extents>; >> >>>> + using OMapping = std::layout_stride::mapping<OExtents>; >> >>>> + >> >>>> + constexpr auto other = OMapping(oexts, >> >>>> strides(Mapping(Extents(oexts)))); >> >>>> + if constexpr (std::is_same_v<Layout, std::layout_right>) >> >>>> + ::verify_nothrow_convertible<Mapping>(other); >> >>>> + else >> >>>> + ::verify_convertible<Mapping>(other); >> >>>> + } >> >>>> + >> >>>> + template<typename Layout, typename Extents, typename OExtents> >> >>>> + constexpr void >> >>>> + verify_constructible(OExtents oexts) >> >>>> + { >> >>>> + using Mapping = typename Layout::mapping<Extents>; >> >>>> + using OMapping = std::layout_stride::mapping<OExtents>; >> >>>> + >> >>>> + constexpr auto other = OMapping(oexts, >> >>>> strides(Mapping(Extents(oexts)))); >> >>>> + if constexpr (std::is_same_v<Layout, std::layout_right>) >> >>>> + ::verify_nothrow_constructible<Mapping>(other); >> >>>> + else >> >>>> + ::verify_constructible<Mapping>(other); >> >>>> + } >> >>>> + >> >>>> + template<typename Layout> >> >>>> + constexpr bool >> >>>> + test_ctor() >> >>>> + { >> >>>> + assert_not_constructible< >> >>>> + typename Layout::mapping<std::extents<int>>, >> >>>> + std::layout_stride::mapping<std::extents<int, 1>>>(); >> >>>> + >> >>>> + assert_not_constructible< >> >>>> + typename Layout::mapping<std::extents<int, 1>>, >> >>>> + std::layout_stride::mapping<std::extents<int>>>(); >> >>>> + >> >>>> + assert_not_constructible< >> >>>> + typename Layout::mapping<std::extents<int, 2>>, >> >>>> + std::layout_stride::mapping<std::extents<int, 1>>>(); >> >>>> + >> >>>> + verify_convertible<Layout, >> >> std::extents<int>>(std::extents<int>{}); >> >>>> + >> >>>> + verify_convertible<Layout, std::extents<unsigned int>>( >> >>>> + std::extents<int>{}); >> >>>> + >> >>>> + // Rank == 0 doesn't check IndexType for convertibility. >> >>>> + verify_convertible<Layout, std::extents<int>>( >> >>>> + std::extents<unsigned int>{}); >> >>>> + >> >>>> + verify_constructible<Layout, std::extents<int, 3>>( >> >>>> + std::extents<int, 3>{}); >> >>>> + >> >>>> + verify_constructible<Layout, std::extents<unsigned int, 3>>( >> >>>> + std::extents<int, 3>{}); >> >>>> + >> >>>> + verify_constructible<Layout, std::extents<int, 3>>( >> >>>> + std::extents<unsigned int, 3>{}); >> >>>> + >> >>>> + verify_constructible<Layout, std::extents<int, 3, 5>>( >> >>>> + std::extents<int, 3, 5>{}); >> >>>> + >> >>>> + verify_constructible<Layout, std::extents<unsigned int, 3, >> 5>>( >> >>>> + std::extents<int, 3, 5>{}); >> >>>> + >> >>>> + verify_constructible<Layout, std::extents<int, 3, 5>>( >> >>>> + std::extents<unsigned int, 3, 5>{}); >> >>>> + return true; >> >>>> + } >> >>>> + >> >>>> + template<typename Layout> >> >>>> + constexpr void >> >>>> + test_all() >> >>>> + { >> >>>> + test_ctor<Layout>(); >> >>>> + static_assert(test_ctor<Layout>()); >> >>>> + } >> >>>> +} >> >>>> + >> >>>> template<typename Layout> >> >>>> constexpr void >> >>>> test_all() >> >>>> @@ -330,6 +428,7 @@ template<typename Layout> >> >>>> default_ctor::test_all<Layout>(); >> >>>> from_extents::test_all<Layout>(); >> >>>> from_same_layout::test_all<Layout>(); >> >>>> + from_stride::test_all<Layout>(); >> >>>> } >> >>>> >> >>>> int >> >>>> diff --git >> >> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc >> >>>> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc >> >>>> index dc2a95ebbe6..d550c0a8e84 100644 >> >>>> --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc >> >>>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc >> >>>> @@ -64,5 +64,6 @@ main() >> >>>> { >> >>>> static_assert(test_all<std::layout_left>()); >> >>>> static_assert(test_all<std::layout_right>()); >> >>>> + static_assert(test_all<std::layout_stride>()); >> >>>> return 0; >> >>>> } >> >>>> diff --git >> >>>> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc >> >>>> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc >> >>>> index 4dc0db66865..963c804a369 100644 >> >>>> --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc >> >>>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc >> >>>> @@ -12,6 +12,7 @@ template<typename Layout, typename Extents> >> >>>> { >> >>>> using M = typename Layout::mapping<Extents>; >> >>>> static_assert(std::__mdspan::__is_extents<typename >> >> M::extents_type>); >> >>>> + static_assert(std::__mdspan::__mapping_alike<M>); >> >>>> static_assert(std::copyable<M>); >> >>>> static_assert(std::is_nothrow_move_constructible_v<M>); >> >>>> static_assert(std::is_nothrow_move_assignable_v<M>); >> >>>> @@ -30,6 +31,8 @@ template<typename Layout, typename Extents> >> >>>> >> >>>> static_assert(M::is_always_unique() && M::is_unique()); >> >>>> static_assert(M::is_always_strided() && M::is_strided()); >> >>>> + if constexpr (!std::is_same_v<Layout, std::layout_stride>) >> >>>> + static_assert(M::is_always_exhaustive() && >> M::is_exhaustive()); >> >>>> return true; >> >>>> } >> >>>> >> >>>> @@ -105,6 +108,39 @@ template<typename Layout> >> >>>> { return exts; } >> >>>> }; >> >>>> >> >>>> +template<> >> >>>> + struct MappingFactory<std::layout_stride> >> >>>> + { >> >>>> + template<typename Extents> >> >>>> + static constexpr std::layout_stride::mapping<Extents> >> >>>> + create(Extents exts) >> >>>> + { >> >>>> + if constexpr (Extents::rank() == 0) >> >>>> + { >> >>>> + auto strides = std::array<size_t, 0>{}; >> >>>> + return std::layout_stride::mapping(exts, strides); >> >>>> + } >> >>>> + else if constexpr (Extents::rank() == 1) >> >>>> + { >> >>>> + auto strides = std::array<size_t, 1>{2}; >> >>>> + return std::layout_stride::mapping(exts, strides); >> >>>> + } >> >>>> + else if constexpr (Extents::rank() == 2) >> >>>> + { >> >>>> + size_t m = exts.extent(1); >> >>>> + auto strides = std::array<size_t, 2>{3*m, 2}; >> >>>> + return std::layout_stride::mapping(exts, strides); >> >>>> + } >> >>>> + else if constexpr (Extents::rank() == 3) >> >>>> + { >> >>>> + size_t n = exts.extent(0); >> >>>> + size_t m = exts.extent(1); >> >>>> + auto strides = std::array<size_t, 3>{3*m, 2, 11*m*n}; >> >>>> + return std::layout_stride::mapping(exts, strides); >> >>>> + } >> >>>> + } >> >>>> + }; >> >>>> + >> >>>> template<typename Layout> >> >>>> constexpr void >> >>>> test_linear_index_3d() >> >>>> @@ -280,6 +316,16 @@ template<typename Layout> >> >>>> VERIFY(m.stride(0) == 1); >> >>>> } >> >>>> >> >>>> +template<> >> >>>> + constexpr void >> >>>> + test_stride_1d<std::layout_stride>() >> >>>> + { >> >>>> + std::array<int, 1> strides{13}; >> >>>> + std::layout_stride::mapping m(std::extents<int, 3>{}, strides); >> >>>> + VERIFY(m.stride(0) == strides[0]); >> >>>> + VERIFY(m.strides() == strides); >> >>>> + } >> >>>> + >> >>>> template<typename Layout> >> >>>> constexpr void >> >>>> test_stride_2d(); >> >>>> @@ -302,6 +348,17 @@ template<> >> >>>> VERIFY(m.stride(1) == 1); >> >>>> } >> >>>> >> >>>> +template<> >> >>>> + constexpr void >> >>>> + test_stride_2d<std::layout_stride>() >> >>>> + { >> >>>> + std::array<int, 2> strides{13, 2}; >> >>>> + std::layout_stride::mapping m(std::extents<int, 3, 5>{}, >> strides); >> >>>> + VERIFY(m.stride(0) == strides[0]); >> >>>> + VERIFY(m.stride(1) == strides[1]); >> >>>> + VERIFY(m.strides() == strides); >> >>>> + } >> >>>> + >> >>>> template<typename Layout> >> >>>> constexpr void >> >>>> test_stride_3d(); >> >>>> @@ -326,6 +383,19 @@ template<> >> >>>> VERIFY(m.stride(2) == 1); >> >>>> } >> >>>> >> >>>> +template<> >> >>>> + constexpr void >> >>>> + test_stride_3d<std::layout_stride>() >> >>>> + { >> >>>> + std::dextents<int, 3> exts(3, 5, 7); >> >>>> + std::array<int, 3> strides{11, 2, 41}; >> >>>> + std::layout_stride::mapping<std::dextents<int, 3>> m(exts, >> >> strides); >> >>>> + VERIFY(m.stride(0) == strides[0]); >> >>>> + VERIFY(m.stride(1) == strides[1]); >> >>>> + VERIFY(m.stride(2) == strides[2]); >> >>>> + VERIFY(m.strides() == strides); >> >>>> + } >> >>>> + >> >>>> template<typename Layout> >> >>>> constexpr bool >> >>>> test_stride_all() >> >>>> @@ -347,7 +417,7 @@ template<typename Layout> >> >>>> test_has_stride_0d() >> >>>> { >> >>>> using Mapping = typename Layout::mapping<std::extents<int>>; >> >>>> - constexpr bool expected = false; >> >>>> + constexpr bool expected = std::is_same_v<Layout, >> >> std::layout_stride>; >> >>>> static_assert(has_stride<Mapping> == expected); >> >>>> } >> >>>> >> >>>> @@ -488,8 +558,11 @@ main() >> >>>> { >> >>>> test_all<std::layout_left>(); >> >>>> test_all<std::layout_right>(); >> >>>> + test_all<std::layout_stride>(); >> >>>> >> >>>> test_has_op_eq<std::layout_right, std::layout_left, false>(); >> >>>> + test_has_op_eq<std::layout_right, std::layout_stride, true>(); >> >>>> + test_has_op_eq<std::layout_left, std::layout_stride, true>(); >> >>>> test_has_op_eq_peculiar(); >> >>>> return 0; >> >>>> } >> >>>> diff --git >> >> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride.cc >> >>>> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride.cc >> >>>> new file mode 100644 >> >>>> index 00000000000..c8af5c61254 >> >>>> --- /dev/null >> >>>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride.cc >> >>>> @@ -0,0 +1,526 @@ >> >>>> +// { dg-do run { target c++23 } } >> >>>> +#include <mdspan> >> >>>> + >> >>>> +#include <testsuite_hooks.h> >> >>>> + >> >>>> +constexpr size_t dyn = std::dynamic_extent; >> >>>> + >> >>>> +template<typename MappingStride> >> >>>> + constexpr void >> >>>> + test_ctor_default_stride() >> >>>> + { >> >>>> + using Extents = typename MappingStride::extents_type; >> >>>> + MappingStride actual; >> >>>> + typename std::layout_right::mapping<Extents> expected; >> >>>> + >> >>>> + constexpr auto rank = MappingStride::extents_type::rank(); >> >>>> + if constexpr (rank > 0) >> >>>> + for(size_t i = 0; i < rank; ++i) >> >>>> + VERIFY(actual.stride(i) == expected.stride(i)); >> >>>> + } >> >>>> + >> >>>> +constexpr bool >> >>>> +test_ctor_default_stride_all() >> >>>> +{ >> >>>> + test_ctor_default_stride< >> >>>> + std::layout_stride::mapping<std::extents<int, 3>>>(); >> >>>> + >> >>>> + test_ctor_default_stride< >> >>>> + std::layout_stride::mapping<std::extents<int, 3, 5, 7>>>(); >> >>>> + >> >>>> + test_ctor_default_stride< >> >>>> + std::layout_stride::mapping<std::dextents<int, 3>>>(); >> >>>> + >> >>>> + test_ctor_default_stride< >> >>>> + std::layout_stride::mapping<std::extents<int, 0, 5, 7>>>(); >> >>>> + >> >>>> + test_ctor_default_stride< >> >>>> + std::layout_stride::mapping<std::extents<int, 3, dyn, dyn>>>(); >> >>>> + >> >>>> + test_ctor_default_stride< >> >>>> + std::layout_stride::mapping<std::extents<int, dyn, dyn, 3>>>(); >> >>>> + return true; >> >>>> +} >> >>>> + >> >>>> +struct IntLikeA >> >>>> +{ >> >>>> + operator int() >> >>>> + { return 0; } >> >>>> +}; >> >>>> + >> >>>> +struct IntLikeB >> >>>> +{ >> >>>> + operator int() noexcept >> >>>> + { return 0; } >> >>>> +}; >> >>>> + >> >>>> +struct NotIntLike >> >>>> +{ }; >> >>>> + >> >>>> +template<typename E, typename E_arg, typename T, size_t N, bool >> >> Expected> >> >>>> +constexpr void >> >>>> +test_stride_constructible() >> >>>> +{ >> >>>> + static_assert(std::is_nothrow_constructible_v< >> >>>> + std::layout_stride::mapping<E>, E_arg, std::span<T, N>> == >> >>>> Expected); >> >>>> + static_assert(std::is_nothrow_constructible_v< >> >>>> + std::layout_stride::mapping<E>, E_arg, std::array<T, N>> == >> >>>> Expected); >> >>>> + >> >> static_assert(!std::is_constructible_v<std::layout_stride::mapping<E>, >> >>>> + E_arg>); >> >>>> +} >> >>>> + >> >>>> +constexpr void >> >>>> +test_stride_constructible_all() >> >>>> +{ >> >>>> + using E0 = std::extents<int>; >> >>>> + using E1 = std::extents<int, 2>; >> >>>> + using E2 = std::extents<int, dyn>; >> >>>> + >> >>>> + test_stride_constructible<E0, E0, int, 0, true>(); >> >>>> + test_stride_constructible<E0, E0, IntLikeA, 0, false>(); >> >>>> + test_stride_constructible<E0, E0, IntLikeB, 0, true>(); >> >>>> + test_stride_constructible<E0, E0, NotIntLike, 0, false>(); >> >>>> + test_stride_constructible<E1, E1, int, 1, true>(); >> >>>> + test_stride_constructible<E2, E1, int, 1, true>(); >> >>>> + test_stride_constructible<E1, E1, int, 2, false>(); >> >>>> + test_stride_constructible<E1, E0, int, 1, false>(); >> >>>> +} >> >>>> + >> >>>> +template<typename Extents, typename Shape> >> >>>> + constexpr void >> >>>> + test_ctor_shape_strides(Extents exts, Shape strides) >> >>>> + { >> >>>> + using M = std::layout_stride::mapping<Extents>; >> >>>> + M m(exts, strides); >> >>>> + >> >>>> + if constexpr (Extents::rank() > 0) >> >>>> + for(size_t i = 0; i < exts.rank(); ++i) >> >>>> + { >> >>>> + VERIFY(m.stride(i) == strides[i]); >> >>>> + VERIFY(m.extents().extent(i) == exts.extent(i)); >> >>>> + } >> >>>> + } >> >>>> + >> >>>> +constexpr bool >> >>>> +test_ctor_shape_stride_all() >> >>>> +{ >> >>>> + test_ctor_shape_strides(std::extents<int>{}, std::array<int, >> 0>{}); >> >>>> + test_ctor_shape_strides(std::extents<int, 2>{}, std::array<int, >> >> 1>{3}); >> >>>> + test_ctor_shape_strides(std::extents<int, 2, 4, 6>{}, >> >>>> + std::array<int, 3>{20, 5, 45}); >> >>>> + return true; >> >>>> +} >> >>>> + >> >>>> +template<typename Extents, std::array<bool, 2> Strided, >> >>>> + std::array<bool, 2> Unique, std::array<bool, 2> Exhautive, >> >>>> + typename Extents::index_type Offset = 0> >> >>>> + struct MappingLike >> >>>> + { >> >>>> + using extents_type = Extents; >> >>>> + using index_type = typename Extents::index_type; >> >>>> + >> >>>> + constexpr >> >>>> + MappingLike(extents_type extents, >> >>>> + std::array<index_type, Extents::rank()> strides) >> >>>> + : _extents(extents), _strides(strides) >> >>>> + { } >> >>>> + >> >>>> + static constexpr bool >> >>>> + is_always_strided() requires (Strided[0]) >> >>>> + { return Strided[1]; } >> >>>> + >> >>>> + static constexpr bool >> >>>> + is_always_unique() requires (Unique[0]) >> >>>> + { return Unique[1]; } >> >>>> + >> >>>> + static constexpr bool >> >>>> + is_always_exhaustive() requires (Exhautive[0]) >> >>>> + { return Exhautive[1]; } >> >>>> + >> >>>> + constexpr Extents >> >>>> + extents() const { return _extents; } >> >>>> + >> >>>> + constexpr index_type >> >>>> + stride(size_t i) const { return _strides[i]; } >> >>>> + >> >>>> + template<typename... Indices> >> >>>> + constexpr index_type >> >>>> + operator()(Indices... indices) const >> >>>> + { >> >>>> + if (empty()) >> >>>> + VERIFY(false); >> >>>> + >> >>>> + std::array<index_type, Extents::rank()> ind_arr{indices...}; >> >>>> + index_type ret = Offset; >> >>>> + for(size_t i = 0; i < Extents::rank(); ++i) >> >>>> + ret += ind_arr[i]*_strides[i]; >> >>>> + return ret; >> >>>> + } >> >>>> + >> >>>> + private: >> >>>> + constexpr bool >> >>>> + empty() const >> >>>> + { >> >>>> + for (size_t i = 0; i < extents_type::rank(); ++i) >> >>>> + if (_extents.extent(i) == 0) >> >>>> + return true; >> >>>> + return false; >> >>>> + } >> >>>> + >> >>>> + Extents _extents; >> >>>> + std::array<index_type, Extents::rank()> _strides; >> >>>> + }; >> >>>> + >> >>>> + >> >>>> +template<size_t Rank> >> >>>> +struct ExtentLike >> >>>> +{ >> >>>> + using index_type = int; >> >>>> + >> >>>> + static constexpr size_t >> >>>> + rank() { return Rank; } >> >>>> +}; >> >>>> + >> >>>> + >> >>>> +template<typename E1> >> >>>> +constexpr void >> >>>> +test_mapping_like_constructible() >> >>>> +{ >> >>>> + using M = std::layout_stride::mapping<E1>; >> >>>> + using E2 = std::dextents<typename E1::index_type, E1::rank()>; >> >>>> + using E3 = std::dextents<typename E1::index_type, E1::rank() + 1>; >> >>>> + using E4 = ExtentLike<E1::rank()>; >> >>>> + >> >>>> + constexpr auto TT = std::array{true, true}; >> >>>> + constexpr auto FT = std::array{false, true}; >> >>>> + constexpr auto TF = std::array{true, false}; >> >>>> + >> >>>> + static_assert(std::is_constructible_v<M, MappingLike<E1, TT, TT, >> >> TT>>); >> >>>> + static_assert(std::is_constructible_v<M, MappingLike<E2, TT, TT, >> >> TT>>); >> >>>> + static_assert(!std::is_constructible_v<M, MappingLike<E3, TT, TT, >> >> TT>>); >> >>>> + static_assert(!std::is_constructible_v<M, MappingLike<E1, FT, TT, >> >> TT>>); >> >>>> + static_assert(!std::is_constructible_v<M, MappingLike<E1, TF, TT, >> >> TT>>); >> >>>> + static_assert(!std::is_constructible_v<M, MappingLike<E1, TT, FT, >> >> TT>>); >> >>>> + static_assert(!std::is_constructible_v<M, MappingLike<E1, TT, TF, >> >> TT>>); >> >>>> + static_assert(!std::is_constructible_v<M, MappingLike<E1, TT, TT, >> >> FT>>); >> >>>> + static_assert(std::is_constructible_v<M, MappingLike<E1, TT, TT, >> >> TF>>); >> >>>> + static_assert(!std::is_constructible_v<M, MappingLike<E4, TT, TT, >> >> TF>>); >> >>>> + static_assert(!std::is_constructible_v<M, MappingLike<E4, TT, TT, >> >> TT>>); >> >>>> +} >> >>>> + >> >>>> +constexpr void >> >>>> +test_mapping_like_constructible_all() >> >>>> +{ >> >>>> + test_mapping_like_constructible<std::extents<int>>(); >> >>>> + test_mapping_like_constructible<std::extents<int, 2>>(); >> >>>> + test_mapping_like_constructible<std::extents<int, 2, 3>>(); >> >>>> +} >> >>>> + >> >>>> +template<typename E1, typename E2> >> >>>> +constexpr void >> >>>> +test_mapping_like_convertible() >> >>>> +{ >> >>>> + using M1 = std::layout_stride::mapping<E1>; >> >>>> + using M2 = std::layout_stride::mapping<E2>; >> >>>> + constexpr auto TT = std::array{true, true}; >> >>>> + >> >>>> + static_assert(!std::is_convertible_v<MappingLike<E1, TT, TT, TT>, >> >> M1>); >> >>>> + static_assert(!std::is_convertible_v<MappingLike<E2, TT, TT, TT>, >> >> M1>); >> >>>> + static_assert(!std::is_convertible_v<MappingLike<E1, TT, TT, TT>, >> >> M2>); >> >>>> + >> >>>> + >> static_assert(std::is_convertible_v<std::layout_stride::mapping<E2>, >> >>>> M1>); >> >>>> + static_assert(std::is_convertible_v<std::layout_left::mapping<E2>, >> >> M1>); >> >>>> + >> static_assert(std::is_convertible_v<std::layout_right::mapping<E2>, >> >>>> M1>); >> >>>> + >> >>>> + >> static_assert(!std::is_convertible_v<std::layout_stride::mapping<E1>, >> >>>> M2>); >> >>>> + >> static_assert(!std::is_convertible_v<std::layout_left::mapping<E1>, >> >>>> M2>); >> >>>> + >> static_assert(!std::is_convertible_v<std::layout_right::mapping<E1>, >> >>>> M2>); >> >>>> +} >> >>>> + >> >>>> +constexpr void >> >>>> +test_mapping_like_convertible_all() >> >>>> +{ >> >>>> + test_mapping_like_convertible<std::extents<unsigned int>, >> >>>> + std::extents<int>>(); >> >>>> + test_mapping_like_convertible<std::extents<unsigned int, 2>, >> >>>> + std::extents<int, 2>>(); >> >>>> + test_mapping_like_convertible<std::extents<int, dyn, 3>, >> >>>> + std::extents<int, 2, 3>>(); >> >>>> +} >> >>>> + >> >>>> +template<typename Extents> >> >>>> +constexpr void >> >>>> +test_ctor_stride_like(Extents exts, std::array<int, Extents::rank()> >> >>>> strides) >> >>>> +{ >> >>>> + auto other_right = std::layout_right::mapping(exts); >> >>>> + auto other_left = std::layout_left::mapping(exts); >> >>>> + auto other_stride = std::layout_stride::mapping(exts, strides); >> >>>> + >> >>>> + VERIFY(std::layout_stride::mapping<Extents>(other_right) == >> >>>> other_right); >> >>>> + VERIFY(std::layout_stride::mapping<Extents>(other_left) == >> >> other_left); >> >>>> + VERIFY(std::layout_stride::mapping<Extents>(other_stride) == >> >>>> other_stride); >> >>>> +} >> >>>> + >> >>>> +constexpr void >> >>>> +test_ctor_stride_like_all() >> >>>> +{ >> >>>> + using E1 = std::extents<int>; >> >>>> + auto s1 = std::array<int, 0>{}; >> >>>> + test_ctor_stride_like(E1{}, s1); >> >>>> + >> >>>> + using E2 = std::extents<int, 3>; >> >>>> + auto s2 = std::array<int, 1>{2}; >> >>>> + test_ctor_stride_like(E2{}, s2); >> >>>> + >> >>>> + using E3 = std::extents<int, 3, 5, 7>; >> >>>> + auto s3 = std::array<int, 3>{5, 1, 15}; >> >>>> + test_ctor_stride_like(E3{}, s3); >> >>>> +} >> >>>> + >> >>>> +constexpr bool >> >>>> +test_ctor_strides_all() >> >>>> +{ >> >>>> + test_ctor_default_stride_all(); >> >>>> + test_ctor_shape_stride_all(); >> >>>> + test_ctor_stride_like_all(); >> >>>> + return true; >> >>>> +} >> >>>> + >> >>>> +// Check is_exhaustive. >> >>>> +template<typename Extents, typename Strides> >> >>>> + constexpr void >> >>>> + test_is_exhaustive(Extents extents, Strides strides, bool >> expected) >> >>>> + { >> >>>> + std::layout_stride::mapping<Extents> m(extents, strides); >> >>>> + VERIFY(m.is_exhaustive() == expected); >> >>>> + >> >>>> + bool always_exhaustive = extents.rank() == 0 || >> >>>> m.required_span_size() == 0; >> >>>> + VERIFY(m.is_always_exhaustive() == always_exhaustive); >> >>>> + } >> >>>> + >> >>>> +constexpr void >> >>>> +test_is_exhaustive_zero_1d() >> >>>> +{ >> >>>> + std::extents<int, 0> extents; >> >>>> + test_is_exhaustive(extents, std::array{1}, true); >> >>>> + test_is_exhaustive(extents, std::array{2}, true); >> >>>> +} >> >>>> + >> >>>> +constexpr void >> >>>> +test_is_exhaustive_zero_3d() >> >>>> +{ >> >>>> + std::extents<int, 3, 0, 7> extents; >> >>>> + >> >>>> + test_is_exhaustive(extents, std::array{1, 1, 1}, true); >> >>>> + test_is_exhaustive(extents, std::array{1, 2*21, 2*3}, true); >> >>>> + test_is_exhaustive(extents, std::array{7, 2*21, 1}, true); >> >>>> + test_is_exhaustive(extents, std::array{1, 21, 3}, true); >> >>>> + test_is_exhaustive(extents, std::array{7, 21, 1}, true); >> >>>> +} >> >>>> + >> >>>> +constexpr void >> >>>> +test_is_exhaustive_0d() >> >>>> +{ >> >>>> + std::extents<int> extents; >> >>>> + test_is_exhaustive(extents, std::array<int, 0>{}, true); >> >>>> +} >> >>>> + >> >>>> +constexpr void >> >>>> +test_is_exhaustive_1d() >> >>>> +{ >> >>>> + std::extents<int, 3> extents; >> >>>> + test_is_exhaustive(extents, std::array{1}, true); >> >>>> + test_is_exhaustive(extents, std::array{3}, false); >> >>>> +} >> >>>> + >> >>>> + >> >>>> +constexpr void >> >>>> +test_is_exhaustive_3d() >> >>>> +{ >> >>>> + std::extents<int, 3, dyn, 7> extents(5); >> >>>> + >> >>>> + test_is_exhaustive(extents, std::array{1, 3, 3*5}, true); >> >>>> + test_is_exhaustive(extents, std::array{5*7, 1, 5}, true); >> >>>> + test_is_exhaustive(extents, std::array{7, 3*7, 1}, true); >> >>>> + >> >>>> + test_is_exhaustive(extents, std::array{1, 3, 2*3*5}, false); >> >>>> + test_is_exhaustive(extents, std::array{2*5*7, 1, 2*5}, false); >> >>>> + test_is_exhaustive(extents, std::array{2*7, 2*3*7, 2}, false); >> >>>> +} >> >>>> + >> >>>> +constexpr void >> >>>> +test_is_exhaustive_ones() >> >>>> +{ >> >>>> + std::extents<int, 1, 1, 3, 1> extents; >> >>>> + test_is_exhaustive(extents, std::array{1, 1, 1, 1}, true); >> >>>> + test_is_exhaustive(extents, std::array{1, 1, 1, 3}, true); >> >>>> + test_is_exhaustive(extents, std::array{3, 3, 1, 3}, true); >> >>>> + test_is_exhaustive(extents, std::array{3, 1, 1, 3}, true); >> >>>> +} >> >>>> + >> >>>> +constexpr bool >> >>>> +test_is_exhaustive_all() >> >>>> +{ >> >>>> + test_is_exhaustive_zero_1d(); >> >>>> + test_is_exhaustive_zero_3d(); >> >>>> + test_is_exhaustive_ones(); >> >>>> + test_is_exhaustive_0d(); >> >>>> + test_is_exhaustive_1d(); >> >>>> + test_is_exhaustive_3d(); >> >>>> + return true; >> >>>> +} >> >>>> + >> >>>> +template<typename Extents, int Offset> >> >>>> + using OffsetMapping = MappingLike<Extents, {true, true}, {true, >> >> true}, >> >>>> + {true, false}, Offset>; >> >>>> + >> >>>> +template<typename Extents> >> >>>> + constexpr void >> >>>> + test_eq(Extents exts, >> >>>> + std::array<typename Extents::index_type, Extents::rank()> >> >>>> left_strides, >> >>>> + std::array<typename Extents::index_type, Extents::rank()> >> >>>> right_strides, >> >>>> + std::array<typename Extents::index_type, Extents::rank()> >> >>>> padded_strides) >> >>>> + { >> >>>> + using DExtents = std::dextents<int, Extents::rank()>; >> >>>> + >> >>>> + std::layout_left::mapping<Extents> ml; >> >>>> + std::layout_right::mapping<DExtents> mr(exts); >> >>>> + >> >>>> + std::layout_stride::mapping<Extents> msd; >> >>>> + std::layout_stride::mapping<Extents> msl(exts, left_strides); >> >>>> + std::layout_stride::mapping<Extents> msr(exts, right_strides); >> >>>> + std::layout_stride::mapping<Extents> msp(exts, padded_strides); >> >>>> + >> >>>> + OffsetMapping<Extents, 0> mor{exts, right_strides}; >> >>>> + OffsetMapping<Extents, 0> mol{exts, left_strides}; >> >>>> + OffsetMapping<Extents, 0> mop{exts, padded_strides}; >> >>>> + OffsetMapping<Extents, 1> moo{exts, right_strides}; >> >>>> + >> >>>> + VERIFY(msd == mr); >> >>>> + VERIFY(msd == mor); >> >>>> + VERIFY(msd != msp); >> >>>> + VERIFY(msd != mop); >> >>>> + >> >>>> + VERIFY(msl == ml); >> >>>> + VERIFY(msl == mol); >> >>>> + VERIFY(msd != msp); >> >>>> + VERIFY(msl != mop); >> >>>> + >> >>>> + VERIFY(msp == mop); >> >>>> + VERIFY(msp != ml); >> >>>> + VERIFY(msp != mr); >> >>>> + >> >>>> + VERIFY(msd != moo); >> >>>> + } >> >>>> + >> >>>> +constexpr void >> >>>> +test_eq_0d() >> >>>> +{ >> >>>> + using Extents = std::extents<int>; >> >>>> + Extents exts; >> >>>> + std::layout_left::mapping<Extents> ml; >> >>>> + std::layout_right::mapping<Extents> mr; >> >>>> + std::layout_stride::mapping<Extents> ms; >> >>>> + OffsetMapping<Extents, 0> mor{exts, {}}; >> >>>> + OffsetMapping<Extents, 1> moo{exts, {}}; >> >>>> + >> >>>> + VERIFY(ms == ml); >> >>>> + VERIFY(ms == mr); >> >>>> + VERIFY(ms == mor); >> >>>> + VERIFY(ms != moo); >> >>>> +} >> >>>> + >> >>>> +constexpr void >> >>>> +test_eq_1d() >> >>>> +{ >> >>>> + using Extents = std::extents<int, 2>; >> >>>> + auto exhaustive_strides = std::array{1}; >> >>>> + auto padded_strides = std::array{2}; >> >>>> + >> >>>> + test_eq(Extents{}, exhaustive_strides, exhaustive_strides, >> >>>> padded_strides); >> >>>> +} >> >>>> + >> >>>> +constexpr void >> >>>> +test_eq_2d() >> >>>> +{ >> >>>> + using Extents = std::extents<int, 1, 2>; >> >>>> + auto left_strides = std::array{1, 1}; >> >>>> + auto right_strides = std::array{2, 1}; >> >>>> + auto padded_strides = std::array{2, 8}; >> >>>> + >> >>>> + test_eq(Extents{}, left_strides, right_strides, padded_strides); >> >>>> +} >> >>>> + >> >>>> +constexpr void >> >>>> +test_eq_zero() >> >>>> +{ >> >>>> + using Extents = std::extents<int, 0, 2>; >> >>>> + using Mapping = std::layout_stride::mapping<Extents>; >> >>>> + >> >>>> + Extents exts; >> >>>> + std::array<int, 2> sl{1, 5}; >> >>>> + std::array<int, 2> sr{5, 1}; >> >>>> + >> >>>> + Mapping m1(exts, sl); >> >>>> + Mapping m2(exts, sl); >> >>>> + Mapping m3(exts, sr); >> >>>> + OffsetMapping<Extents, 0> m4(exts, sl); >> >>>> + >> >>>> + VERIFY(m1 == m2); >> >>>> + VERIFY(m1 != m3); >> >>>> + VERIFY(m1 == m4); >> >>>> + >> >>>> +} >> >>>> + >> >>>> +constexpr bool >> >>>> +test_eq_all() >> >>>> +{ >> >>>> + test_eq_0d(); >> >>>> + test_eq_1d(); >> >>>> + test_eq_2d(); >> >>>> + test_eq_zero(); >> >>>> + return true; >> >>>> +} >> >>>> + >> >>>> +template<typename M1, typename M2> >> >>>> + concept has_op_eq = requires (M1 m1, M2 m2) >> >>>> + { >> >>>> + { m1 == m2 } -> std::same_as<bool>; >> >>>> + { m2 == m1 } -> std::same_as<bool>; >> >>>> + { m1 != m2 } -> std::same_as<bool>; >> >>>> + { m2 != m1 } -> std::same_as<bool>; >> >>>> + }; >> >>>> + >> >>>> +constexpr void >> >>>> +test_has_op_eq() >> >>>> +{ >> >>>> + using E1 = std::extents<int>; >> >>>> + using E2 = std::extents<int, 2>; >> >>>> + using E3 = std::extents<int, 1, 2>; >> >>>> + constexpr auto FT = std::array{false, true}; >> >>>> + >> >>>> + static_assert(!has_op_eq< >> >>>> + std::layout_stride::mapping<E1>, MappingLike<E1, FT, FT, >> FT>>); >> >>>> + >> >>>> + static_assert(!has_op_eq< >> >>>> + std::layout_stride::mapping<E2>, MappingLike<E2, FT, FT, >> FT>>); >> >>>> + >> >>>> + static_assert(!has_op_eq< >> >>>> + std::layout_stride::mapping<E3>, MappingLike<E3, FT, FT, >> FT>>); >> >>>> +} >> >>>> + >> >>>> +int >> >>>> +main() >> >>>> +{ >> >>>> + test_ctor_strides_all(); >> >>>> + static_assert(test_ctor_strides_all()); >> >>>> + test_mapping_like_convertible_all(); >> >>>> + test_mapping_like_constructible_all(); >> >>>> + test_stride_constructible_all(); >> >>>> + test_is_exhaustive_all(); >> >>>> + static_assert(test_is_exhaustive_all()); >> >>>> + test_eq_all(); >> >>>> + static_assert(test_eq_all()); >> >>>> + test_has_op_eq(); >> >>>> + return 0; >> >>>> +} >> >>>> -- >> >>>> 2.49.0 >> >>>> >> >>>> >> >>> >> >> >> >> >> > >> >>