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 3559F385841F for ; Wed, 14 Sep 2022 14:19:05 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 3559F385841F 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=1663165144; 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; bh=faphKzWehiSXrs3ZOfMdLO0HjDlxlONe5yS9N+2BAAg=; b=dM5kqF4LZpI1pycR+C6L8YyPWszccP9+HePcGVi5NyZxQqHs5HJMav1XVAWCrqwaJcOVFg +B1LY1VQIqr/BAh5Xdxza0A39KhxxIjkvBTzHZRHTfzrzdqZajI3Eeg8KCOE0bkvB02vrx y3otuZDgDGSB1psVB5ZgMObnqiTNFCw= Received: from mail-qk1-f199.google.com (mail-qk1-f199.google.com [209.85.222.199]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_128_GCM_SHA256) id us-mta-427-MzTIDqnwNOWUfaSMKYfNNg-1; Wed, 14 Sep 2022 10:19:03 -0400 X-MC-Unique: MzTIDqnwNOWUfaSMKYfNNg-1 Received: by mail-qk1-f199.google.com with SMTP id x22-20020a05620a259600b006b552a69231so13031588qko.18 for ; Wed, 14 Sep 2022 07:19:03 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date; bh=faphKzWehiSXrs3ZOfMdLO0HjDlxlONe5yS9N+2BAAg=; b=Il1nj0bATayrtahTZ9XDXcKAFv/mwMv4PbdXRF6ZIaoPr3oGv27kdIYBs/GrftWhe3 KiMB83xrVhWDTq/JzQt2ERvVMEHw/XbrBRrzdUKgfj8kiDBrF6KHf5Vao7zzwCYrdVUK I7EjR8nrNOY2tMHQJ3PrRATGMbe6C0PXpU0SQlX2F60uE+b/CyfvAFI8l/Prl/4Kbk6k A6BLoHcbI5rWAKobCg5wpPtTp9HoXLvFZ16PmmG/nr9QKDUU6YEWRhAmUKE4wtU3JX2e mwPiu9Yfr7JZM7xsHi8HuaREvddXpWm+TlqIQ5gml9xARD/1sFPPdFRGF9UqOJXJNfCk VJDg== X-Gm-Message-State: ACgBeo1S+9qdz8P+DKEJw4KZXf3mtrhp/2KRei7ZuoWu+Iy2FI5ckbqW YdFybfoIIhgkFNpmG/H9kF8q6HXap4G9XcrjYZCI8UYYT7D/N2OYWgte2kgL0ddq329wlGdwGn5 zSHuKbOeAdULxgDI= X-Received: by 2002:a37:9ac2:0:b0:6cd:e304:7189 with SMTP id c185-20020a379ac2000000b006cde3047189mr17328493qke.536.1663165143051; Wed, 14 Sep 2022 07:19:03 -0700 (PDT) X-Google-Smtp-Source: AA6agR7ef/vxcJ1IdK2Py18W5MXZKjk77kb6AFtfuMkrgRQW1jumNF9/aMp0SIQonzRTRP0ypTCDtw== X-Received: by 2002:a37:9ac2:0:b0:6cd:e304:7189 with SMTP id c185-20020a379ac2000000b006cde3047189mr17328465qke.536.1663165142765; Wed, 14 Sep 2022 07:19:02 -0700 (PDT) Received: from localhost.localdomain (ool-457670bb.dyn.optonline.net. [69.118.112.187]) by smtp.gmail.com with ESMTPSA id bn29-20020a05620a2add00b006bb2cd2f6d1sm1792396qkb.127.2022.09.14.07.19.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 14 Sep 2022 07:19:02 -0700 (PDT) From: Patrick Palka To: gcc-patches@gcc.gnu.org Cc: libstdc++@gcc.gnu.org, Patrick Palka Subject: [PATCH] libstdc++: Implement ranges::chunk_by_view from P2443R1 Date: Wed, 14 Sep 2022 10:19:00 -0400 Message-Id: <20220914141900.3489407-1-ppalka@redhat.com> X-Mailer: git-send-email 2.37.3.611.ge188ec3a73 MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-14.1 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_LOW,SPF_HELO_NONE,SPF_NONE,TXREP,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: Tested on x86_64-pc-linux-gnu, does this look OK for trunk? libstdc++-v3/ChangeLog: * include/bits/ranges_algo.h (__adjacent_find_fn, adjacent_find): Move to ... * include/bits/ranges_util.h: ... here. * include/std/ranges (chunk_by_view): Define. (chunk_by_view::_Iterator): Define. (__detail::__can_chunk_by_view): Define. (_ChunkBy, chunk_by): Define. * testsuite/std/ranges/adaptors/chunk_by/1.cc: New test. --- libstdc++-v3/include/bits/ranges_algo.h | 38 +--- libstdc++-v3/include/bits/ranges_util.h | 38 ++++ libstdc++-v3/include/std/ranges | 193 ++++++++++++++++++ .../std/ranges/adaptors/chunk_by/1.cc | 58 ++++++ 4 files changed, 290 insertions(+), 37 deletions(-) create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/chunk_by/1.cc diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h index 2a116361a67..228e10b62bf 100644 --- a/libstdc++-v3/include/bits/ranges_algo.h +++ b/libstdc++-v3/include/bits/ranges_algo.h @@ -506,43 +506,7 @@ namespace ranges inline constexpr __find_end_fn find_end{}; - struct __adjacent_find_fn - { - template _Sent, - typename _Proj = identity, - indirect_binary_predicate, - projected<_Iter, _Proj>> _Pred - = ranges::equal_to> - constexpr _Iter - operator()(_Iter __first, _Sent __last, - _Pred __pred = {}, _Proj __proj = {}) const - { - if (__first == __last) - return __first; - auto __next = __first; - for (; ++__next != __last; __first = __next) - { - if (std::__invoke(__pred, - std::__invoke(__proj, *__first), - std::__invoke(__proj, *__next))) - return __first; - } - return __next; - } - - template, _Proj>, - projected, _Proj>> _Pred = ranges::equal_to> - constexpr borrowed_iterator_t<_Range> - operator()(_Range&& __r, _Pred __pred = {}, _Proj __proj = {}) const - { - return (*this)(ranges::begin(__r), ranges::end(__r), - std::move(__pred), std::move(__proj)); - } - }; - - inline constexpr __adjacent_find_fn adjacent_find{}; + // adjacent_find is defined in . struct __is_permutation_fn { diff --git a/libstdc++-v3/include/bits/ranges_util.h b/libstdc++-v3/include/bits/ranges_util.h index bb56deee01b..85ddea68407 100644 --- a/libstdc++-v3/include/bits/ranges_util.h +++ b/libstdc++-v3/include/bits/ranges_util.h @@ -704,6 +704,44 @@ namespace ranges inline constexpr __min_fn min{}; + struct __adjacent_find_fn + { + template _Sent, + typename _Proj = identity, + indirect_binary_predicate, + projected<_Iter, _Proj>> _Pred + = ranges::equal_to> + constexpr _Iter + operator()(_Iter __first, _Sent __last, + _Pred __pred = {}, _Proj __proj = {}) const + { + if (__first == __last) + return __first; + auto __next = __first; + for (; ++__next != __last; __first = __next) + { + if (std::__invoke(__pred, + std::__invoke(__proj, *__first), + std::__invoke(__proj, *__next))) + return __first; + } + return __next; + } + + template, _Proj>, + projected, _Proj>> _Pred = ranges::equal_to> + constexpr borrowed_iterator_t<_Range> + operator()(_Range&& __r, _Pred __pred = {}, _Proj __proj = {}) const + { + return (*this)(ranges::begin(__r), ranges::end(__r), + std::move(__pred), std::move(__proj)); + } + }; + + inline constexpr __adjacent_find_fn adjacent_find{}; + } // namespace ranges using ranges::get; diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index 44a4df8b5d5..53093a3762f 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -6676,6 +6676,199 @@ namespace views::__adaptor inline constexpr _Slide slide; } + + template, iterator_t<_Vp>> _Pred> + requires view<_Vp> && is_object_v<_Pred> + class chunk_by_view : public view_interface> + { + _Vp _M_base = _Vp(); + __detail::__box<_Pred> _M_pred = _Pred(); + __detail::_CachedPosition<_Vp> _M_cached_begin; + + constexpr iterator_t<_Vp> + _M_find_next(iterator_t<_Vp> __current) + { + __glibcxx_assert(_M_pred.has_value()); + auto __pred = [this](_Tp&& __x, _Tp&& __y) { + return !bool((*_M_pred)(std::forward<_Tp>(__x), std::forward<_Tp>(__y))); + }; + auto __it = ranges::adjacent_find(__current, ranges::end(_M_base), __pred); + return ranges::next(__it, 1, ranges::end(_M_base)); + } + + constexpr iterator_t<_Vp> + _M_find_prev(iterator_t<_Vp> __current) requires bidirectional_range<_Vp> + { + __glibcxx_assert(_M_pred.has_value()); + auto __pred = [this](_Tp&& __x, _Tp&& __y) { + return !bool((*_M_pred)(std::forward<_Tp>(__y), std::forward<_Tp>(__x))); + }; + auto __rbegin = std::make_reverse_iterator(__current); + auto __rend = std::make_reverse_iterator(ranges::begin(_M_base)); + __glibcxx_assert(__rbegin != __rend); + auto __it = ranges::adjacent_find(__rbegin, __rend, __pred).base(); + return ranges::prev(__it, 1, ranges::begin(_M_base)); + } + + class _Iterator; + + public: + chunk_by_view() requires (default_initializable<_Vp> + && default_initializable<_Pred>) + = default; + + constexpr explicit + chunk_by_view(_Vp __base, _Pred __pred) + : _M_base(std::move(__base)), _M_pred(std::move(__pred)) + { } + + constexpr _Vp + base() const & requires copy_constructible<_Vp> + { return _M_base; } + + constexpr _Vp + base() && + { return std::move(_M_base); } + + constexpr const _Pred& + pred() const + { return *_M_pred; } + + constexpr _Iterator + begin() + { + __glibcxx_assert(_M_pred.has_value()); + iterator_t<_Vp> __it; + if (_M_cached_begin._M_has_value()) + __it = _M_cached_begin._M_get(_M_base); + else + { + __it = _M_find_next(ranges::begin(_M_base)); + _M_cached_begin._M_set(_M_base, __it); + } + return _Iterator(*this, ranges::begin(_M_base), __it); + } + + constexpr auto + end() + { + if constexpr (common_range<_Vp>) + return _Iterator(*this, ranges::end(_M_base), ranges::end(_M_base)); + else + return default_sentinel; + } + }; + + template + chunk_by_view(_Range&&, _Pred) -> chunk_by_view, _Pred>; + + template, iterator_t<_Vp>> _Pred> + requires view<_Vp> && is_object_v<_Pred> + class chunk_by_view<_Vp, _Pred>::_Iterator + { + chunk_by_view* _M_parent = nullptr; + iterator_t<_Vp> _M_current = iterator_t<_Vp>(); + iterator_t<_Vp> _M_next = iterator_t<_Vp>(); + + constexpr + _Iterator(chunk_by_view& __parent, iterator_t<_Vp> __current, iterator_t<_Vp> __next) + : _M_parent(std::__addressof(__parent)), _M_current(__current), _M_next(__next) + { } + + static auto + _S_iter_concept() + { + if constexpr (bidirectional_range<_Vp>) + return bidirectional_iterator_tag{}; + else + return forward_iterator_tag{}; + } + + friend chunk_by_view; + + public: + using value_type = subrange>; + using difference_type = range_difference_t<_Vp>; + using iterator_category = input_iterator_tag; + using iterator_concept = decltype(_S_iter_concept()); + + _Iterator() = default; + + constexpr value_type + operator*() const + { + __glibcxx_assert(_M_current != _M_next); + return ranges::subrange(_M_current, _M_next); + } + + constexpr _Iterator& + operator++() + { + __glibcxx_assert(_M_current != _M_next); + _M_current = _M_next; + _M_next = _M_parent->_M_find_next(_M_current); + return *this; + } + + constexpr _Iterator + operator++(int) + { + auto __tmp = *this; + ++*this; + return __tmp; + } + + constexpr _Iterator& + operator--() requires bidirectional_range<_Vp> + { + _M_next = _M_current; + _M_current = _M_parent->_M_find_prev(_M_next); + return *this; + } + + constexpr _Iterator + operator--(int) requires bidirectional_range<_Vp> + { + auto __tmp = *this; + --*this; + return __tmp; + } + + friend constexpr bool + operator==(const _Iterator& __x, const _Iterator& __y) + { return __x._M_current == __y._M_current; } + + friend constexpr bool + operator==(const _Iterator& __x, default_sentinel_t) + { return __x._M_current == __x._M_next; } + }; + + namespace views + { + namespace __detail + { + template + concept __can_chunk_by_view + = requires { chunk_by_view(std::declval<_Range>(), std::declval<_Pred>()); }; + } + + struct _ChunkBy : __adaptor::_RangeAdaptor<_ChunkBy> + { + template + requires __detail::__can_chunk_by_view<_Range, _Pred> + constexpr auto + operator() [[nodiscard]] (_Range&& __r, _Pred&& __pred) const + { return chunk_by_view(std::forward<_Range>(__r), std::forward<_Pred>(__pred)); } + + using __adaptor::_RangeAdaptor<_ChunkBy>::operator(); + static constexpr int _S_arity = 2; + static constexpr bool _S_has_simple_extra_args = true; + }; + + inline constexpr _ChunkBy chunk_by; + } #endif // C++23 } // namespace ranges diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/chunk_by/1.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/chunk_by/1.cc new file mode 100644 index 00000000000..d57b127fbc8 --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/chunk_by/1.cc @@ -0,0 +1,58 @@ +// { dg-options "-std=gnu++23" } +// { dg-do run { target c++23 } } + +#include +#include +#include +#include +#include + +namespace ranges = std::ranges; +namespace views = std::views; + +constexpr bool +test01() +{ + int x[] = {1, 2, 2, 3, 0, 4, 5, 2}; + auto v = x | views::chunk_by(ranges::less_equal{}); + static_assert(ranges::bidirectional_range + && ranges::common_range); + VERIFY( ranges::equal(v, (std::initializer_list[]){{1, 2, 2, 3}, {0, 4, 5}, {2}}, + ranges::equal) ); + VERIFY( ranges::equal(v | views::reverse, + (std::initializer_list[]){{2}, {0, 4, 5}, {1, 2, 2, 3}}, + ranges::equal) ); + VERIFY( ranges::equal(v | views::join, x) ); + auto i = v.begin(); + auto j = i; + j++; + VERIFY( i == i && i != v.end() ); + VERIFY( j == j && j != v.end() ); + VERIFY( j != i ); + j--; + VERIFY( j == i ); + + return true; +} + +void +test02() +{ + int x[] = {1, 2, 3}; + __gnu_test::test_forward_range rx(x); + auto v = rx | views::chunk_by(ranges::equal_to{}); + static_assert(!ranges::bidirectional_range + && !ranges::common_range); + VERIFY( ranges::equal(v, x | views::transform(views::single), ranges::equal) ); + auto i = v.begin(); + VERIFY( i != v.end() ); + ranges::advance(i, 3); + VERIFY( i == v.end() ); +} + +int +main() +{ + static_assert(test01()); + test02(); +} -- 2.37.3.611.ge188ec3a73