[PATCH v5 3/8] libstdc++: Add tests for layout_left.

Luc Grosheintz luc.grosheintz@gmail.com
Tue Jun 3 12:50:39 GMT 2025



On 6/3/25 14:31, Tomasz Kaminski wrote:
> On Mon, Jun 2, 2025 at 9:07 AM Luc Grosheintz <luc.grosheintz@gmail.com>
> wrote:
> 
>>
>>
>> On 5/30/25 18:42, Luc Grosheintz wrote:
>>> Implements a suite of tests for the currently implemented parts of
>>> layout_left. The individual tests are templated over the layout type, to
>>> allow reuse as more layouts are added.
>>>
>>> libstdc++-v3/ChangeLog:
>>>
>>>        * testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc: New
>> test.
>>>        * testsuite/23_containers/mdspan/layouts/ctors.cc: New test.
>>>        * testsuite/23_containers/mdspan/layouts/empty.cc: New test.
>>>        * testsuite/23_containers/mdspan/layouts/mapping.cc: New test.
>>>
>>
>> CI complains that empty.cc contains a bug on 32-bit systems. I'll explain
>> below.
>>
>>> Signed-off-by: Luc Grosheintz <luc.grosheintz@gmail.com>
>>> ---
>>>    .../mdspan/layouts/class_mandate_neg.cc       |  36 ++
>>>    .../23_containers/mdspan/layouts/ctors.cc     | 280 +++++++++++
>>>    .../23_containers/mdspan/layouts/empty.cc     |  67 +++
>>>    .../23_containers/mdspan/layouts/mapping.cc   | 437 ++++++++++++++++++
>>>    4 files changed, 820 insertions(+)
>>>    create mode 100644
>> libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc
>>>    create mode 100644
>> libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
>>>    create mode 100644
>> libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc
>>>    create mode 100644
>> libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.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
>>> new file mode 100644
>>> index 00000000000..62526140200
>>> --- /dev/null
>>> +++
>> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc
>>> @@ -0,0 +1,36 @@
>>> +// { dg-do compile { target c++23 } }
>>> +#include<mdspan>
>>> +
>>> +#include <cstdint>
>>> +
>>> +constexpr size_t dyn = std::dynamic_extent;
>>> +static constexpr size_t n = std::numeric_limits<uint8_t>::max() / 2;
>>> +
>>> +template<typename Layout>
>>> +  struct A
>>> +  {
>>> +    typename Layout::mapping<std::extents<uint8_t, n, 2>> m0;
>>> +    typename Layout::mapping<std::extents<uint8_t, n, 2, dyn>> m1;
>>> +    typename Layout::mapping<std::extents<uint8_t, n, 2, 0>> m2;
>>> +
>>> +    using extents_type = std::extents<uint8_t, n, 4>;
>>> +    typename Layout::mapping<extents_type> m3; // { dg-error "required
>> from" }
>>> +  };
>>> +
>>> +template<size_t Count, typename Layout, typename OLayout>
>>> +  struct B                     // { dg-error "expansion of" }
>>> +  {
>>> +    using Extents = std::extents<uint8_t, dyn, dyn, Count>;
>>> +    using OExtents = std::extents<uint16_t, n, 4, Count>;
>>> +
>>> +    using Mapping = typename Layout::mapping<Extents>;
>>> +    using OMapping = typename OLayout::mapping<OExtents>;
>>> +
>>> +    Mapping m{OMapping{}};
>>> +  };
>>> +
>>> +A<std::layout_left> a_left;                      // { dg-error
>> "required from" }
>>> +
>>> +B<1, std::layout_left, std::layout_left> bll;    // { 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
>>> new file mode 100644
>>> index 00000000000..8e1fd2d9524
>>> --- /dev/null
>>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
>>> @@ -0,0 +1,280 @@
>>> +// { dg-do run { target c++23 } }
>>> +#include <mdspan>
>>> +
>>> +#include <cstdint>
>>> +#include <testsuite_hooks.h>
>>> +
>>> +constexpr size_t dyn = std::dynamic_extent;
>>> +
>>> +template<typename Mapping, typename IndexType, size_t... Extents>
>>> +  constexpr void
>>> +  verify(std::extents<IndexType, Extents...> oexts)
>>> +  {
>>> +    auto m = Mapping(oexts);
>>> +    VERIFY(m.extents() == oexts);
>>> +  }
>>> +
>>> +template<typename Mapping, typename OMapping>
>>> +  requires (requires { typename OMapping::layout_type; })
>>> +  constexpr void
>>> +  verify(OMapping other)
>>> +  {
>>> +    constexpr auto rank = Mapping::extents_type::rank();
>>> +    auto m = Mapping(other);
>>> +    VERIFY(m.extents() == other.extents());
>>> +    if constexpr (rank > 0)
>>> +      for(size_t i = 0; i < rank; ++i)
>>> +     VERIFY(std::cmp_equal(m.stride(i), other.stride(i)));
>>> +  }
>>> +
>>> +
>>> +template<typename To, typename From>
>>> +  constexpr void
>>> +  verify_convertible(From from)
>>> +  {
>>> +    static_assert(std::is_convertible_v<From, To>);
>>> +    verify<To>(from);
>>> +  }
>>> +
>>> +template<typename To, typename From>
>>> +  constexpr void
>>> +  verify_nothrow_convertible(From from)
>>> +  {
>>> +    static_assert(std::is_nothrow_constructible_v<To, From>);
>>> +    verify_convertible<To>(from);
>>> +  }
>>> +
>>> +template<typename To, typename From>
>>> +  constexpr void
>>> +  verify_constructible(From from)
>>> +  {
>>> +    static_assert(!std::is_convertible_v<From, To>);
>>> +    static_assert(std::is_constructible_v<To, From>);
>>> +    verify<To>(from);
>>> +  }
>>> +
>>> +template<typename To, typename From>
>>> +  constexpr void
>>> +  verify_nothrow_constructible(From from)
>>> +  {
>>> +    static_assert(std::is_nothrow_constructible_v<To, From>);
>>> +    verify_constructible<To>(from);
>>> +  }
>>> +
>>> +template<typename Mapping, typename OExtents>
>>> +  constexpr void
>>> +  assert_not_constructible()
>>> +  {
>>> +    static_assert(!std::is_constructible_v<Mapping, OExtents>);
>>> +  }
>>> +
>>> +// ctor: mapping()
>>> +namespace default_ctor
>>> +{
>>> +  template<typename Layout, typename Extents>
>>> +    constexpr void
>>> +    test_default_ctor()
>>> +    {
>>> +      using Mapping = typename Layout::mapping<Extents>;
>>> +
>>> +      Mapping m;
>>> +      for(size_t i = 0; i < Extents::rank(); ++i)
>>> +     {
>>> +       if (Extents::static_extent(i) == std::dynamic_extent)
>>> +         VERIFY(m.extents().extent(i) == 0);
>>> +       else
>>> +         VERIFY(m.extents().static_extent(i) ==
>> Extents::static_extent(i));
>>> +     }
>>> +    }
>>> +
>>> +  template<typename Layout>
>>> +    constexpr bool
>>> +    test_default_ctor_all()
>>> +    {
>>> +      test_default_ctor<Layout, std::extents<int, dyn>>();
>>> +      test_default_ctor<Layout, std::extents<int, 1, 2>>();
>>> +      test_default_ctor<Layout, std::extents<int, dyn, 2>>();
>>> +      test_default_ctor<Layout, std::extents<int, dyn, dyn>>();
>>> +      test_default_ctor<Layout, std::extents<int, dyn, 2, dyn>>();
>>> +      test_default_ctor<Layout, std::extents<int, dyn, dyn, dyn>>();
>>> +      return true;
>>> +    }
>>> +
>>> +  template<typename Layout>
>>> +  constexpr void
>>> +  test_all()
>>> +  {
>>> +    test_default_ctor_all<Layout>();
>>> +    static_assert(test_default_ctor_all<Layout>());
>>> +  }
>>> +}
>>> +
>>> +// ctor: mapping(const extents&)
>>> +namespace from_extents
>>> +{
>>> +  template<typename Layout, typename Extents, typename OExtents>
>>> +    constexpr void
>>> +    verify_nothrow_convertible(OExtents oexts)
>>> +    {
>>> +      using Mapping = typename Layout::mapping<Extents>;
>>> +      ::verify_nothrow_convertible<Mapping>(oexts);
>>> +    }
>>> +
>>> +  template<typename Layout, typename Extents, typename OExtents>
>>> +    constexpr void
>>> +    verify_nothrow_constructible(OExtents oexts)
>>> +    {
>>> +      using Mapping = typename Layout::mapping<Extents>;
>>> +      ::verify_nothrow_constructible<Mapping>(oexts);
>>> +    }
>>> +
>>> +  template<typename Layout, typename Extents, typename OExtents>
>>> +    constexpr void
>>> +    assert_not_constructible()
>>> +    {
>>> +      using Mapping = typename Layout::mapping<Extents>;
>>> +      ::assert_not_constructible<Mapping, OExtents>();
>>> +    }
>>> +
>>> +  template<typename Layout>
>>> +    constexpr bool
>>> +    test_ctor()
>>> +    {
>>> +      verify_nothrow_convertible<Layout, std::extents<int>>(
>>> +     std::extents<int>{});
>>> +
>>> +      verify_nothrow_convertible<Layout, std::extents<int, 2>>(
>>> +     std::extents<int, 2>{});
>>> +
>>> +      verify_nothrow_convertible<Layout, std::extents<int, dyn, 3>>(
>>> +     std::extents<int, dyn, 3>{2});
>>> +
>>> +      verify_nothrow_constructible<Layout, std::extents<unsigned int>>(
>>> +     std::extents<int>{});
>>> +
>>> +      verify_nothrow_constructible<Layout, std::extents<int, dyn>>(
>>> +     std::extents<int, 2>{});
>>> +
>>> +      verify_nothrow_constructible<Layout, std::extents<int, dyn, 3>>(
>>> +     std::extents<int, 2, 3>{});
>>> +
>>> +      assert_not_constructible<Layout, std::extents<int>,
>>> +                            std::extents<unsigned int>>();
>>> +      assert_not_constructible<Layout, std::extents<int, 2>,
>>> +                            std::extents<int, dyn>>();
>>> +      assert_not_constructible<Layout, std::extents<int, 2, 3>,
>>> +                            std::extents<int, dyn, 3>>();
>>> +      return true;
>>> +    }
>>> +
>>> +  template<typename Layout, typename Extents>
>>> +    constexpr void
>>> +    assert_deducible(Extents exts)
>>> +    {
>>> +      typename Layout::mapping m(exts);
>>> +      static_assert(std::same_as<decltype(m),
>>> +                 typename Layout::mapping<Extents>>);
>>> +    }
>>> +
>>> +  template<typename Layout>
>>> +    constexpr void
>>> +    test_deducible()
>>> +    {
>>> +      assert_deducible<Layout>(std::extents<int>());
>>> +      assert_deducible<Layout>(std::extents<int, 1>());
>>> +      assert_deducible<Layout>(std::extents<int, 1, 2, dyn>(3));
>>> +    }
>>> +
>>> +  template<typename Layout>
>>> +    constexpr void
>>> +    test_all()
>>> +    {
>>> +      test_ctor<Layout>();
>>> +      static_assert(test_ctor<Layout>());
>>> +      test_deducible<Layout>();
>>> +    }
>>> +}
>>> +
>>> +// ctor: mapping(mapping<OExtents>)
>>> +namespace from_same_layout
>>> +{
>>> +  template<typename Layout, typename Extents, typename OExtents>
>>> +    constexpr void
>>> +    verify_nothrow_convertible(OExtents exts)
>>> +    {
>>> +      using Mapping = typename Layout::mapping<Extents>;
>>> +      using OMapping = typename Layout::mapping<OExtents>;
>>> +
>>> +      ::verify_nothrow_convertible<Mapping>(OMapping(exts));
>>> +    }
>>> +
>>> +  template<typename Layout, typename Extents, typename OExtents>
>>> +    constexpr void
>>> +    verify_nothrow_constructible(OExtents exts)
>>> +    {
>>> +      using Mapping = typename Layout::mapping<Extents>;
>>> +      using OMapping = typename Layout::mapping<OExtents>;
>>> +
>>> +      ::verify_nothrow_constructible<Mapping>(OMapping(exts));
>>> +    }
>>> +
>>> +  template<typename Layout>
>>> +    constexpr bool
>>> +    test_ctor()
>>> +    {
>>> +      verify_nothrow_convertible<Layout, std::extents<unsigned int>>(
>>> +     std::extents<int>{});
>>> +
>>> +      verify_nothrow_constructible<Layout, std::extents<int>>(
>>> +     std::extents<unsigned int>{});
>>> +
>>> +      assert_not_constructible<
>>> +     typename Layout::mapping<std::extents<int>>,
>>> +     typename Layout::mapping<std::extents<int, 1>>>();
>>> +
>>> +      assert_not_constructible<
>>> +     typename Layout::mapping<std::extents<int, 1>>,
>>> +     typename Layout::mapping<std::extents<int>>>();
>>> +
>>> +      verify_nothrow_constructible<Layout, std::extents<int, 1>>(
>>> +     std::extents<int, dyn>{1});
>>> +
>>> +      verify_nothrow_convertible<Layout, std::extents<int, dyn>>(
>>> +     std::extents<int, 1>{});
>>> +
>>> +      assert_not_constructible<
>>> +     typename Layout::mapping<std::extents<int, 1, 2>>,
>>> +     typename Layout::mapping<std::extents<int, 1>>>();
>>> +
>>> +      verify_nothrow_constructible<Layout, std::extents<int, 1, 2>>(
>>> +     std::extents<int, dyn, 2>{1});
>>> +
>>> +      verify_nothrow_convertible<Layout, std::extents<int, dyn, 2>>(
>>> +     std::extents<int, 1, 2>{});
>>> +      return true;
>>> +    }
>>> +
>>> +  template<typename Layout>
>>> +    constexpr void
>>> +    test_all()
>>> +    {
>>> +      test_ctor<Layout>();
>>> +      static_assert(test_ctor<Layout>());
>>> +    }
>>> +}
>>> +
>>> +template<typename Layout>
>>> +  constexpr void
>>> +  test_all()
>>> +  {
>>> +    default_ctor::test_all<Layout>();
>>> +    from_extents::test_all<Layout>();
>>> +    from_same_layout::test_all<Layout>();
>>> +  }
>>> +
>>> +int
>>> +main()
>>> +{
>>> +  test_all<std::layout_left>();
>>> +  return 0;
>>> +}
>>> diff --git
>> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc
>> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc
>>> new file mode 100644
>>> index 00000000000..10843e2a2bb
>>> --- /dev/null
>>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc
>>> @@ -0,0 +1,67 @@
>>> +// { dg-do run { target c++23 } }
>>> +#include <mdspan>
>>> +
>>> +#include <cstdint>
>>> +#include <testsuite_hooks.h>
>>> +
>>> +constexpr size_t dyn = std::dynamic_extent;
>>> +
>>> +template<typename Mapping>
>>> +  constexpr void
>>> +  invoke_stride(Mapping m)
>>> +  {
>>> +    // Only checking for UB, e.g. signed overflow.
>>> +    for(size_t i = 0; i < Mapping::extents_type::rank(); ++i)
>>> +      m.stride(i);
>>> +  }
>>> +
>>> +template<typename Mapping>
>>> +  constexpr void
>>> +  verify_required_span_size(Mapping m)
>>> +  { VERIFY(m.required_span_size() == 0); }
>>> +
>>> +template<typename Mapping>
>>> +  constexpr void
>>> +  verify_all(Mapping m)
>>> +  {
>>> +    verify_required_span_size(m);
>>> +    invoke_stride(m);
>>> +  }
>>> +
>>> +template<typename Layout, typename Int>
>>> +constexpr void
>>> +test_default_constructed()
>>> +{
>>> +  constexpr auto n = std::numeric_limits<Int>::max();
>>> +  verify_all(typename Layout::mapping<std::extents<Int, n, n, 0, n,
>> n>>{});
>>> +  verify_all(typename Layout::mapping<std::extents<Int, 0, n, n, n>>{});
>>> +  verify_all(typename Layout::mapping<std::extents<Int, dyn, n, n,
>> n>>{});
>>> +  verify_all(typename Layout::mapping<std::extents<Int, n, n, n, 0>>{});
>>> +  verify_all(typename Layout::mapping<std::extents<Int, n, n, n,
>> dyn>>{});
>>> +}
>>
>> The problem here are cases where
>>
>>     sizeof(Int) >= sizeof(size_t)
>>
>> and it comes in two variations:
>>
>>     1. 32-bit systems: failure due to narrowing conversion from `long long
>>        int` to size_t, which is the bug that the CI found.
>>
> 
>>     2. any system: If `sizeof(Int) == sizeof(size_t) && is_unsigned_v<Int>`
>>        then `n == std::dynamic_extents` and the test is ineffective.
>>
>> Proposed solution:
>>
>>     constexpr Int n1 = std::numeric_limits<Int>::max();
>>     constexpr size_t n2 = std::numeric_limits<size_t>::max() - 1;
>>     constexpr size_t n = std::cmp_less(n1, n2) ? size_t(n1) : n2;
>>
> And I would replace: std::numeric_limits<size_t>::max() - 1 with
> dynamic_extent-1.
> This would make it more clear for the reader. You can also add a
> constructor.
>>
>>
>> -------------------------------------------------------------------------
>>
>> The tests are also insufficient because they don't cover cases where
>> the overflow could happen while multiplying dynamic extents.
>>
> In which code path that multiplication will happen? I think it will always
> trigger
> the preconditions in the constructor.
> 

Look at the extents, they're all either:
   * fully static with a 0,
   * or have one dyn which is 0.

What we don't check is the case where we have 4 dynamic extents,
one is 0 and the rest are huge, e.g.

   layout_left::mapping(std::dextents<Int, 4>(n, n, n, 0));

This matters because we split __exts_prod as follow:
   __exts_prod = __sta_prod * __dyn_prod

(The notation isn't very precise, but is gets to the core.)

We've carefully checked that there's no overflow in computing
__sta_prod but not __dyn_prod.

The way this could cause issues is via m.stride(i) (for layout_left
and layout_right) or maybe in `required_span_size()` (if one doesn't
prescan for 0).

Remember the cases in empty.cc don't trigger the precondition, because
the required span size is 0.

>>
>>> +
>>> +template<typename Layout>
>>> +constexpr bool
>>> +test_all()
>>> +{
>>> +  test_default_constructed<Layout, signed char>();
>>> +  test_default_constructed<Layout, short int>();
>>> +  test_default_constructed<Layout, int>();
>>> +  test_default_constructed<Layout, long int>();
>>> +  test_default_constructed<Layout, long long int>();
>>> +
>>> +  test_default_constructed<Layout, unsigned char>();
>>> +  test_default_constructed<Layout, unsigned short int>();
>>> +  test_default_constructed<Layout, unsigned int>();
>>> +  test_default_constructed<Layout, unsigned long int>();
>>> +  test_default_constructed<Layout, unsigned long long int>();
>>> +  test_default_constructed<Layout, size_t>();
>>> +  return true;
>>> +}
>>> +
>>> +int
>>> +main()
>>> +{
>>> +  static_assert(test_all<std::layout_left>());
>>> +  return 0;
>>> +}
>>> diff --git
>> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
>> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
>>> new file mode 100644
>>> index 00000000000..a5be1166617
>>> --- /dev/null
>>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
>>> @@ -0,0 +1,437 @@
>>> +// { dg-do run { target c++23 } }
>>> +#include <mdspan>
>>> +
>>> +#include <cstdint>
>>> +#include <testsuite_hooks.h>
>>> +
>>> +constexpr size_t dyn = std::dynamic_extent;
>>> +
>>> +template<typename Layout, typename Extents>
>>> +  constexpr bool
>>> +  test_mapping_properties()
>>> +  {
>>> +    using M = typename Layout::mapping<Extents>;
>>> +    static_assert(std::__mdspan::__is_extents<typename
>> M::extents_type>);
>>> +    static_assert(std::copyable<M>);
>>> +    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(std::is_same_v<typename M::extents_type, Extents>);
>>> +    static_assert(std::is_same_v<typename M::index_type,
>>> +                              typename M::extents_type::index_type>);
>>> +    static_assert(std::is_same_v<typename M::size_type,
>>> +                              typename M::extents_type::size_type>);
>>> +    static_assert(std::is_same_v<typename M::rank_type,
>>> +                              typename M::extents_type::rank_type>);
>>> +    static_assert(std::is_same_v<typename M::layout_type, Layout>);
>>> +
>>> +    static_assert(std::is_trivially_copyable_v<M>);
>>> +    static_assert(std::regular<M>);
>>> +
>>> +    static_assert(M::is_always_unique() && M::is_unique());
>>> +    static_assert(M::is_always_strided() && M::is_strided());
>>> +    return true;
>>> +  }
>>> +
>>> +template<typename Layout>
>>> +  constexpr bool
>>> +  test_mapping_properties_all()
>>> +  {
>>> +    test_mapping_properties<Layout, std::extents<int>>();
>>> +    test_mapping_properties<Layout, std::extents<int, 1>>();
>>> +    test_mapping_properties<Layout, std::extents<int, dyn>>();
>>> +    test_mapping_properties<Layout, std::extents<int, dyn, dyn>>();
>>> +    return true;
>>> +  }
>>> +
>>> +// Check operator()(Indices...)
>>> +template<typename Mapping, size_t N>
>>> +  constexpr typename Mapping::index_type
>>> +  linear_index(const Mapping& mapping,
>>> +            const std::array<typename Mapping::index_type, N>& indices)
>>> +  {
>>> +    typename Mapping::index_type ret = 0;
>>> +    for(size_t r = 0; r < indices.size(); ++r)
>>> +      ret += indices[r] * mapping.stride(r);
>>> +    return ret;
>>> +  }
>>> +
>>> +template<typename Mapping, typename... Indices>
>>> +  constexpr void
>>> +  test_linear_index(const Mapping& m, Indices... i)
>>> +  {
>>> +    using index_type = typename Mapping::index_type;
>>> +    index_type expected = linear_index(m, std::array{index_type(i)...});
>>> +    VERIFY(m(i...) == expected);
>>> +    VERIFY(m(uint8_t(i)...) == expected);
>>> +  }
>>> +
>>> +template<typename Layout>
>>> +  constexpr void
>>> +  test_linear_index_0d()
>>> +  {
>>> +    constexpr typename Layout::mapping<std::extents<int>> m;
>>> +    VERIFY(m() == 0);
>>> +  }
>>> +
>>> +template<typename Layout>
>>> +  constexpr void
>>> +  test_linear_index_1d()
>>> +  {
>>> +    typename Layout::mapping<std::extents<int, 5>> m;
>>> +    test_linear_index(m, 0);
>>> +    test_linear_index(m, 1);
>>> +    test_linear_index(m, 4);
>>> +  }
>>> +
>>> +template<typename Layout>
>>> +  constexpr void
>>> +  test_linear_index_2d()
>>> +  {
>>> +    typename Layout::mapping<std::extents<int, 3, 256>> m;
>>> +    test_linear_index(m, 0, 0);
>>> +    test_linear_index(m, 1, 0);
>>> +    test_linear_index(m, 0, 1);
>>> +    test_linear_index(m, 1, 1);
>>> +    test_linear_index(m, 2, 4);
>>> +  }
>>> +
>>> +template<typename Layout>
>>> +  struct MappingFactory
>>> +  {
>>> +    template<typename Extents>
>>> +      static constexpr typename Layout::mapping<Extents>
>>> +      create(Extents exts)
>>> +      { return exts; }
>>> +  };
>>> +
>>> +template<typename Layout>
>>> +  constexpr void
>>> +  test_linear_index_3d()
>>> +  {
>>> +    auto m = MappingFactory<Layout>::create(std::extents(3, 5, 7));
>>> +    test_linear_index(m, 0, 0, 0);
>>> +    test_linear_index(m, 1, 0, 0);
>>> +    test_linear_index(m, 0, 1, 0);
>>> +    test_linear_index(m, 0, 0, 1);
>>> +    test_linear_index(m, 1, 1, 0);
>>> +    test_linear_index(m, 2, 4, 6);
>>> +  }
>>> +
>>> +struct IntLikeA
>>> +{
>>> +  operator int()
>>> +  { return 0; }
>>> +};
>>> +
>>> +struct IntLikeB
>>> +{
>>> +  operator int() noexcept
>>> +  { return 0; }
>>> +};
>>> +
>>> +struct NotIntLike
>>> +{ };
>>> +
>>> +template<typename Layout>
>>> +  constexpr void
>>> +  test_has_linear_index_0d()
>>> +  {
>>> +    using Mapping = typename Layout::mapping<std::extents<int>>;
>>> +    static_assert(std::invocable<Mapping>);
>>> +    static_assert(!std::invocable<Mapping, int>);
>>> +    static_assert(!std::invocable<Mapping, IntLikeA>);
>>> +    static_assert(!std::invocable<Mapping, IntLikeB>);
>>> +    static_assert(!std::invocable<Mapping, NotIntLike>);
>>> +  }
>>> +
>>> +template<typename Layout>
>>> +  constexpr void
>>> +  test_has_linear_index_1d()
>>> +  {
>>> +    using Mapping = typename Layout::mapping<std::extents<int, 3>>;
>>> +    static_assert(std::invocable<Mapping, int>);
>>> +    static_assert(!std::invocable<Mapping>);
>>> +    static_assert(!std::invocable<Mapping, IntLikeA>);
>>> +    static_assert(std::invocable<Mapping, IntLikeB>);
>>> +    static_assert(!std::invocable<Mapping, NotIntLike>);
>>> +    static_assert(std::invocable<Mapping, double>);
>>> +  }
>>> +
>>> +template<typename Layout>
>>> +  constexpr void
>>> +  test_has_linear_index_2d()
>>> +  {
>>> +    using Mapping = typename Layout::mapping<std::extents<int, 3, 5>>;
>>> +    static_assert(std::invocable<Mapping, int, int>);
>>> +    static_assert(!std::invocable<Mapping, int>);
>>> +    static_assert(!std::invocable<Mapping, IntLikeA, int>);
>>> +    static_assert(std::invocable<Mapping, IntLikeB, int>);
>>> +    static_assert(!std::invocable<Mapping, NotIntLike, int>);
>>> +    static_assert(std::invocable<Mapping, double, double>);
>>> +  }
>>> +
>>> +template<typename Layout>
>>> +  constexpr bool
>>> +  test_linear_index_all()
>>> +  {
>>> +    test_linear_index_0d<Layout>();
>>> +    test_linear_index_1d<Layout>();
>>> +    test_linear_index_2d<Layout>();
>>> +    test_linear_index_3d<Layout>();
>>> +    test_has_linear_index_0d<Layout>();
>>> +    test_has_linear_index_1d<Layout>();
>>> +    test_has_linear_index_2d<Layout>();
>>> +    return true;
>>> +  }
>>> +
>>> +template<typename Mapping>
>>> +  constexpr typename Mapping::index_type
>>> +  linear_index_end(Mapping m)
>>> +  {
>>> +    using index_type = typename Mapping::index_type;
>>> +    constexpr size_t rank = Mapping::extents_type::rank();
>>> +
>>> +    auto impl = [m]<index_type... Counts>(
>>> +     std::integer_sequence<index_type, Counts...>) -> index_type
>>> +      {
>>> +     auto exts = m.extents();
>>> +     if(((exts.extent(Counts) == 0) || ...))
>>> +       return 0;
>>> +     return m((exts.extent(Counts) - 1)...) + 1;
>>> +      };
>>> +
>>> +    return impl(std::make_integer_sequence<index_type, rank>());
>>> +  }
>>> +
>>> +// Check required_span_size
>>> +template<typename Mapping>
>>> +  constexpr void
>>> +  test_required_span_size(Mapping m)
>>> +  { VERIFY(m.required_span_size() == linear_index_end(m)); }
>>> +
>>> +template<typename Layout>
>>> +  constexpr void
>>> +  test_required_span_size_0d()
>>> +  {
>>> +    typename Layout::mapping<std::extents<int>> m;
>>> +    test_required_span_size(m);
>>> +  }
>>> +
>>> +template<typename Layout>
>>> +  constexpr void
>>> +  test_required_span_size_1d()
>>> +  {
>>> +    auto m = MappingFactory<Layout>::create(std::extents(3));
>>> +    test_required_span_size(m);
>>> +  }
>>> +
>>> +template<typename Layout>
>>> +  constexpr void
>>> +  test_required_span_size_2d()
>>> +  {
>>> +    auto m = MappingFactory<Layout>::create(std::extents(3, 5));
>>> +    test_required_span_size(m);
>>> +  }
>>> +
>>> +template<typename Layout>
>>> +  constexpr void
>>> +  test_required_span_size_3d()
>>> +  {
>>> +    auto m = MappingFactory<Layout>::create(std::extents(3, 5, 7));
>>> +    test_required_span_size(m);
>>> +  }
>>> +
>>> +template<typename Layout>
>>> +  constexpr void
>>> +  test_required_span_size_zero_1d()
>>> +  {
>>> +    auto m = MappingFactory<Layout>::create(std::extents(3, 0));
>>> +    test_required_span_size(m);
>>> +  }
>>> +
>>> +template<typename Layout>
>>> +  constexpr void
>>> +  test_required_span_size_zero_3d()
>>> +  {
>>> +    auto m = MappingFactory<Layout>::create(std::extents(3, 0, 7));
>>> +    test_required_span_size(m);
>>> +  }
>>> +
>>> +template<typename Layout>
>>> +  constexpr bool
>>> +  test_required_span_size_all()
>>> +  {
>>> +    test_required_span_size_0d<Layout>();
>>> +    test_required_span_size_1d<Layout>();
>>> +    test_required_span_size_2d<Layout>();
>>> +    test_required_span_size_3d<Layout>();
>>> +    test_required_span_size_zero_1d<Layout>();
>>> +    test_required_span_size_zero_3d<Layout>();
>>> +    return true;
>>> +  }
>>> +
>>> +// Check stride
>>> +template<typename Layout>
>>> +  constexpr void
>>> +  test_stride_1d()
>>> +  {
>>> +    std::layout_left::mapping<std::extents<int, 3>> m;
>>> +    VERIFY(m.stride(0) == 1);
>>> +  }
>>> +
>>> +template<typename Layout>
>>> +  constexpr void
>>> +  test_stride_2d();
>>> +
>>> +template<>
>>> +  constexpr void
>>> +  test_stride_2d<std::layout_left>()
>>> +  {
>>> +    std::layout_left::mapping<std::extents<int, 3, 5>> m;
>>> +    VERIFY(m.stride(0) == 1);
>>> +    VERIFY(m.stride(1) == 3);
>>> +  }
>>> +
>>> +template<typename Layout>
>>> +  constexpr void
>>> +  test_stride_3d();
>>> +
>>> +template<>
>>> +  constexpr void
>>> +  test_stride_3d<std::layout_left>()
>>> +  {
>>> +    std::layout_left::mapping m(std::dextents<int, 3>(3, 5, 7));
>>> +    VERIFY(m.stride(0) == 1);
>>> +    VERIFY(m.stride(1) == 3);
>>> +    VERIFY(m.stride(2) == 3*5);
>>> +  }
>>> +
>>> +template<typename Layout>
>>> +  constexpr bool
>>> +  test_stride_all()
>>> +  {
>>> +    test_stride_1d<Layout>();
>>> +    test_stride_2d<Layout>();
>>> +    test_stride_3d<Layout>();
>>> +    return true;
>>> +  }
>>> +
>>> +template<typename Mapping>
>>> +  concept has_stride = requires (Mapping m)
>>> +  {
>>> +    { m.stride(0) } -> std::same_as<typename Mapping::index_type>;
>>> +  };
>>> +
>>> +template<typename Layout>
>>> +  constexpr void
>>> +  test_has_stride_0d()
>>> +  {
>>> +    using Mapping = typename Layout::mapping<std::extents<int>>;
>>> +    constexpr bool expected = false;
>>> +    static_assert(has_stride<Mapping> == expected);
>>> +  }
>>> +
>>> +template<typename Layout>
>>> +  constexpr void
>>> +  test_has_stride_1d()
>>> +  { static_assert(has_stride<typename Layout::mapping<std::extents<int,
>> 1>>>); }
>>> +
>>> +template<typename Layout>
>>> +  constexpr void
>>> +  test_has_stride_2d()
>>> +  {
>>> +    using Extents = std::extents<int, 1, 2>;
>>> +    static_assert(has_stride<typename Layout::mapping<Extents>>);
>>> +  }
>>> +
>>> +// Check operator==
>>> +template<typename Layout>
>>> +  constexpr void
>>> +  test_eq()
>>> +  {
>>> +    typename Layout::mapping<std::extents<int, 1, 2>> m1;
>>> +    typename Layout::mapping<std::extents<int, 2, 2>> m2;
>>> +    typename Layout::mapping<std::dextents<int, 2>> m3(m1);
>>> +
>>> +    VERIFY(m1 == m1);
>>> +    VERIFY(m1 != m2);
>>> +    VERIFY(m1 == m3);
>>> +    VERIFY(m2 != m3);
>>> +  }
>>> +
>>> +template<typename Layout>
>>> +  constexpr void
>>> +  test_eq_zero()
>>> +  {
>>> +    typename Layout::mapping<std::extents<int, 0, 2>> m1;
>>> +    typename Layout::mapping<std::extents<int, 0, 2>> m2;
>>> +    typename Layout::mapping<std::extents<int, 2, 0>> m3;
>>> +
>>> +    VERIFY(m1 == m2);
>>> +    VERIFY(m1 != m3);
>>> +  }
>>> +
>>> +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>;
>>> +  };
>>> +
>>> +template<typename Layout>
>>> +  constexpr bool
>>> +  test_has_op_eq()
>>> +  {
>>> +    static_assert(!has_op_eq<
>>> +     typename Layout::mapping<std::extents<int, 1, 2>>,
>>> +     typename Layout::mapping<std::extents<int, 1>>>);
>>> +
>>> +    static_assert(has_op_eq<
>>> +     typename Layout::mapping<std::extents<int, 1>>,
>>> +     typename Layout::mapping<std::extents<int, 1>>>);
>>> +
>>> +    static_assert(has_op_eq<
>>> +     typename Layout::mapping<std::extents<int, 1>>,
>>> +     typename Layout::mapping<std::extents<int, 2>>>);
>>> +    return true;
>>> +  }
>>> +
>>> +template<typename Layout>
>>> +  constexpr bool
>>> +  test_mapping_all()
>>> +  {
>>> +    test_linear_index_all<Layout>();
>>> +    test_required_span_size_all<Layout>();
>>> +    test_stride_all<Layout>();
>>> +
>>> +    test_eq<Layout>();
>>> +    test_eq_zero<Layout>();
>>> +    return true;
>>> +  }
>>> +
>>> +template<typename Layout>
>>> +  constexpr void
>>> +  test_all()
>>> +  {
>>> +    static_assert(std::is_trivially_default_constructible_v<Layout>);
>>> +    static_assert(std::is_trivially_copyable_v<Layout>);
>>> +    static_assert(test_mapping_properties_all<Layout>());
>>> +
>>> +    test_mapping_all<Layout>();
>>> +    static_assert(test_mapping_all<Layout>());
>>> +
>>> +    test_has_stride_0d<Layout>();
>>> +    test_has_stride_1d<Layout>();
>>> +    test_has_stride_2d<Layout>();
>>> +    test_has_op_eq<Layout>();
>>> +  }
>>> +
>>> +int
>>> +main()
>>> +{
>>> +  test_all<std::layout_left>();
>>> +  return 0;
>>> +}
>>
>>
> 



More information about the Libstdc++ mailing list