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 43673385842D for ; Mon, 29 Apr 2024 09:11:30 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 43673385842D Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 43673385842D Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714381893; cv=none; b=q6dUkCLfAZVHhHfazthTqjMM0XR7ekJVsEl99Uvc+j8xyuKPF7c0vCBH3StEGVcTcDDq1XPiPGUY/Zy0hHG/X2PNeoovLo9TtjwbRJ9QsbvM+GnqjIx4Cno55vmhRSkHdMwZd7HJcZr1rfpCxQFXGOODbHcvQeOEWvSlx2iUUEM= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1714381893; c=relaxed/simple; bh=dIufyRdq+r6ZrGVpj0RXSF0C0+UfUwnVgUP4sAU4KFI=; h=DKIM-Signature:MIME-Version:From:Date:Message-ID:Subject:To; b=vi5pRjc6LF45H1QfHgAItwI67Jna847xvb/nxwbmGCGiV1PInq7vWTRCNONCOXAdyYd9x3nX3UC3bEgtXzSZ0mHDLvLIP3H9UJ4jSJfdRdAJP7Vt8MB2KtWzkY5fhRFlPyWf3jiH8IV/fx0f0/mU7nM5wkgBONC5d5K9ZDkJDV4= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1714381889; 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: in-reply-to:in-reply-to:references:references; bh=DRk68ydOiwAsJBfi9NoStyr7PkdGQWQIlyIObE/74uY=; b=PZgun/UvKkdhkm26GK8RVjgXtaFGmILO24TPgmZ6TbIeC9C14zOYm9qMKK7z7IYfA0q0pT X7w7dcvLD0HCmdTC54Gkbv8K2MSAdsk2eSoUB8tZqtGhoZAG8IpqWDAAm5Gmo30Cpqv3S1 HzXOW7wlMtE3msaQo7PNRrKfyO/q4uM= Received: from mail-yb1-f200.google.com (mail-yb1-f200.google.com [209.85.219.200]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-651-Z4rxx2EBNRaoJf-XqW4pWg-1; Mon, 29 Apr 2024 05:11:28 -0400 X-MC-Unique: Z4rxx2EBNRaoJf-XqW4pWg-1 Received: by mail-yb1-f200.google.com with SMTP id 3f1490d57ef6-de60321ce6cso112222276.1 for ; Mon, 29 Apr 2024 02:11:28 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1714381888; x=1714986688; h=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=DRk68ydOiwAsJBfi9NoStyr7PkdGQWQIlyIObE/74uY=; b=qD9MObf2VfF0T8+XbbuzQJkwSlxIlSLKzcc91JT6ghuKIw5p0tKJqsut5RfwQge5Sn WQwJpZG08djqYiAUV36VGKIni5wXDQBjtZHnRPDZwFRRowKME4YBLP8dPOeUcRwpkg5Z zMOs9zp6SHw5aW8edujiXATpbs0sbpYXMgf2Tyuhz8PuB3hK5uxmeERziGKJ+SDq3w2p NSJmCCqJNX/BC4sC6l2wN+BaW7ryW0IePvRjt2M5m2hCSsE71KZ7SBOdBlA7H/A/LHb5 +wKKQ+8LStSdA47shfiqEPsgpDFHpMkiSbaR+oo4+evSHrNiYdfw2/VNw8pdKieRN31t SijQ== X-Forwarded-Encrypted: i=1; AJvYcCXmp+AFS3WlFhZDbZm46QKeml5kFCL//o4w9Jmpja30TcLGWJmIpPXvxU8+GWVcQkoahYbO0oG/rI7nIQRTslsDxZIPzbA= X-Gm-Message-State: AOJu0YyPI9Xx+NNSpZpizt1r1NlCACo+GBUE5PmxM4Ec3aw1RRRpE9gF ZTouOnBaoYXTHQnjhT+jsUFJRy+4s52kDCfQ9fuAaTfS3xBMgioUldy3Mk9jcqjfByWMe25zvRM ga+cFwWC/rdBq2pqwVX8loExfG97Fhsp0lnLjvcJgQgDqRCLmHbIjUhkrOkZyq8mshE33klaYL8 d9Y1uRJspCLHECCPOMjOrKA063wVM= X-Received: by 2002:a25:83c6:0:b0:de4:7f18:4a3 with SMTP id v6-20020a2583c6000000b00de47f1804a3mr8911997ybm.25.1714381887845; Mon, 29 Apr 2024 02:11:27 -0700 (PDT) X-Google-Smtp-Source: AGHT+IER58o6GdidiGaZ2x9AfhT0NoWv2b5Gs0qQCjudt519ayK0VxE62fr4MpKapZ2o81oN6Cw+NsUmg+6s1AjZjHY= X-Received: by 2002:a25:83c6:0:b0:de4:7f18:4a3 with SMTP id v6-20020a2583c6000000b00de47f1804a3mr8911980ybm.25.1714381887472; Mon, 29 Apr 2024 02:11:27 -0700 (PDT) MIME-Version: 1.0 References: <20240422214231.3530630-1-ppalka@redhat.com> In-Reply-To: <20240422214231.3530630-1-ppalka@redhat.com> From: Jonathan Wakely Date: Mon, 29 Apr 2024 10:11:11 +0100 Message-ID: Subject: Re: [PATCH] libstdc++: Implement ranges::concat_view from P2542R7 To: Patrick Palka 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" X-Spam-Status: No, score=-12.5 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_H4,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE,SPF_NONE,TXREP 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: On Mon, 22 Apr 2024 at 22:43, Patrick Palka wrote: > > Tested on x86_64-pc-linux-gnu, does this look OK for trunk? More tests > are needed but I figured I'd submit this now for possible consideration into > GCC 14 since we're getting close to release.. All changes are confined to > C++26. OK for trunk. Maybe we can backport it for 14.2 later, but not now. Sorry for the review being slow. > > -- >8 -- > > libstdc++-v3/ChangeLog: > > * include/bits/version.def (ranges_concat): Define. > * include/bits/version.h: Regenerate. > * include/std/ranges (__detail::__concat_reference_t): Define > for C++26. > (__detail::__concat_value_t): Likewise. > (__detail::__concat_rvalue_reference_t): Likewise. > (__detail::__concat_indirectly_readable_impl): Likewise. > (__detail::__concat_indirectly_readable): Likewise. > (__detail::__concatable): Likewise. > (__detail::__all_but_last_common): Likewise. > (__detail::__concat_is_random_access): Likewise. > (__detail::__concat_is_bidirectional): Likewise. > (__detail::__last_is_common): Likewise. > (concat_view): Likewise. > (__detail::__concat_view_iter_cat): Likewise. > (concat_view::iterator): Likewise. > (views::__detail::__can_concat_view): Likewise. > (views::_Concat, views::concat): Likewise. > * testsuite/std/ranges/concat/1.cc: New test. > --- > libstdc++-v3/include/bits/version.def | 8 + > libstdc++-v3/include/bits/version.h | 10 + > libstdc++-v3/include/std/ranges | 584 ++++++++++++++++++ > libstdc++-v3/testsuite/std/ranges/concat/1.cc | 61 ++ > 4 files changed, 663 insertions(+) > create mode 100644 libstdc++-v3/testsuite/std/ranges/concat/1.cc > > diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def > index 5c0477fb61e..af13090c094 100644 > --- a/libstdc++-v3/include/bits/version.def > +++ b/libstdc++-v3/include/bits/version.def > @@ -1796,6 +1796,14 @@ ftms = { > }; > }; > > +ftms = { > + name = ranges_concat; > + values = { > + v = 202403; > + cxxmin = 26; > + }; > +}; > + > // Standard test specifications. > stds[97] = ">= 199711L"; > stds[03] = ">= 199711L"; > diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h > index 65e708c73fb..1f27bfe050d 100644 > --- a/libstdc++-v3/include/bits/version.h > +++ b/libstdc++-v3/include/bits/version.h > @@ -2003,4 +2003,14 @@ > #endif /* !defined(__cpp_lib_to_string) && defined(__glibcxx_want_to_string) */ > #undef __glibcxx_want_to_string > > +#if !defined(__cpp_lib_ranges_concat) > +# if (__cplusplus > 202302L) > +# define __glibcxx_ranges_concat 202403L > +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges_concat) > +# define __cpp_lib_ranges_concat 202403L > +# endif > +# endif > +#endif /* !defined(__cpp_lib_ranges_concat) && defined(__glibcxx_want_ranges_concat) */ > +#undef __glibcxx_want_ranges_concat > + > #undef __glibcxx_want_all > diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges > index afce818376b..28a39bf6f34 100644 > --- a/libstdc++-v3/include/std/ranges > +++ b/libstdc++-v3/include/std/ranges > @@ -55,6 +55,7 @@ > #define __glibcxx_want_ranges_as_const > #define __glibcxx_want_ranges_as_rvalue > #define __glibcxx_want_ranges_cartesian_product > +#define __glibcxx_want_ranges_concat > #define __glibcxx_want_ranges_chunk > #define __glibcxx_want_ranges_chunk_by > #define __glibcxx_want_ranges_enumerate > @@ -9514,6 +9515,589 @@ namespace __detail > } // namespace ranges > #endif // __cpp_lib_ranges_to_container > > +#if __cpp_lib_ranges_concat // C++ >= C++26 > +namespace ranges > +{ > + namespace __detail > + { > + template > + using __concat_reference_t = common_reference_t...>; > + > + template > + using __concat_value_t = common_type_t...>; > + > + template > + using __concat_rvalue_reference_t > + = common_reference_t...>; > + > + template > + concept __concat_indirectly_readable_impl = requires (const _It __it) { > + { *__it } -> convertible_to<_Ref>; > + { ranges::iter_move(__it) } -> convertible_to<_RRef>; > + }; > + > + template > + concept __concat_indirectly_readable > + = common_reference_with<__concat_reference_t<_Rs...>&&, __concat_value_t<_Rs...>&> > + && common_reference_with<__concat_reference_t<_Rs...>&&, > + __concat_rvalue_reference_t<_Rs...>&&> > + && common_reference_with<__concat_rvalue_reference_t<_Rs...>&&, > + __concat_value_t<_Rs...> const&> > + && (__concat_indirectly_readable_impl<__concat_reference_t<_Rs...>, > + __concat_rvalue_reference_t<_Rs...>, > + iterator_t<_Rs>> > + && ...); > + > + template > + concept __concatable = requires { > + typename __concat_reference_t<_Rs...>; > + typename __concat_value_t<_Rs...>; > + typename __concat_rvalue_reference_t<_Rs...>; > + } && __concat_indirectly_readable<_Rs...>; > + > + template > + struct __all_but_last_common > + { > + static inline constexpr bool value > + = requires { requires (common_range<__maybe_const_t<_Const, _Range>> > + && __all_but_last_common<_Const, _Rs...>::value); }; > + }; > + > + template > + struct __all_but_last_common<_Const, _Range> > + { static inline constexpr bool value = true; }; > + > + template > + concept __concat_is_random_access = __all_random_access<_Const, _Rs...> > + && __all_but_last_common<_Const, _Rs...>::value; > + > + template > + concept __concat_is_bidirectional = __all_bidirectional<_Const, _Rs...> > + && __all_but_last_common<_Const, _Rs...>::value; > + > + template > + struct __last_is_common > + { static inline constexpr bool value = __last_is_common<_Rs...>::value; }; > + > + template > + struct __last_is_common<_Range> > + { static inline constexpr bool value = common_range<_Range>; }; > + } // namespace __detail > + > + template > + requires (view<_Vs> && ...) && (sizeof...(_Vs) > 0) && __detail::__concatable<_Vs...> > + class concat_view : public view_interface> > + { > + tuple<_Vs...> _M_views; > + > + template class iterator; > + > + public: > + constexpr concat_view() = default; > + > + constexpr explicit > + concat_view(_Vs... __views) > + : _M_views(std::move(__views)...) > + { } > + > + constexpr iterator > + begin() requires(!(__detail::__simple_view<_Vs> && ...)) > + { > + iterator __it(this, in_place_index<0>, ranges::begin(std::get<0>(_M_views))); > + __it.template _M_satisfy<0>(); > + return __it; > + } > + > + constexpr iterator > + begin() const requires (range && ...) && __detail::__concatable > + { > + iterator __it(this, in_place_index<0>, ranges::begin(std::get<0>(_M_views))); > + __it.template _M_satisfy<0>(); > + return __it; > + } > + > + constexpr auto > + end() requires(!(__detail::__simple_view<_Vs> && ...)) > + { > + if constexpr (__detail::__last_is_common<_Vs...>::value) > + { > + constexpr auto __n = sizeof...(_Vs); > + return iterator(this, in_place_index<__n - 1>, > + ranges::end(std::get<__n - 1>(_M_views))); > + } > + else > + return default_sentinel; > + } > + > + constexpr auto > + end() const requires (range && ...) && __detail::__concatable > + { > + if constexpr (__detail::__last_is_common::value) > + { > + constexpr auto __n = sizeof...(_Vs); > + return iterator(this, in_place_index<__n - 1>, > + ranges::end(std::get<__n - 1>(_M_views))); > + } > + else > + return default_sentinel; > + } > + > + constexpr auto > + size() requires (sized_range<_Vs>&&...) > + { > + return std::apply([](auto... __sizes) { > + using _CT = __detail::__make_unsigned_like_t>; > + return (_CT(__sizes) + ...); > + }, __detail::__tuple_transform(ranges::size, _M_views)); > + } > + > + constexpr auto > + size() const requires (sized_range&&...) > + { > + return std::apply([](auto... __sizes) { > + using _CT = __detail::__make_unsigned_like_t>; > + return (_CT(__sizes) + ...); > + }, __detail::__tuple_transform(ranges::size, _M_views)); > + } > + }; > + > + template > + concat_view(_Rs&&...) -> concat_view...>; > + > + namespace __detail > + { > + template > + struct __concat_view_iter_cat > + { }; > + > + template > + requires __detail::__all_forward<_Const, _Vs...> > + struct __concat_view_iter_cat<_Const, _Vs...> > + { > + static auto > + _S_iter_cat() > + { > + if constexpr (!is_reference_v<__concat_reference_t<__maybe_const_t<_Const, _Vs>...>>) > + return input_iterator_tag{}; > + else > + return [](_Cats... __cats) { > + if constexpr ((derived_from<_Cats, random_access_iterator_tag> && ...) > + && __concat_is_random_access<_Const, _Vs...>) > + return random_access_iterator_tag{}; > + else if constexpr ((derived_from<_Cats, bidirectional_iterator_tag> && ...) > + && __concat_is_bidirectional<_Const, _Vs...>) > + return bidirectional_iterator_tag{}; > + else if constexpr ((derived_from<_Cats, forward_iterator_tag> && ...)) > + return forward_iterator_tag{}; > + else > + return input_iterator_tag{}; > + }(typename iterator_traits>> > + ::iterator_category{}...); > + } > + }; > + } > + > + template > + requires (view<_Vs> && ...) && (sizeof...(_Vs) > 0) && __detail::__concatable<_Vs...> > + template > + class concat_view<_Vs...>::iterator > + : public __detail::__concat_view_iter_cat<_Const, _Vs...> > + { > + static auto > + _S_iter_concept() > + { > + if constexpr (__detail::__concat_is_random_access<_Const, _Vs...>) > + return random_access_iterator_tag{}; > + else if constexpr (__detail::__concat_is_bidirectional<_Const, _Vs...>) > + return bidirectional_iterator_tag{}; > + else if constexpr (__detail::__all_forward<_Const, _Vs...>) > + return forward_iterator_tag{}; > + else > + return input_iterator_tag{}; > + } > + > + friend concat_view; > + friend iterator; > + > + public: > + // iterator_category defined in __concat_view_iter_cat > + using iterator_concept = decltype(_S_iter_concept()); > + using value_type = __detail::__concat_value_t<__maybe_const_t<_Const, _Vs>...>; > + using difference_type = common_type_t>...>; > + > + private: > + using __base_iter = variant>...>; > + > + __maybe_const_t<_Const, concat_view>* _M_parent = nullptr; > + __base_iter _M_it; > + > + template > + constexpr void > + _M_satisfy() > + { > + if constexpr (_Nm < (sizeof...(_Vs) - 1)) > + { > + if (std::get<_Nm>(_M_it) == ranges::end(std::get<_Nm>(_M_parent->_M_views))) > + { > + _M_it.template emplace<_Nm + 1>(ranges::begin > + (std::get<_Nm + 1>(_M_parent->_M_views))); > + _M_satisfy<_Nm + 1>(); > + } > + } > + } > + > + template > + constexpr void > + _M_prev() > + { > + if constexpr (_Nm == 0) > + --std::get<0>(_M_it); > + else > + { > + if (std::get<_Nm>(_M_it) == ranges::begin(std::get<_Nm>(_M_parent->_M_views))) > + { > + _M_it.template emplace<_Nm - 1>(ranges::end > + (std::get<_Nm - 1>(_M_parent->_M_views))); > + _M_prev<_Nm - 1>(); > + } > + else > + --std::get<_Nm>(_M_it); > + } > + } > + > + template > + constexpr void > + _M_advance_fwd(difference_type __offset, difference_type __steps) > + { > + using _Dt = iter_difference_t>; > + if constexpr (_Nm == sizeof...(_Vs) - 1) > + std::get<_Nm>(_M_it) += static_cast<_Dt>(__steps); > + else > + { > + auto __n_size = ranges::distance(std::get<_Nm>(_M_parent->_M_views)); > + if (__offset + __steps < __n_size) > + std::get<_Nm>(_M_it) += static_cast<_Dt>(__steps); > + else > + { > + _M_it.template emplace<_Nm + 1>(ranges::begin > + (std::get<_Nm + 1>(_M_parent->_M_views))); > + _M_advance_fwd<_Nm + 1>(0, __offset + __steps - __n_size); > + } > + } > + } > + > + template > + constexpr void > + _M_advance_bwd(difference_type __offset, difference_type __steps) > + { > + using _Dt = iter_difference_t>; > + if constexpr (_Nm == 0) > + std::get<_Nm>(_M_it) -= static_cast<_Dt>(__steps); > + else { > + if (__offset >= __steps) > + std::get<_Nm>(_M_it) -= static_cast<_Dt>(__steps); > + else > + { > + auto __prev_size = ranges::distance(std::get<_Nm - 1>(_M_parent->_M_views)); > + _M_it.template emplace<_Nm - 1>(ranges::end > + (std::get<_Nm - 1>(_M_parent->_M_views))); > + _M_advance_bwd<_Nm - 1>(__prev_size, __steps - __offset); > + } > + } > + } > + > + // Invoke the function object __f, which has a call operator taking a size_t > + // index template parameter (bounded by the number of underlying views), > + // according the runtime value of __index. > + template > + static constexpr auto > + _S_invoke_with_runtime_index(_Fp&& __f, size_t __index) > + { > + return [&__f, __index](this auto&& __self) { > + if (_Idx == __index) > + return __f.template operator()<_Idx>(); > + if constexpr (_Idx + 1 < sizeof...(_Vs)) > + return __self.template operator()<_Idx + 1>(); > + }.template operator()<0>(); > + } > + > + template > + constexpr auto > + _M_invoke_with_runtime_index(_Fp&& __f) > + { return _S_invoke_with_runtime_index(std::forward<_Fp>(__f), _M_it.index()); } > + > + template > + explicit constexpr > + iterator(__maybe_const_t<_Const, concat_view>* __parent, _Args&&... __args) > + requires constructible_from<__base_iter, _Args&&...> > + : _M_parent(__parent), _M_it(std::forward<_Args>(__args)...) > + { } > + > + public: > + iterator() = default; > + > + constexpr > + iterator(iterator __it) > + requires _Const && (convertible_to, iterator_t> && ...) > + : _M_parent(__it._M_parent) > + { > + _M_invoke_with_runtime_index([this, &__it]() { > + _M_it.template emplace<_Idx>(std::get<_Idx>(std::move(__it._M_it))); > + }); > + } > + > + constexpr decltype(auto) > + operator*() const > + { > + __glibcxx_assert(!_M_it.valueless_by_exception()); > + using reference = __detail::__concat_reference_t<__maybe_const_t<_Const, _Vs>...>; > + return std::visit([](auto&& __it) -> reference { return *__it; }, _M_it); > + } > + > + constexpr iterator& > + operator++() > + { > + _M_invoke_with_runtime_index([this]() { > + ++std::get<_Idx>(_M_it); > + _M_satisfy<_Idx>(); > + }); > + return *this; > + } > + > + constexpr void > + operator++(int) > + { > + ++*this; > + } > + > + constexpr iterator > + operator++(int) > + requires __detail::__all_forward<_Const, _Vs...> > + { > + auto __tmp = *this; > + ++*this; > + return __tmp; > + } > + > + constexpr iterator& > + operator--() > + requires __detail::__concat_is_bidirectional<_Const, _Vs...> > + { > + __glibcxx_assert(!_M_it.valueless_by_exception()); > + _M_invoke_with_runtime_index([this]() { > + _M_prev<_Idx>(); > + }); > + return *this; > + } > + > + constexpr iterator > + operator--(int) > + requires __detail::__concat_is_bidirectional<_Const, _Vs...> > + { > + auto __tmp = *this; > + --*this; > + return __tmp; > + } > + > + constexpr iterator& > + operator+=(difference_type __n) > + requires __detail::__concat_is_random_access<_Const, _Vs...> > + { > + __glibcxx_assert(!_M_it.valueless_by_exception()); > + _M_invoke_with_runtime_index([this, __n]() { > + auto __begin = ranges::begin(std::get<_Idx>(_M_parent->_M_views)); > + if (__n > 0) > + _M_advance_fwd<_Idx>(std::get<_Idx>(_M_it) - __begin, __n); > + else if (__n < 0) > + _M_advance_bwd<_Idx>(std::get<_Idx>(_M_it) - __begin, -__n); > + }); > + return *this; > + } > + > + constexpr iterator& > + operator-=(difference_type __n) > + requires __detail::__concat_is_random_access<_Const, _Vs...> > + { > + *this += -__n; > + return *this; > + } > + > + constexpr decltype(auto) > + operator[](difference_type __n) const > + requires __detail::__concat_is_random_access<_Const, _Vs...> > + { return *((*this) + __n); } > + > + friend constexpr bool > + operator==(const iterator& __x, const iterator& __y) > + requires (equality_comparable>> && ...) > + { > + __glibcxx_assert(!__x._M_it.valueless_by_exception()); > + __glibcxx_assert(!__y._M_it.valueless_by_exception()); > + return __x._M_it == __y._M_it; > + } > + > + friend constexpr bool > + operator==(const iterator& __it, default_sentinel_t) > + { > + __glibcxx_assert(!__it._M_it.valueless_by_exception()); > + constexpr auto __last_idx = sizeof...(_Vs) - 1; > + return (__it._M_it.index() == __last_idx > + && (std::get<__last_idx>(__it._M_it) > + == ranges::end(std::get<__last_idx>(__it._M_parent->_M_views)))); > + } > + > + friend constexpr bool > + operator<(const iterator& __x, const iterator& __y) > + requires __detail::__all_random_access<_Const, _Vs...> > + { return __x._M_it < __y._M_it; } > + > + friend constexpr bool > + operator>(const iterator& __x, const iterator& __y) > + requires __detail::__all_random_access<_Const, _Vs...> > + { return __x._M_it > __y._M_it; } > + > + friend constexpr bool > + operator<=(const iterator& __x, const iterator& __y) > + requires __detail::__all_random_access<_Const, _Vs...> > + { return __x._M_it <= __y._M_it; } > + > + friend constexpr bool > + operator>=(const iterator& __x, const iterator& __y) > + requires __detail::__all_random_access<_Const, _Vs...> > + { return __x._M_it >= __y._M_it; } > + > + friend constexpr auto > + operator<=>(const iterator& __x, const iterator& __y) > + requires (__detail::__all_random_access<_Const, _Vs...> > + && (three_way_comparable>> > + && ...)) > + { return __x._M_it <=> __y._M_it; } > + > + friend constexpr iterator > + operator+(const iterator& __it, difference_type __n) > + requires __detail::__concat_is_random_access<_Const, _Vs...> > + { return auto(__it) += __n; } > + > + friend constexpr iterator > + operator+(difference_type __n, const iterator& __it) > + requires __detail::__concat_is_random_access<_Const, _Vs...> > + { return __it + __n; } > + > + friend constexpr iterator > + operator-(const iterator& __it, difference_type __n) > + requires __detail::__concat_is_random_access<_Const, _Vs...> > + { return auto(__it) -= __n; } > + > + friend constexpr difference_type > + operator-(const iterator& __x, const iterator& __y) > + requires __detail::__concat_is_random_access<_Const, _Vs...> > + { > + return _S_invoke_with_runtime_index([&]() -> difference_type { > + return _S_invoke_with_runtime_index([&]() -> difference_type { > + if constexpr (_Ix > _Iy) > + { > + auto __dy = ranges::distance(std::get<_Iy>(__y._M_it), > + ranges::end(std::get<_Iy>(__y._M_parent > + ->_M_views))); > + auto __dx = ranges::distance(ranges::begin(std::get<_Ix>(__x._M_parent > + ->_M_views)), > + std::get<_Ix>(__x._M_it)); > + difference_type __sum = 0; > + [&](this auto&& __self) { > + if constexpr (_Idx < _Ix) > + { > + __sum += ranges::size(std::get<_Idx>(__x._M_parent->_M_views)); > + __self.template operator()<_Idx + 1>(); > + } > + }(); > + return __dy + __sum + __dx; > + } > + else if constexpr (_Ix < _Iy) > + return -(__y - __x); > + else > + return std::get<_Ix>(__x._M_it) - std::get<_Iy>(__y._M_it); > + }, __y._M_it.index()); > + }, __x._M_it.index()); > + } > + > + friend constexpr difference_type > + operator-(const iterator& __x, default_sentinel_t) > + requires __detail::__concat_is_random_access<_Const, _Vs...> > + && __detail::__last_is_common<__maybe_const_t<_Const, _Vs>...>::value > + { > + return _S_invoke_with_runtime_index([&]() -> difference_type { > + auto __dx = ranges::distance(std::get<_Ix>(__x._M_it), > + ranges::end(std::get<_Ix>(__x._M_parent->_M_views))); > + difference_type __sum = 0; > + [&](this auto&& __self) { > + if constexpr (_Idx < sizeof...(_Vs)) > + { > + __sum += ranges::size(std::get<_Idx>(__x._M_parent->_M_views)); > + __self.template operator()<_Idx + 1>(); > + } > + }(); > + return -(__dx + __sum); > + }, __x._M_it.index()); > + } > + > + friend constexpr difference_type > + operator-(default_sentinel_t, const iterator& __x) > + requires __detail::__concat_is_random_access<_Const, _Vs...> > + && __detail::__last_is_common<__maybe_const_t<_Const, _Vs>...>::value > + { return -(__x - default_sentinel); } > + > + friend constexpr decltype(auto) > + iter_move(const iterator& __it) > + { > + using _Res = __detail::__concat_rvalue_reference_t<__maybe_const_t<_Const, _Vs>...>; > + return std::visit([](const auto& __i) -> _Res { > + return ranges::iter_move(__i); > + }, __it._M_it); > + } > + > + friend constexpr void > + iter_swap(const iterator& __x, const iterator& __y) > + requires swappable_with, > + iter_reference_t> > + && (... && indirectly_swappable>>) > + { > + std::visit([&](const auto& __it1, const auto& __it2) { > + if constexpr (is_same_v) > + ranges::iter_swap(__it1, __it2); > + else > + ranges::swap(*__it1, *__it2); > + }, __x._M_it, __y._M_it); > + } > + }; > + > + namespace views > + { > + namespace __detail > + { > + template > + concept __can_concat_view = requires { concat_view(std::declval<_Ts>()...); }; > + } > + > + struct _Concat > + { > + template > + requires __detail::__can_concat_view<_Ts...> > + constexpr auto > + operator() [[nodiscard]] (_Ts&&... __ts) const > + { > + if constexpr (sizeof...(_Ts) == 1) > + return views::all(std::forward<_Ts>(__ts)...); > + else > + return concat_view(std::forward<_Ts>(__ts)...); > + } > + }; > + > + inline constexpr _Concat concat; > + } > + > +} // namespace ranges > +#endif // __cpp_lib_ranges_concat > + > _GLIBCXX_END_NAMESPACE_VERSION > } // namespace std > #endif // library concepts > diff --git a/libstdc++-v3/testsuite/std/ranges/concat/1.cc b/libstdc++-v3/testsuite/std/ranges/concat/1.cc > new file mode 100644 > index 00000000000..91c8893cdc5 > --- /dev/null > +++ b/libstdc++-v3/testsuite/std/ranges/concat/1.cc > @@ -0,0 +1,61 @@ > +// { dg-do run { target c++26 } } > +// { dg-add-options no_pch } > + > +#include > + > +#if __cpp_lib_ranges_concat != 202403L > +# error "Feature-test macro __cpp_lib_ranges_concat has wrong value in " > +#endif > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +namespace ranges = std::ranges; > +namespace views = std::views; > + > +constexpr bool > +test01() > +{ > + std::vector v1{1, 2, 3}, v2{4, 5}, v3{}; > + std::array a{6, 7, 8}; > + auto s = views::single(9); > + > + auto v = views::concat(v1, v2, v3, a, s); > + VERIFY( ranges::size(v) == 9 ); > + VERIFY( ranges::equal(v, views::iota(1, 10)) ); > + VERIFY( ranges::equal(v, views::iota(1, 10)) ); > + VERIFY( ranges::equal(v | views::reverse, > + views::iota(1, 10) | views::reverse) ); > + > + auto it0 = v.begin(); > + auto cit = std::as_const(v).begin(); > + VERIFY( it0 == it0 ); > + VERIFY( cit == cit ); > + VERIFY( it0 == cit ); > + VERIFY( it0 - it0 == 0); > + for (int i = 0; i < 10; i++) > + { > + VERIFY( it0 + i - it0 == i); > + VERIFY( it0 + i - i + i == it0 + i ); > + VERIFY( it0 + i - (it0 + i) == 0 ); > + } > + VERIFY( std::default_sentinel - it0 == 9 ); > + VERIFY( it0 + 9 == std::default_sentinel ); > + > + auto it5 = it0+5; > + ranges::iter_swap(it0, it5); > + VERIFY( *it0 == 6 && *it5 == 1 ); > + ranges::iter_swap(it0, it5); > + *it0 = ranges::iter_move(it0); > + return true; > +} > + > +int > +main() > +{ > + static_assert(test01()); > +} > -- > 2.45.0.rc0 >