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 205713858D38 for ; Mon, 22 Apr 2024 21:42:37 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 205713858D38 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 205713858D38 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=1713822163; cv=none; b=jEf9shhY4ykMhpID7f9jOpyvy1qYzSDUk/PcV7HlDFlZzGDdHlyIBbVHNKrK2jogAOI534xkjvsnA1UrxPP/Zm0KHNLcgZtPOfufrWYYOH8fiDyT6Im6yJK/3yinplWz0Og9XTfiEUwk2cSsY9M0reDePl/uwsCDSc6seHLbq1o= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1713822163; c=relaxed/simple; bh=ZEWSQrog2QkZN1ArmPDEAvdpv3GjEiXqAEkYxbggs9I=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=j4Pwu7B4CHNY2dQHdgnwSusvubM8rXOSbtGos2CZ5qwitRV3B1NLQBELZM5wFWYxJqsFhe8yv0FAml+EeMh3Esk6nKs53x8Fei4Sis4N6MSFgvw/bRBcL6axFcYpyygaKAtSLoAhaNIFOwajEiMCRyvhqSeeaVUXF22QWwXrO+M= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1713822156; 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=iYm8UZh4RrhbDKz/JVqM3KWIctJTu8I7B/QENf5LIIA=; b=VINfyi2z8klfoIIa+EbjWIuFQSVtNrsr8POsg7xiCmwFfH996fglnmWJURsz3NOkB6yNHQ b9GcAtFOGrmOhMVZNY6F+AS4nt30bXH3ZRU76164JYWrn5A2m17fC3hFodzyMmYmsGylU3 X+sJKjFjZAfahjZsSmS3cWO+y3IsMOA= Received: from mail-oi1-f197.google.com (mail-oi1-f197.google.com [209.85.167.197]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-453-83wLKSRONq-EftchMToRQg-1; Mon, 22 Apr 2024 17:42:35 -0400 X-MC-Unique: 83wLKSRONq-EftchMToRQg-1 Received: by mail-oi1-f197.google.com with SMTP id 5614622812f47-3c6ef2ea691so7244187b6e.0 for ; Mon, 22 Apr 2024 14:42:35 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1713822154; x=1714426954; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=iYm8UZh4RrhbDKz/JVqM3KWIctJTu8I7B/QENf5LIIA=; b=MPIRkLdb9y2efPnClDLHM6FDPHvS4952YRNF6dnLDl/qcr88sxWplI8fSKi+oFQoP+ joFj09hyo0e07zhgiZmeXAM7XXat/FvxMJE7IHT+zSmrt6U13zSScjZLWKvEmQqfPKOZ HAiItp6wDmj2P4ruoMenE7KKMOGf368R1Opg58C6KybopUwQ9Xe90ij8fZ1AI+SuZdIT X2bmE2oFrM1rTfcr4uDn0ZhsMa3P1WSu1mzvEDaPwNvS8fz5Huf2UUsrNj+z4ZnLvyfa 9BoEFD/7OIsHSLAElV+nr5UT/8bAPxUZXLZW9yLLJWUMI+PHDfurY/HmO7r9id2vN2bB q1aw== X-Gm-Message-State: AOJu0YzZfMgXfvRfouvnmmaMzItDUQPWM+BAQJF1e7N/P0CMgS0BHpPd +iAfTxN32YOAB9YBZ4J4Ce/c45qN+1h+K+vY31LmF6kXI6kUgNbmrlRD1C6WuKP9h7w0zQRUs/O egPSwZcYowFRDWCCdkx9JPARWkrdiHGsbJa7ZoEJRE5ylqcyRgIeI X-Received: by 2002:aca:240c:0:b0:3c5:ed16:c0fc with SMTP id n12-20020aca240c000000b003c5ed16c0fcmr11838658oic.52.1713822154258; Mon, 22 Apr 2024 14:42:34 -0700 (PDT) X-Google-Smtp-Source: AGHT+IF+9NFXkZ1d+BjVPAJZnaQN/em5/Zt2UYdFDqAvb2Uk3pVADM/HRyG/V4FtCja+yEyNMoR3UQ== X-Received: by 2002:aca:240c:0:b0:3c5:ed16:c0fc with SMTP id n12-20020aca240c000000b003c5ed16c0fcmr11838642oic.52.1713822153793; Mon, 22 Apr 2024 14:42:33 -0700 (PDT) Received: from localhost.localdomain (ool-457670bb.dyn.optonline.net. [69.118.112.187]) by smtp.gmail.com with ESMTPSA id s11-20020a0c8d4b000000b0069ba200fd5csm3313822qvb.70.2024.04.22.14.42.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 22 Apr 2024 14:42:33 -0700 (PDT) From: Patrick Palka To: gcc-patches@gcc.gnu.org Cc: libstdc++@gcc.gnu.org, Patrick Palka Subject: [PATCH] libstdc++: Implement ranges::concat_view from P2542R7 Date: Mon, 22 Apr 2024 17:42:30 -0400 Message-ID: <20240422214231.3530630-1-ppalka@redhat.com> X-Mailer: git-send-email 2.45.0.rc0 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=-12.9 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,RCVD_IN_SORBS_WEB,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: 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. -- >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