From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 621453858D33 for ; Wed, 12 Apr 2023 17:42:36 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 621453858D33 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1681321356; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=VxD7Rwxq5Eci5shqedbqMCFQ7FMZ9p77nWpuEEY6BgQ=; b=JIT+Cwz49arprkYpx2CtmgXm31iVG8pYye0CAFVELYUgjDfXYypUSb6f7yBOFaG+C25drj Mv89FMJ+BTO8RpVwoJ27Y9Rf1+cvwGHYjuPiZTy1kCyTMKfDuDW7b6/fNgOJS5ebS5Ufkf CtZsyTM8GzrBkb/CTA3EfNfUW/0DOx4= Received: from mail-ej1-f70.google.com (mail-ej1-f70.google.com [209.85.218.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-211-ZQ_-PxPCMUqPax2CdlyKvg-1; Wed, 12 Apr 2023 13:42:34 -0400 X-MC-Unique: ZQ_-PxPCMUqPax2CdlyKvg-1 Received: by mail-ej1-f70.google.com with SMTP id ja8-20020a170907988800b0094aa0655f0eso3802077ejc.18 for ; Wed, 12 Apr 2023 10:42:34 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681321353; x=1683913353; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=VxD7Rwxq5Eci5shqedbqMCFQ7FMZ9p77nWpuEEY6BgQ=; b=UTxl8j37LubCa465KqlnVLLHLe7hIqZ3tO59mz1Ey0lINoQ4Kea7/ZgzFPWClyW48q iGHQ9IE5xaYr8AnoQtLAFe0lFTn4sfzubgJLC0ygsZLOrfdzk8iIPiLpI9elWpyhl2gj 2LrQH/ASQfZSwRlrdqkY3u/3OR0HjfrxkeGatr4gZD54zrN9Rz60Qm1DXL8APHMzeQyr Cox8dIGxApFDrXO8ycqNb/N808323hI5PYQY2d2ISW/ZGqoJVttC/M10X+S76iVx4Tvp A+ofb3tPx5aUI5EgnnlWG4ZvA1Qab8y5LZ2JcHi9XzA6TtddzaZaX8bkfJALBTOyFRyr JJ9w== X-Gm-Message-State: AAQBX9fD0C6AvEWaI08vSgKTMOCGDnD+M/2r6cewRS3l0o5XYogGGFNu cRms7uriTlBSgL6Zgtfdeq9aSWFYrjmUpfkAHo9L+Hym7Z3FA9eAjCI5CZ8HASPz7fEA3Huhmew WNxtEXTHqfTpvqfrKknHKfqLoBADNSXchdHjgdDQKiA== X-Received: by 2002:a17:906:65d7:b0:931:8502:ad02 with SMTP id z23-20020a17090665d700b009318502ad02mr1726856ejn.13.1681321353446; Wed, 12 Apr 2023 10:42:33 -0700 (PDT) X-Google-Smtp-Source: AKy350YgEQI4BZxNPAKQl7BrYGVlVIEDF/3eBtV1OuchA5tIsLvJxRYHpjQoTYB20qpb4CwNa0nkTDUWNInIRrfjrlg= X-Received: by 2002:a17:906:65d7:b0:931:8502:ad02 with SMTP id z23-20020a17090665d700b009318502ad02mr1726846ejn.13.1681321353043; Wed, 12 Apr 2023 10:42:33 -0700 (PDT) MIME-Version: 1.0 References: <20230411145838.2862361-1-ppalka@redhat.com> In-Reply-To: From: Patrick Palka Date: Wed, 12 Apr 2023 13:42:22 -0400 Message-ID: Subject: Re: [PATCH] libstdc++: Implement ranges::enumerate_view from P2164R9 To: Jonathan Wakely Cc: gcc-patches@gcc.gnu.org, libstdc++@gcc.gnu.org X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable X-Spam-Status: No, score=-13.3 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,KAM_NUMSUBJECT,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE,TXREP autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: On Tue, Apr 11, 2023 at 11:12=E2=80=AFAM Jonathan Wakely wrote: > > On Tue, 11 Apr 2023 at 15:59, Patrick Palka via Libstdc++ > wrote: > > > > Tested on x86_64-pc-linux-gnu, does this look OK for trunk perhaps? > > Yes, this is only for C++23 so OK for trunk now. Yay thanks, pushed as r13-7161-g0f3b4d38d4bad8. > > The auto(x) uses mean this won't work with older versions of Clang, > but that's OK. I already introduced that dependency into > basic_string::resize_for_overwrite, and it just means users of older > Clang versions can't use some C++23 features. They can still use C++20 > and lower. Ah, sounds good. It seems our is already unusable before Clang 16 even in C++20 mode anyway: https://godbolt.org/z/Ebob8naMo > > > > > libstdc++-v3/ChangeLog: > > > > * include/std/ranges (__cpp_lib_ranges_enumerate): Define > > for C++23. > > (__detail::__range_with_movable_reference): Likewise. > > (enumerate_view): Likewise. > > (enumerate_view::_Iterator): Likewise. > > (enumerate_view::_Sentinel): Likewise. > > * include/std/version (__cpp_lib_ranges_enumerate): Likewise. > > * testsuite/std/ranges/version_c++23.cc: Verify value of > > __cpp_lib_ranges_enumerate. > > * testsuite/std/ranges/adaptors/enumerate/1.cc: New test. > > --- > > libstdc++-v3/include/std/ranges | 303 ++++++++++++++++++ > > libstdc++-v3/include/std/version | 1 + > > .../std/ranges/adaptors/enumerate/1.cc | 102 ++++++ > > .../testsuite/std/ranges/version_c++23.cc | 4 + > > 4 files changed, 410 insertions(+) > > create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/enumerat= e/1.cc > > > > diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std= /ranges > > index 14754c125ff..be71c370eb7 100644 > > --- a/libstdc++-v3/include/std/ranges > > +++ b/libstdc++-v3/include/std/ranges > > @@ -8732,6 +8732,309 @@ namespace views::__adaptor > > > > inline constexpr _AsConst as_const; > > } > > + > > +#define __cpp_lib_ranges_enumerate 202302L > > + > > + namespace __detail > > + { > > + template > > + concept __range_with_movable_reference =3D input_range<_Range> > > + && move_constructible> > > + && move_constructible>; > > + } > > + > > + template > > + requires __detail::__range_with_movable_reference<_Vp> > > + class enumerate_view : public view_interface> > > + { > > + _Vp _M_base =3D _Vp(); > > + > > + template class _Iterator; > > + template class _Sentinel; > > + > > + public: > > + enumerate_view() requires default_initializable<_Vp> =3D default; > > + > > + constexpr explicit > > + enumerate_view(_Vp __base) > > + : _M_base(std::move(__base)) > > + { } > > + > > + constexpr auto > > + begin() requires (!__detail::__simple_view<_Vp>) > > + { return _Iterator(ranges::begin(_M_base), 0); } > > + > > + constexpr auto > > + begin() const requires __detail::__range_with_movable_reference > > + { return _Iterator(ranges::begin(_M_base), 0); } > > + > > + constexpr auto > > + end() requires (!__detail::__simple_view<_Vp>) > > + { > > + if constexpr (common_range<_Vp> && sized_range<_Vp>) > > + return _Iterator(ranges::end(_M_base), ranges::distance(= _M_base)); > > + else > > + return _Sentinel(ranges::end(_M_base)); > > + } > > + > > + constexpr auto > > + end() const requires __detail::__range_with_movable_reference > > + { > > + if constexpr (common_range && sized_range) > > + return _Iterator(ranges::end(_M_base), ranges::distance(_= M_base)); > > + else > > + return _Sentinel(ranges::end(_M_base)); > > + } > > + > > + constexpr auto > > + size() requires sized_range<_Vp> > > + { return ranges::size(_M_base); } > > + > > + constexpr auto > > + size() const requires sized_range > > + { return ranges::size(_M_base); } > > + > > + constexpr _Vp > > + base() const & requires copy_constructible<_Vp> > > + { return _M_base; } > > + > > + constexpr _Vp > > + base() && > > + { return std::move(_M_base); } > > + }; > > + > > + template > > + enumerate_view(_Range&&) -> enumerate_view>; > > + > > + template > > + inline constexpr bool enable_borrowed_range> > > + =3D enable_borrowed_range<_Tp>; > > + > > + template > > + requires __detail::__range_with_movable_reference<_Vp> > > + template > > + class enumerate_view<_Vp>::_Iterator > > + { > > + using _Base =3D __maybe_const_t<_Const, _Vp>; > > + > > + static auto > > + _S_iter_concept() > > + { > > + if constexpr (random_access_range<_Base>) > > + return random_access_iterator_tag{}; > > + else if constexpr (bidirectional_range<_Base>) > > + return bidirectional_iterator_tag{}; > > + else if constexpr (forward_range<_Base>) > > + return forward_iterator_tag{}; > > + else > > + return input_iterator_tag{}; > > + } > > + > > + friend enumerate_view; > > + > > + public: > > + using iterator_category =3D input_iterator_tag; > > + using iterator_concept =3D decltype(_S_iter_concept()); > > + using difference_type =3D range_difference_t<_Base>; > > + using value_type =3D tuple>; > > + > > + private: > > + using __reference_type =3D tuple>; > > + > > + iterator_t<_Base> _M_current =3D iterator_t<_Base>(); > > + difference_type _M_pos =3D 0; > > + > > + constexpr explicit > > + _Iterator(iterator_t<_Base> __current, difference_type __pos) > > + : _M_current(std::move(__current)), _M_pos(__pos) > > + { } > > + > > + public: > > + _Iterator() requires default_initializable> =3D = default; > > + > > + constexpr > > + _Iterator(_Iterator __i) > > + requires _Const && convertible_to, iterator_t<_Bas= e>> > > + : _M_current(std::move(__i._M_current)), _M_pos(__i._M_pos) > > + { } > > + > > + constexpr const iterator_t<_Base> & > > + base() const & noexcept > > + { return _M_current; } > > + > > + constexpr iterator_t<_Base> > > + base() && > > + { return std::move(_M_current); } > > + > > + constexpr difference_type > > + index() const noexcept > > + { return _M_pos; } > > + > > + constexpr auto > > + operator*() const > > + { return __reference_type(_M_pos, *_M_current); } > > + > > + constexpr _Iterator& > > + operator++() > > + { > > + ++_M_current; > > + ++_M_pos; > > + return *this; > > + } > > + > > + constexpr void > > + operator++(int) > > + { ++*this; } > > + > > + constexpr _Iterator > > + operator++(int) requires forward_range<_Base> > > + { > > + auto __tmp =3D *this; > > + ++*this; > > + return __tmp; > > + } > > + > > + constexpr _Iterator& > > + operator--() requires bidirectional_range<_Base> > > + { > > + --_M_current; > > + --_M_pos; > > + return *this; > > + } > > + > > + constexpr _Iterator > > + operator--(int) requires bidirectional_range<_Base> > > + { > > + auto __tmp =3D *this; > > + --*this; > > + return __tmp; > > + } > > + > > + constexpr _Iterator& > > + operator+=3D(difference_type __n) requires random_access_range<_Ba= se> > > + { > > + _M_current +=3D __n; > > + _M_pos +=3D __n; > > + return *this; > > + } > > + > > + constexpr _Iterator& > > + operator-=3D(difference_type __n) requires random_access_range<_Ba= se> > > + { > > + _M_current -=3D __n; > > + _M_pos -=3D __n; > > + return *this; > > + } > > + > > + constexpr auto > > + operator[](difference_type __n) const requires random_access_range= <_Base> > > + { return __reference_type(_M_pos + __n, _M_current[__n]); } > > + > > + friend constexpr bool > > + operator=3D=3D(const _Iterator& __x, const _Iterator& __y) noexcep= t > > + { return __x._M_pos =3D=3D __y._M_pos; } > > + > > + friend constexpr strong_ordering > > + operator<=3D>(const _Iterator& __x, const _Iterator& __y) noexcept > > + { return __x._M_pos <=3D> __y._M_pos; } > > + > > + friend constexpr _Iterator > > + operator+(const _Iterator& __x, difference_type __y) > > + requires random_access_range<_Base> > > + { return (auto(__x) +=3D __y); } > > + > > + friend constexpr _Iterator > > + operator+(difference_type __x, const _Iterator& __y) > > + requires random_access_range<_Base> > > + { return auto(__y) +=3D __x; } > > + > > + friend constexpr _Iterator > > + operator-(const _Iterator& __x, difference_type __y) > > + requires random_access_range<_Base> > > + { return auto(__x) -=3D __y; } > > + > > + friend constexpr difference_type > > + operator-(const _Iterator& __x, const _Iterator& __y) > > + { return __x._M_pos - __y._M_pos; } > > + > > + friend constexpr auto > > + iter_move(const _Iterator& __i) > > + noexcept(noexcept(ranges::iter_move(__i._M_current)) > > + && is_nothrow_move_constructible_v>) > > + { > > + return tuple> > > + (__i._M_pos, ranges::iter_move(__i._M_current)); > > + } > > + }; > > + > > + template > > + requires __detail::__range_with_movable_reference<_Vp> > > + template > > + class enumerate_view<_Vp>::_Sentinel > > + { > > + using _Base =3D __maybe_const_t<_Const, _Vp>; > > + > > + sentinel_t<_Base> _M_end =3D sentinel_t<_Base>(); > > + > > + constexpr explicit > > + _Sentinel(sentinel_t<_Base> __end) > > + : _M_end(std::move(__end)) > > + { } > > + > > + friend enumerate_view; > > + > > + public: > > + _Sentinel() =3D default; > > + > > + constexpr > > + _Sentinel(_Sentinel __other) > > + requires _Const && convertible_to, sentinel_t<_Bas= e>> > > + : _M_end(std::move(__other._M_end)) > > + { } > > + > > + constexpr sentinel_t<_Base> > > + base() const > > + { return _M_end; } > > + > > + template > > + requires sentinel_for, iterator_t<__maybe_const_= t<_OtherConst, _Vp>>> > > + friend constexpr bool > > + operator=3D=3D(const _Iterator<_OtherConst>& __x, const _Sentinel&= __y) > > + { return __x._M_current =3D=3D __y._M_end; } > > + > > + template > > + requires sized_sentinel_for, iterator_t<__maybe_= const_t<_OtherConst, _Vp>>> > > + friend constexpr range_difference_t<__maybe_const_t<_OtherConst, _= Vp>> > > + operator-(const _Iterator<_OtherConst>& __x, const _Sentinel& __y) > > + { return __x._M_current - __y._M_end; } > > + > > + template > > + requires sized_sentinel_for, iterator_t<__maybe_= const_t<_OtherConst, _Vp>>> > > + friend constexpr range_difference_t<__maybe_const_t<_OtherConst, _= Vp>> > > + operator-(const _Sentinel& __x, const _Iterator<_OtherConst>& __y) > > + { return __x._M_end - __y._M_current; } > > + }; > > + > > + namespace views > > + { > > + namespace __detail > > + { > > + template > > + concept __can_enumerate_view > > + =3D requires { enumerate_view>(std::declval<_Tp>()= ); }; > > + } > > + > > + struct _Enumerate : __adaptor::_RangeAdaptorClosure > > + { > > + template > > + requires __detail::__can_enumerate_view<_Range> > > + constexpr auto > > + operator() [[nodiscard]] (_Range&& __r) const > > + { return enumerate_view>(std::forward<_Range>(__r= )); } > > + }; > > + > > + inline constexpr _Enumerate enumerate; > > + } > > #endif // C++23 > > } // namespace ranges > > > > diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/st= d/version > > index dfe1e242a2a..42f7e9e15b7 100644 > > --- a/libstdc++-v3/include/std/version > > +++ b/libstdc++-v3/include/std/version > > @@ -340,6 +340,7 @@ > > #define __cpp_lib_ranges_cartesian_product 202207L > > #define __cpp_lib_ranges_as_rvalue 202207L > > #define __cpp_lib_ranges_as_const 202207L > > +#define __cpp_lib_ranges_enumerate 202302L > > #if __cpp_constexpr_dynamic_alloc > > # if _GLIBCXX_HOSTED > > # define __cpp_lib_constexpr_bitset 202202L > > diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/enumerate/1.cc = b/libstdc++-v3/testsuite/std/ranges/adaptors/enumerate/1.cc > > new file mode 100644 > > index 00000000000..445d9854c8c > > --- /dev/null > > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/enumerate/1.cc > > @@ -0,0 +1,102 @@ > > +// { dg-options "-std=3Dgnu++23" } > > +// { dg-do run { target c++23 } } > > + > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +#if __cpp_lib_ranges_enumerate !=3D 202302L > > +# error "Feature-test macro __cpp_lib_ranges_enumerate has wrong value= in " > > +#endif > > + > > +namespace ranges =3D std::ranges; > > +namespace views =3D std::views; > > + > > +using __gnu_test::test_input_range; > > +using __gnu_test::test_forward_range; > > +using __gnu_test::test_bidirectional_range; > > +using __gnu_test::test_random_access_range; > > + > > +constexpr bool > > +test01() > > +{ > > + int x[] =3D {1, 2, 3}; > > + auto v =3D x | views::enumerate; > > + > > + VERIFY( ranges::equal(v | views::keys, (int[]){0, 1, 2}) ); > > + VERIFY( ranges::equal(v | views::values, (int[]){1, 2, 3}) ); > > + > > + auto it =3D v.begin(); > > + VERIFY( it =3D=3D it ); > > + VERIFY( it !=3D it + 1 ); > > + VERIFY( it !=3D v.end() ); > > + > > + VERIFY( it.index() =3D=3D 0 ); > > + VERIFY( (++it).index() =3D=3D 1 ); > > + VERIFY( (++it).index() =3D=3D 2 ); > > + > > + return true; > > +} > > + > > +template class Container> > > +void > > +test02() > > +{ > > + int x[] =3D {1, 2, 3}; > > + Container rx (x); > > + auto v =3D rx | views::enumerate; > > + > > + int j =3D 0; > > + for (auto [i, y] : v) > > + { > > + VERIFY (&y =3D=3D &x[j]); > > + VERIFY (j =3D=3D i); > > + ++j; > > + } > > + VERIFY (j =3D=3D ranges::size(x)); > > + > > + if constexpr (ranges::bidirectional_range) > > + { > > + static_assert(ranges::bidirectional_range); > > + for (auto [i, y] : v | views::reverse) > > + { > > + --j; > > + VERIFY (&y =3D=3D &x[j]); > > + VERIFY (j =3D=3D i); > > + } > > + VERIFY (j =3D=3D 0); > > + } > > + > > + if constexpr (ranges::random_access_range) > > + { > > + static_assert(ranges::random_access_range); > > + for (j =3D 0; j < ranges::ssize(x); ++j) > > + { > > + VERIFY (std::get<0>(v[j]) =3D=3D j); > > + VERIFY (&std::get<1>(v[j]) =3D=3D &x[j]); > > + VERIFY (*(v.begin() + j) =3D=3D v[j]); > > + VERIFY (*(v.begin() + (ranges::size(x) - 1) - j) =3D=3D v[ran= ges::size(x) - 1 - j]); > > + VERIFY (v.begin() + j + 1 > v.begin() + j ); > > + VERIFY (v.begin() + j < v.begin() + j + 1 ); > > + VERIFY (v.begin() + j >=3D v.begin() ); > > + VERIFY (v.begin() <=3D v.begin() + j ); > > + VERIFY( v.begin() + j !=3D v.end() ); > > + VERIFY( v.begin() + j - v.begin() =3D=3D j ); > > + VERIFY( v.end() - (v.begin() + j) =3D=3D ranges::ssize(x) - j= ); > > + } > > + VERIFY( v.begin() + j =3D=3D v.end() ); > > + } > > +} > > + > > +int > > +main() > > +{ > > + static_assert(test01()); > > + > > + test02(); > > + test02(); > > + test02(); > > + test02(); > > +} > > diff --git a/libstdc++-v3/testsuite/std/ranges/version_c++23.cc b/libst= dc++-v3/testsuite/std/ranges/version_c++23.cc > > index fc98bef922a..e2c14edc8ef 100644 > > --- a/libstdc++-v3/testsuite/std/ranges/version_c++23.cc > > +++ b/libstdc++-v3/testsuite/std/ranges/version_c++23.cc > > @@ -48,3 +48,7 @@ > > #if __cpp_lib_ranges_as_const !=3D 202207L > > # error "Feature-test macro __cpp_lib_ranges_as_const has wrong value = in " > > #endif > > + > > +#if __cpp_lib_ranges_enumerate !=3D 202302L > > +# error "Feature-test macro __cpp_lib_ranges_enumerate has wrong value= in " > > +#endif > > -- > > 2.40.0.315.g0607f793cb > > >