On Tue, Sep 2, 2025 at 1:28 PM Luc Grosheintz <[email protected]>
wrote:

>
>
> On 9/2/25 10:21, Tomasz Kaminski wrote:
> > On Tue, Sep 2, 2025 at 10:15 AM Luc Grosheintz <[email protected]
> >
> > wrote:
> >
> >> A usecase for P2781R9 is more ergonomic creation of span and mdspan with
> >> mixed static and dynamic extents, e.g.:
> >>
> >>      span(ptr, cw<3>)
> >>      extents(cw<3>, 5, cw<7>)
> >>      mdspan(ptr, cw<3>, 5, cw<7>)
> >>
> >> should be deduced as:
> >>      span<..., 3>
> >>      extents<..., 3, dyn, 7>
> >>      mdspan<..., extents<..., 3, dyn, 7>>
> >>
> >> The change required is to strip cv-qualifiers and references from
> >> `_Tp::value`, because of:
> >>
> >>      template<_CwFixedValue _X, typename>
> >>        struct constant_wrapper : _CwOperators
> >>        {
> >>          static constexpr const auto& value = _X._M_data;
> >>
> >> libstdc++-v3/ChangeLog:
> >>
> >>          * include/std/span (__integral_constant_like): Allow the member
> >>          `value` of a constant wrapping type to be a const reference of
> >>          an integer.
> >>          * testsuite/23_containers/mdspan/extents/misc.cc: Add test for
> >>          cw and constant_wrapper.
> >>          * testsuite/23_containers/mdspan/mdspan.cc: Ditto.
> >>          * testsuite/23_containers/span/deduction.cc: Ditto.
> >>
> >> Signed-off-by: Luc Grosheintz <[email protected]>
> >> ---
> >>   libstdc++-v3/include/std/span                 |  3 ++-
> >>   .../23_containers/mdspan/extents/misc.cc      | 20 +++++++++++++------
> >>   .../testsuite/23_containers/mdspan/mdspan.cc  | 20 ++++++++++++-------
> >>   .../testsuite/23_containers/span/deduction.cc | 10 ++++++++++
> >>   4 files changed, 39 insertions(+), 14 deletions(-)
> >>
> >> diff --git a/libstdc++-v3/include/std/span
> b/libstdc++-v3/include/std/span
> >> index 44f9b36a7ef..f9aa3c77e8e 100644
> >> --- a/libstdc++-v3/include/std/span
> >> +++ b/libstdc++-v3/include/std/span
> >> @@ -480,7 +480,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>     namespace __detail
> >>     {
> >>       template<typename _Tp>
> >> -      concept __integral_constant_like =
> >> is_integral_v<decltype(_Tp::value)>
> >> +      concept __integral_constant_like =
> >> +       is_integral_v<remove_cvref_t<decltype(_Tp::value)>>
> >>          && !is_same_v<bool, remove_const_t<decltype(_Tp::value)>>
> >>
> > On the last LWG telecon  we have noticed, that this was not updated to
> > remove_cvref_t above,
> > and in consequence I believe, that:
> > span(ptr, true_type{}); // does not work, it is not integral constant
> like
> > span(ptr, cw<true>); // works,
> >
> > Could you add test like above, and let me know what are the result, so I
> > can report back?
>
> Let's look at:
>
>    std::span s16(a.data(), std::true_type{});
>    std::span s17(a.data(), std::cw<true>);
>    std::span s18(a.data(), true);
>
> Quick reminder that `true_type` and `constant_wrapper` implicitly
> convert to the underlying type, i.e. to bool.
>
> 1. Prior to any changes to the CTADs, s16 and s18, both compile and result
> in dynamically sized spans:
>
> https://godbolt.org/z/qeos99aMh
>
> 2. As per current wording of the standard and as implemented in v2 of this
> patch:
>
>     - s16 has dynamic extent.
>     - s17 has static extent `1`.
>     - s18 has dynamic extent.
>
> 3. As per your suggestion:
>
>      template<typename _Tp>
>        concept __integral_constant_like =
>         is_integral_v<remove_cvref_t<decltype(_Tp::value)>>
>         && !is_same_v<bool, remove_cvref_t<decltype(_Tp::value)>>
>         && convertible_to<_Tp, decltype(_Tp::value)>
>         && equality_comparable_with<_Tp, decltype(_Tp::value)>
>         && bool_constant<_Tp() == _Tp::value>::value
>         && bool_constant<static_cast<decltype(_Tp::value)>(_Tp()) ==
> _Tp::value>
>              ::value;
>
>      All three cases are deduced as having a dynamic extent.
>
> Could you please expand a little on what "works" means? Because the code
> seems to quite explicitly not consider "wrapped booleans" as integer
> constants, i.e.
>
>         && !is_same_v<bool, remove_cvref_t<decltype(_Tp::value)>>
>
> What's not clear to is if this is intentional, or if it's an attempt at
> rejecting unrelated traits:
>
Yes, this is an attempt to reject unrelated traits.

>
>     template<typename T>
>     class is_nice
>     {
>       constexpr static bool value = true;
>     };
>
> (There's many `std::is_*` that have a static member `value` and I wouldn't
> consider those "integer constants".)
>
> Let me know if you need anything else.
>
No, that's all, thank you very much for your response.

>
> > Do not send an v3 yet, until I will finish review.
> >
> >>          && convertible_to<_Tp, decltype(_Tp::value)>
> >>          && equality_comparable_with<_Tp, decltype(_Tp::value)>
> >> diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/extents/misc.cc
> >> b/libstdc++-v3/testsuite/23_containers/mdspan/extents/misc.cc
> >> index 8a43a682004..018c4f95930 100644
> >> --- a/libstdc++-v3/testsuite/23_containers/mdspan/extents/misc.cc
> >> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/misc.cc
> >> @@ -98,7 +98,7 @@ test_deduction(Extents... exts)
> >>   }
> >>
> >>   constexpr bool
> >> -test_integral_constant_deduction()
> >> +test_deduction_from_constant()
> >>   {
> >>     auto verify = [](auto actual, auto expected)
> >>       {
> >> @@ -106,13 +106,21 @@ test_integral_constant_deduction()
> >>         VERIFY(actual == expected);
> >>       };
> >>
> >> -  constexpr auto c1 = std::integral_constant<size_t, 1>{};
> >> -  constexpr auto c2 = std::integral_constant<int, 2>{};
> >> +  constexpr auto i1 = std::integral_constant<size_t, 1>{};
> >> +  constexpr auto i2 = std::integral_constant<int, 2>{};
> >>
> >>     verify(std::extents(1), std::extents<size_t, dyn>{1});
> >> -  verify(std::extents(c1), std::extents<size_t, 1>{});
> >> +  verify(std::extents(i1), std::extents<size_t, 1>{});
> >> +  verify(std::extents(i2), std::extents<size_t, 2>{});
> >> +  verify(std::extents(i1, 2), std::extents<size_t, 1, dyn>{2});
> >> +
> >> +#if __glibcxx_constant_wrapper
> >> +  constexpr auto c2 = std::constant_wrapper<2>{};
> >>     verify(std::extents(c2), std::extents<size_t, 2>{});
> >> -  verify(std::extents(c1, 2), std::extents<size_t, 1, dyn>{2});
> >> +  verify(std::extents(1, c2), std::extents<size_t, dyn, 2>{1});
> >> +  verify(std::extents(std::cw<2>), std::extents<size_t, 2>{});
> >> +  verify(std::extents(1, std::cw<2>), std::extents<size_t, dyn, 2>{1});
> >> +#endif
> >>     return true;
> >>   }
> >>
> >> @@ -123,7 +131,7 @@ test_deduction_all()
> >>     test_deduction<1>(1);
> >>     test_deduction<2>(1.0, 2.0f);
> >>     test_deduction<3>(int(1), short(2), size_t(3));
> >> -  test_integral_constant_deduction();
> >> +  test_deduction_from_constant();
> >>     return true;
> >>   }
> >>
> >> diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
> >> b/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
> >> index bdfc6ebcf56..2583047413c 100644
> >> --- a/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
> >> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
> >> @@ -280,7 +280,7 @@ test_from_pointer_and_shape()
> >>   }
> >>
> >>   constexpr bool
> >> -test_from_pointer_and_integral_constant()
> >> +test_from_pointer_and_constant()
> >>   {
> >>     std::array<double, 6> buffer{};
> >>     double * ptr = buffer.data();
> >> @@ -292,12 +292,18 @@ test_from_pointer_and_integral_constant()
> >>         VERIFY(actual.extents() == expected.extents());
> >>       };
> >>
> >> -  auto c3 = std::integral_constant<int, 3>{};
> >> -  auto c6 = std::integral_constant<int, 6>{};
> >> +  auto i3 = std::integral_constant<int, 3>{};
> >> +  auto i6 = std::integral_constant<int, 6>{};
> >>
> >>     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));
> >> +  verify(std::mdspan(ptr, i6), std::extents(i6));
> >> +  verify(std::mdspan(ptr, 2, i3), std::extents(2, i3));
> >> +
> >> +#if __glibcxx_constant_wrapper
> >> +  auto c3 = std::constant_wrapper<3>{};
> >> +  verify(std::mdspan(ptr, 2, c3), std::extents(2, i3));
> >> +  verify(std::mdspan(ptr, 2, std::cw<3>), std::extents(2, i3));
> >> +#endif
> >>     return true;
> >>   }
> >>
> >> @@ -729,8 +735,8 @@ main()
> >>     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_pointer_and_constant();
> >> +  static_assert(test_from_pointer_and_constant());
> >>
> >>     test_from_extents();
> >>     static_assert(test_from_extents());
> >> diff --git a/libstdc++-v3/testsuite/23_containers/span/deduction.cc
> >> b/libstdc++-v3/testsuite/23_containers/span/deduction.cc
> >> index c66db90222e..0d045505bed 100644
> >> --- a/libstdc++-v3/testsuite/23_containers/span/deduction.cc
> >> +++ b/libstdc++-v3/testsuite/23_containers/span/deduction.cc
> >> @@ -83,4 +83,14 @@ test01()
> >>
> >>     std::span s13(a.data(), std::integral_constant<size_t, 3>{});
> >>     static_assert( is_static_span<long, 3>(s13));
> >> +
> >> +#if __glibcxx_constant_wrapper
> >> +  auto c5 = std::constant_wrapper<5>{};
> >> +
> >> +  std::span s14(a.data(), std::cw<4>);
> >> +  static_assert( is_static_span<long, 4>(s14));
> >> +
> >> +  std::span s15(a.data(), c5);
> >> +  static_assert( is_static_span<long, 5>(s15));
> >> +#endif
> >>   }
> >> --
> >> 2.51.0
> >>
> >>
> >
>
>

Reply via email to