Message ID | 20250310222713.3045246-1-ppalka@redhat.com |
---|---|
State | Committed |
Headers |
Return-Path: <gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org> X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 5ED183858C2F for <patchwork@sourceware.org>; Mon, 10 Mar 2025 22:28:40 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 5ED183858C2F Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=f8aDqoyt X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id 80ED03858D38 for <gcc-patches@gcc.gnu.org>; Mon, 10 Mar 2025 22:27:21 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 80ED03858D38 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 80ED03858D38 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=1741645641; cv=none; b=p62JQeFIDWzTjp6wSYBG/4wqyXoIU9y3jSzLOl7diXk3zEkrMY6E86qA3Qt9/KBUWzpvfy28wyqQ8NwV3WVvQ2QThPJvh3ma3XD2VZr/cQ4Jrclpla/mdHdSzJ/jvl+DFr9UNDinhCYRikOGrA/Gv+2topNUQmep/CEADAEBYxY= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1741645641; c=relaxed/simple; bh=Q39gEidVRUew+UjDG2p4xmeG1nMZrRvLw+7ZQyZ57OY=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=T5YDPZgJPhFa5WQLIhhFlMqN60AelqTi1byiyCNF7crYJjYbxh0Qc45lqGLjGFYzoqMEBfX1R7o3L1gu69Qrrmg7NfGwRsM0eX1lOnYJHYhCOOX9DWeIPjEGH8+miRmYiM5jmsCENMjdRRSvzHukDRJaAC9okplkiSCXax3nAZo= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 80ED03858D38 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1741645641; 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=1eNwMbEPUVnODwMgdHIbVabTNSNePGxMm5i/pmsspF4=; b=f8aDqoytTiVF7Xs58Nnfh8CykFw72vDVS9bPztuzJcDRY02HK6xbMD0qnfP9ufYmCktfpa RsEO54/B/MvayK5WjqtJzjRKLzR+P/Sm7/4Y6KKu2W3meGsBqHnN8CnUX1gD2OVqtZRvW0 51iinQoAYnX/Y0nWllwbShtNf1HMK3c= Received: from mail-qv1-f69.google.com (mail-qv1-f69.google.com [209.85.219.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-231-PYVuSenZPQahpaXY7Po6FQ-1; Mon, 10 Mar 2025 18:27:19 -0400 X-MC-Unique: PYVuSenZPQahpaXY7Po6FQ-1 X-Mimecast-MFC-AGG-ID: PYVuSenZPQahpaXY7Po6FQ_1741645639 Received: by mail-qv1-f69.google.com with SMTP id 6a1803df08f44-6e19bfc2025so12778466d6.1 for <gcc-patches@gcc.gnu.org>; Mon, 10 Mar 2025 15:27:19 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741645638; x=1742250438; 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=1eNwMbEPUVnODwMgdHIbVabTNSNePGxMm5i/pmsspF4=; b=mKr8g3ZVTJ3R0ep6SxSoIx/OZdJ1Y29Mr6LUXeXxaf7Y6DEj8rjQq/xNT03doh8zKf u5+vZRDzBUx69nhOLq0Vuuo3RAt80AaAXRViirofY8BbHJoCuUzGweZlSf2nanALz1Jj o5MahEtKavZvKVZgsgG/N22XYK+R5UKdFuCGPfO7bIz4ZUegYUdmt/N5ALtnNcEUB2Ix 7eobNgwdS+zwSbqTnc/2ArBNML1m7ksxLMTgeB3B81XrTGbu1gj52m1Xt/0C+EqXRHEt 2c8CbtePYKJPsuHaJkxZNR+biYVkPsBlIuD0oFCb3x8sw2I1KPeqSIjwQl4JGwIsSbW6 nTGA== X-Gm-Message-State: AOJu0Yy/Xi3wSyG2QPh+pbY1sUmuKyEuL2cjCVczDY3ChVx8vzN15ZrJ UgBVDYtihZe7jCXMNSsQmyJxiche4TSpaoXEmNyBBt/ek9nA12jwhAe7CopacXahLoVrwLIP0ry k0LguQx3i50ZML2tkN61dnNnlZeNJ3ltOqROavA5dIXXS5qQqifn6mZbErL1Gccesd2UWh0X90R P+/MMYHvkvvDFQMmZGvC7PqPLpOmrHF1e4IuLO X-Gm-Gg: ASbGncut3ITkyoVNMApFtmENso+fmUncxnSnsjvXFz+LhuRBQBQkFlukyFc+//7vCbG 6JW9np4V5DJO9VDRkt1/xai2icW3YiBI20f4BZmFZKwbbwwfp9npch4DCGEI4H54h36pYSf0UJH 2RZign7j3a3WYMgX2nXCd5TOvMU7lflf0mA5GQRGHjnEdo6w01rds75aNvxhHlvQcXi8TtDxh6M W2sWeu5xrXTjLhWN4JBX6d/JtEnzMMcd2ApRFZ5RAi51/5hwunJCDSAJDXjwcK2LoOyL6oGyI7n S5mU8DA72O7Nq69ZcQ5XQzE7PG8J62mLkSdCJY2+lB5Kq3Ea3iyJ9V4= X-Received: by 2002:a05:622a:5b8a:b0:474:e4bd:834 with SMTP id d75a77b69052e-47666580ba9mr56998881cf.2.1741645637672; Mon, 10 Mar 2025 15:27:17 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEZxp2+jfAuxlTXI1BDG50PMbbmiNVgGR0/IQb5mkMzx83xMZZ034CQDJsd2eyPu7iqxpZh6w== X-Received: by 2002:a05:622a:5b8a:b0:474:e4bd:834 with SMTP id d75a77b69052e-47666580ba9mr56998761cf.2.1741645637234; Mon, 10 Mar 2025 15:27:17 -0700 (PDT) Received: from localhost.localdomain (ool-18bb2a2e.dyn.optonline.net. [24.187.42.46]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-476747bb85esm31877601cf.58.2025.03.10.15.27.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 10 Mar 2025 15:27:16 -0700 (PDT) From: Patrick Palka <ppalka@redhat.com> To: gcc-patches@gcc.gnu.org Cc: libstdc++@gcc.gnu.org, Patrick Palka <ppalka@redhat.com> Subject: [PATCH] libstdc++: Implement P3137R3 views::to_input for C++26 Date: Mon, 10 Mar 2025 18:27:13 -0400 Message-ID: <20250310222713.3045246-1-ppalka@redhat.com> X-Mailer: git-send-email 2.49.0.rc1.37.ge969bc8759 MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: v1vt0IhS0fS3FIbT4rHFZ4Mt4pPInl4zqPdgeHn3gqA_1741645639 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit content-type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-13.5 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H5, RCVD_IN_MSPIKE_WL, 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 X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list <gcc-patches.gcc.gnu.org> List-Unsubscribe: <https://gcc.gnu.org/mailman/options/gcc-patches>, <mailto:gcc-patches-request@gcc.gnu.org?subject=unsubscribe> List-Archive: <https://gcc.gnu.org/pipermail/gcc-patches/> List-Post: <mailto:gcc-patches@gcc.gnu.org> List-Help: <mailto:gcc-patches-request@gcc.gnu.org?subject=help> List-Subscribe: <https://gcc.gnu.org/mailman/listinfo/gcc-patches>, <mailto:gcc-patches-request@gcc.gnu.org?subject=subscribe> Errors-To: gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org |
Series |
libstdc++: Implement P3137R3 views::to_input for C++26
|
|
Commit Message
Patrick Palka
March 10, 2025, 10:27 p.m. UTC
Tested on x86_64-pc-linux-gnu, does this look OK for trunk? -- >8 -- libstdc++-v3/ChangeLog: * include/bits/version.def (ranges_to_input): Define. * include/bits/version.h: Regenerate. * include/std/ranges (ranges::to_input_view): Define for C++26. (views::__detail::__can_to_input): Likewise. (views::_ToInput, views::to_input): Likewise. * testsuite/std/ranges/adaptors/to_input/1.cc: New test. --- libstdc++-v3/include/bits/version.def | 8 + libstdc++-v3/include/bits/version.h | 10 ++ libstdc++-v3/include/std/ranges | 170 ++++++++++++++++++ .../std/ranges/adaptors/to_input/1.cc | 58 ++++++ 4 files changed, 246 insertions(+) create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc
Comments
On Mon, Mar 10, 2025 at 11:28 PM Patrick Palka <ppalka@redhat.com> wrote: > Tested on x86_64-pc-linux-gnu, does this look OK for trunk? > > -- >8 -- > > libstdc++-v3/ChangeLog: > > * include/bits/version.def (ranges_to_input): Define. > * include/bits/version.h: Regenerate. > * include/std/ranges (ranges::to_input_view): Define for C++26. > (views::__detail::__can_to_input): Likewise. > (views::_ToInput, views::to_input): Likewise. > * testsuite/std/ranges/adaptors/to_input/1.cc: New test. > --- > libstdc++-v3/include/bits/version.def | 8 + > libstdc++-v3/include/bits/version.h | 10 ++ > libstdc++-v3/include/std/ranges | 170 ++++++++++++++++++ > .../std/ranges/adaptors/to_input/1.cc | 58 ++++++ > 4 files changed, 246 insertions(+) > create mode 100644 > libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > > diff --git a/libstdc++-v3/include/bits/version.def > b/libstdc++-v3/include/bits/version.def > index 2af5a54bff2..c2b5283df89 100644 > --- a/libstdc++-v3/include/bits/version.def > +++ b/libstdc++-v3/include/bits/version.def > @@ -1910,6 +1910,14 @@ ftms = { > }; > }; > > +ftms = { > + name = ranges_to_input; > + values = { > + v = 202502; > + cxxmin = 26; > + }; > +}; > + > ftms = { > name = to_string; > values = { > diff --git a/libstdc++-v3/include/bits/version.h > b/libstdc++-v3/include/bits/version.h > index 9833023cfdc..775c8642139 100644 > --- a/libstdc++-v3/include/bits/version.h > +++ b/libstdc++-v3/include/bits/version.h > @@ -2120,6 +2120,16 @@ > #endif /* !defined(__cpp_lib_text_encoding) && > defined(__glibcxx_want_text_encoding) */ > #undef __glibcxx_want_text_encoding > > +#if !defined(__cpp_lib_ranges_to_input) > +# if (__cplusplus > 202302L) > +# define __glibcxx_ranges_to_input 202502L > +# if defined(__glibcxx_want_all) || > defined(__glibcxx_want_ranges_to_input) > +# define __cpp_lib_ranges_to_input 202502L > +# endif > +# endif > +#endif /* !defined(__cpp_lib_ranges_to_input) && > defined(__glibcxx_want_ranges_to_input) */ > +#undef __glibcxx_want_ranges_to_input > + > #if !defined(__cpp_lib_to_string) > # if (__cplusplus > 202302L) && _GLIBCXX_HOSTED && (__glibcxx_to_chars) > # define __glibcxx_to_string 202306L > diff --git a/libstdc++-v3/include/std/ranges > b/libstdc++-v3/include/std/ranges > index e21f5284b46..dd97d276ef0 100644 > --- a/libstdc++-v3/include/std/ranges > +++ b/libstdc++-v3/include/std/ranges > @@ -69,6 +69,7 @@ > #define __glibcxx_want_ranges_slide > #define __glibcxx_want_ranges_stride > #define __glibcxx_want_ranges_to_container > +#define __glibcxx_want_ranges_to_input > #define __glibcxx_want_ranges_zip > #include <bits/version.h> > > @@ -10390,6 +10391,175 @@ namespace ranges > } // namespace ranges > #endif // __cpp_lib_ranges_cache_latest > > +#if __cpp_lib_ranges_to_input // C++ >= 26 > +namespace ranges > +{ > + template<input_range _Vp> > + requires view<_Vp> > + class to_input_view : public view_interface<to_input_view<_Vp>> > + { > + _Vp _M_base = _Vp(); > + > + template<bool _Const> > + class _Iterator; > + > + public: > + to_input_view() requires default_initializable<_Vp> = default; > + > + constexpr explicit > + to_input_view(_Vp __base) > + : _M_base(std::move(__base)) > + { } > + > + constexpr _Vp > + base() const & requires copy_constructible<_Vp> > + { return _M_base; } > + > + constexpr _Vp > + base() && > + { return std::move(_M_base); } > + > + constexpr auto > + begin() requires (!__detail::__simple_view<_Vp>) > + { return _Iterator<false>(ranges::begin(_M_base)); } > + > + constexpr auto > + begin() const requires range<const _Vp> > + { return _Iterator<true>(ranges::begin(_M_base)); } > + > + constexpr auto > + end() requires (!__detail::__simple_view<_Vp>) > + { return ranges::end(_M_base); } > + > + constexpr auto > + end() const requires range<const _Vp> > + { return ranges::end(_M_base); } > + > + constexpr auto > + size() requires sized_range<_Vp> > + { return ranges::size(_M_base); } > + > + constexpr auto > + size() const requires sized_range<const _Vp> > + { return ranges::size(_M_base); } > + }; > + > + template<typename _Range> > + to_input_view(_Range&&) -> to_input_view<views::all_t<_Range>>; > + > + template<input_range _Vp> > + requires view<_Vp> > + template<bool _Const> > + class to_input_view<_Vp>::_Iterator > + { > + using _Base = __maybe_const_t<_Const, _Vp>; > + > + iterator_t<_Base> _M_current = iterator_t<_Base>(); > + > + constexpr explicit > + _Iterator(iterator_t<_Base> __current) > + : _M_current(std::move(__current)) > + { } > + > + friend to_input_view; > + friend _Iterator<!_Const>; > + > + public: > + using difference_type = range_difference_t<_Base>; > + using value_type = range_value_t<_Base>; > + using iterator_concept = input_iterator_tag; > + > + _Iterator() requires default_initializable<iterator_t<_Base>> = > default; > + > + _Iterator(_Iterator&&) = default; > + _Iterator& operator=(_Iterator&&) = default; > + > + constexpr > + _Iterator(_Iterator<!_Const> __i) > + requires _Const && convertible_to<iterator_t<_Vp>, > iterator_t<_Base>> > + : _M_current(std::move(__i._M_current)) > + { } > + > + constexpr iterator_t<_Base> > + base() && > + { return std::move(_M_current); } > + > + constexpr const iterator_t<_Base>& > + base() const & noexcept > + { return _M_current; } > + > + constexpr decltype(auto) > + operator*() const > + { return *_M_current; } > + > + constexpr _Iterator& > + operator++() > + { > + ++_M_current; > + return *this; > + } > + > + constexpr void > + operator++(int) > + { ++*this; } > + > + friend constexpr bool > + operator==(const _Iterator& __x, const sentinel_t<_Base>& __y) > + { return __x._M_current == __y; } > + > + friend constexpr difference_type > + operator-(const sentinel_t<_Base>& __y, const _Iterator& __x) > + requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>> > + { return __y - __x._M_current; } > + > + friend constexpr difference_type > + operator-(const _Iterator& __x, const sentinel_t<_Base>& __y) > + requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>> > + { return __x._M_current - __y; } > + > + friend constexpr range_rvalue_reference_t<_Base> > + iter_move(const _Iterator& __i) > + noexcept(noexcept(ranges::iter_move(__i._M_current))) > + { return ranges::iter_move(__i._M_current); } > + > + friend constexpr void > + iter_swap(const _Iterator& __x, const _Iterator& __y) > + noexcept(noexcept(ranges::iter_swap(__x._M_current, > __y._M_current))) > + requires indirectly_swappable<iterator_t<_Base>> > + { ranges::iter_swap(__x._M_current, __y._M_current); } > + }; > + > + namespace views > + { > + namespace __detail > + { > + template<typename _Tp> > + concept __can_to_input = requires { > to_input_view(std::declval<_Tp>()); }; > + } > + > + struct _ToInput : __adaptor::_RangeAdaptorClosure<_ToInput> > + { > + template<viewable_range _Range> > + requires __detail::__can_to_input<_Range> > + constexpr auto > + operator() [[nodiscard]] (_Range&& __r) const > + { > + if constexpr (input_range<_Range> > + && !common_range<_Range> > + && !forward_range<_Range>) > + return views::all(__r); > + else > + return to_input_view(std::forward<_Range>(__r)); > + } > + > + static constexpr bool _S_has_simple_call_op = true; > + }; > + > + inline constexpr _ToInput to_input; > + } > +} // namespace ranges > +#endif // __cpp_lib_ranges_to_input > + > _GLIBCXX_END_NAMESPACE_VERSION > } // namespace std > #endif // library concepts > diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > b/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > new file mode 100644 > index 00000000000..1e43281adb4 > --- /dev/null > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > @@ -0,0 +1,58 @@ > +// { dg-do run { target c++26 } } > + > +#include <ranges> > + > +#if __cpp_lib_ranges_to_input != 202502L > +# error "Feature-test macro __cpp_lib_ranges_to_input has wrong value in > <ranges>" > +#endif > + > +#include <algorithm> > +#include <vector> > +#include <testsuite_hooks.h> > +#include <testsuite_iterators.h> > + > +namespace ranges = std::ranges; > +namespace views = std::views; > + > +void > +test01() > +{ > + std::vector<int> r{1,2,3}; > + auto v = r | views::to_input; > + using type = decltype(v); > + static_assert( ranges::input_range<type> && > !ranges::forward_range<type> ); > + > + VERIFY( ranges::equal(v.base(), r) ); > + VERIFY( v.size() == r.size() ); > + VERIFY( v.end() == r.end() ); > + auto it = v.begin(); > + VERIFY( it != r.end() ); > + *it = 42; > + ++it; > + *it = 43; > + it++; > + ranges::iter_swap(v.begin(), it); > As far as I can tell this is not invoking the iter_swap friend function, and instead defaulting to swap by 3 iter_moves. We cannot copy the iterator, but you could replace v.begin(), with views::to_input(v).begin() to call the overload, and have the same effect. > + VERIFY( ranges::equal(r, (int[]){3,43,42}) ); > + *it = ranges::iter_move(it); > + VERIFY( it == r.begin() + 2 ); > + VERIFY( r.end() - it == 1 ); > + VERIFY( it - r.end() == -1 ); > +} > + > +void > +test02() > +{ > + int x[] = {1,2,3}; > + __gnu_test::test_input_range<int> rx(x); > + static_assert( !ranges::common_range<decltype(rx)> ); > + auto v = rx | views::to_input; > + using type = decltype(v); > + using type = ranges::ref_view<decltype(rx)>; > I would prefer a more direct representation of this check, as static_assert(std::is_same_v<decltype(views::all(rx)), decltype())>); > +} > + > +int > +main() > +{ > + test01(); > + test02(); > +} > -- > 2.49.0.rc1.37.ge969bc8759 > >
On Mon, Mar 10, 2025 at 11:28 PM Patrick Palka <ppalka@redhat.com> wrote: > Tested on x86_64-pc-linux-gnu, does this look OK for trunk? > > -- >8 -- > > libstdc++-v3/ChangeLog: > > * include/bits/version.def (ranges_to_input): Define. > * include/bits/version.h: Regenerate. > * include/std/ranges (ranges::to_input_view): Define for C++26. > (views::__detail::__can_to_input): Likewise. > (views::_ToInput, views::to_input): Likewise. > * testsuite/std/ranges/adaptors/to_input/1.cc: New test. > --- > libstdc++-v3/include/bits/version.def | 8 + > libstdc++-v3/include/bits/version.h | 10 ++ > libstdc++-v3/include/std/ranges | 170 ++++++++++++++++++ > .../std/ranges/adaptors/to_input/1.cc | 58 ++++++ > 4 files changed, 246 insertions(+) > create mode 100644 > libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > > diff --git a/libstdc++-v3/include/bits/version.def > b/libstdc++-v3/include/bits/version.def > index 2af5a54bff2..c2b5283df89 100644 > --- a/libstdc++-v3/include/bits/version.def > +++ b/libstdc++-v3/include/bits/version.def > @@ -1910,6 +1910,14 @@ ftms = { > }; > }; > > +ftms = { > + name = ranges_to_input; > + values = { > + v = 202502; > + cxxmin = 26; > + }; > +}; > + > ftms = { > name = to_string; > values = { > diff --git a/libstdc++-v3/include/bits/version.h > b/libstdc++-v3/include/bits/version.h > index 9833023cfdc..775c8642139 100644 > --- a/libstdc++-v3/include/bits/version.h > +++ b/libstdc++-v3/include/bits/version.h > @@ -2120,6 +2120,16 @@ > #endif /* !defined(__cpp_lib_text_encoding) && > defined(__glibcxx_want_text_encoding) */ > #undef __glibcxx_want_text_encoding > > +#if !defined(__cpp_lib_ranges_to_input) > +# if (__cplusplus > 202302L) > +# define __glibcxx_ranges_to_input 202502L > +# if defined(__glibcxx_want_all) || > defined(__glibcxx_want_ranges_to_input) > +# define __cpp_lib_ranges_to_input 202502L > +# endif > +# endif > +#endif /* !defined(__cpp_lib_ranges_to_input) && > defined(__glibcxx_want_ranges_to_input) */ > +#undef __glibcxx_want_ranges_to_input > + > #if !defined(__cpp_lib_to_string) > # if (__cplusplus > 202302L) && _GLIBCXX_HOSTED && (__glibcxx_to_chars) > # define __glibcxx_to_string 202306L > diff --git a/libstdc++-v3/include/std/ranges > b/libstdc++-v3/include/std/ranges > index e21f5284b46..dd97d276ef0 100644 > --- a/libstdc++-v3/include/std/ranges > +++ b/libstdc++-v3/include/std/ranges > @@ -69,6 +69,7 @@ > #define __glibcxx_want_ranges_slide > #define __glibcxx_want_ranges_stride > #define __glibcxx_want_ranges_to_container > +#define __glibcxx_want_ranges_to_input > #define __glibcxx_want_ranges_zip > #include <bits/version.h> > > @@ -10390,6 +10391,175 @@ namespace ranges > } // namespace ranges > #endif // __cpp_lib_ranges_cache_latest > > +#if __cpp_lib_ranges_to_input // C++ >= 26 > +namespace ranges > +{ > + template<input_range _Vp> > + requires view<_Vp> > + class to_input_view : public view_interface<to_input_view<_Vp>> > + { > + _Vp _M_base = _Vp(); > + > + template<bool _Const> > + class _Iterator; > + > + public: > + to_input_view() requires default_initializable<_Vp> = default; > + > + constexpr explicit > + to_input_view(_Vp __base) > + : _M_base(std::move(__base)) > + { } > + > + constexpr _Vp > + base() const & requires copy_constructible<_Vp> > + { return _M_base; } > + > + constexpr _Vp > + base() && > + { return std::move(_M_base); } > + > + constexpr auto > + begin() requires (!__detail::__simple_view<_Vp>) > + { return _Iterator<false>(ranges::begin(_M_base)); } > + > + constexpr auto > + begin() const requires range<const _Vp> > + { return _Iterator<true>(ranges::begin(_M_base)); } > + > + constexpr auto > + end() requires (!__detail::__simple_view<_Vp>) > + { return ranges::end(_M_base); } > + > + constexpr auto > + end() const requires range<const _Vp> > + { return ranges::end(_M_base); } > + > + constexpr auto > + size() requires sized_range<_Vp> > + { return ranges::size(_M_base); } > + > + constexpr auto > + size() const requires sized_range<const _Vp> > + { return ranges::size(_M_base); } > + }; > + > + template<typename _Range> > + to_input_view(_Range&&) -> to_input_view<views::all_t<_Range>>; > + > + template<input_range _Vp> > + requires view<_Vp> > + template<bool _Const> > + class to_input_view<_Vp>::_Iterator > + { > + using _Base = __maybe_const_t<_Const, _Vp>; > + > + iterator_t<_Base> _M_current = iterator_t<_Base>(); > + > + constexpr explicit > + _Iterator(iterator_t<_Base> __current) > + : _M_current(std::move(__current)) > + { } > + > + friend to_input_view; > + friend _Iterator<!_Const>; > + > + public: > + using difference_type = range_difference_t<_Base>; > + using value_type = range_value_t<_Base>; > + using iterator_concept = input_iterator_tag; > + > + _Iterator() requires default_initializable<iterator_t<_Base>> = > default; > + > + _Iterator(_Iterator&&) = default; > + _Iterator& operator=(_Iterator&&) = default; > + > + constexpr > + _Iterator(_Iterator<!_Const> __i) > + requires _Const && convertible_to<iterator_t<_Vp>, > iterator_t<_Base>> > + : _M_current(std::move(__i._M_current)) > + { } > + > + constexpr iterator_t<_Base> > + base() && > + { return std::move(_M_current); } > + > + constexpr const iterator_t<_Base>& > + base() const & noexcept > + { return _M_current; } > + > + constexpr decltype(auto) > + operator*() const > + { return *_M_current; } > + > + constexpr _Iterator& > + operator++() > + { > + ++_M_current; > + return *this; > + } > + > + constexpr void > + operator++(int) > + { ++*this; } > + > + friend constexpr bool > + operator==(const _Iterator& __x, const sentinel_t<_Base>& __y) > + { return __x._M_current == __y; } > + > + friend constexpr difference_type > + operator-(const sentinel_t<_Base>& __y, const _Iterator& __x) > + requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>> > + { return __y - __x._M_current; } > + > + friend constexpr difference_type > + operator-(const _Iterator& __x, const sentinel_t<_Base>& __y) > + requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>> > + { return __x._M_current - __y; } > + > + friend constexpr range_rvalue_reference_t<_Base> > + iter_move(const _Iterator& __i) > + noexcept(noexcept(ranges::iter_move(__i._M_current))) > + { return ranges::iter_move(__i._M_current); } > + > + friend constexpr void > + iter_swap(const _Iterator& __x, const _Iterator& __y) > + noexcept(noexcept(ranges::iter_swap(__x._M_current, > __y._M_current))) > + requires indirectly_swappable<iterator_t<_Base>> > + { ranges::iter_swap(__x._M_current, __y._M_current); } > + }; > + > + namespace views > + { > + namespace __detail > + { > + template<typename _Tp> > + concept __can_to_input = requires { > to_input_view(std::declval<_Tp>()); }; > + } > + > + struct _ToInput : __adaptor::_RangeAdaptorClosure<_ToInput> > + { > + template<viewable_range _Range> > + requires __detail::__can_to_input<_Range> > + constexpr auto > + operator() [[nodiscard]] (_Range&& __r) const > + { > + if constexpr (input_range<_Range> This branch does not create an `to_input_view`, so we do not require it to be well formed (__can_to_input). The only requirement imposed by constraints on that view type is `input_range`, so this is not observable. Maybe it would be better to remove this repeated check? > + && !common_range<_Range> > + && !forward_range<_Range>) > + return views::all(__r); > + else > + return to_input_view(std::forward<_Range>(__r)); > + } > + > + static constexpr bool _S_has_simple_call_op = true; > + }; > + > + inline constexpr _ToInput to_input; > + } > +} // namespace ranges > +#endif // __cpp_lib_ranges_to_input > + > _GLIBCXX_END_NAMESPACE_VERSION > } // namespace std > #endif // library concepts > diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > b/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > new file mode 100644 > index 00000000000..1e43281adb4 > --- /dev/null > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > @@ -0,0 +1,58 @@ > +// { dg-do run { target c++26 } } > + > +#include <ranges> > + > +#if __cpp_lib_ranges_to_input != 202502L > +# error "Feature-test macro __cpp_lib_ranges_to_input has wrong value in > <ranges>" > +#endif > + > +#include <algorithm> > +#include <vector> > +#include <testsuite_hooks.h> > +#include <testsuite_iterators.h> > + > +namespace ranges = std::ranges; > +namespace views = std::views; > + > +void > +test01() > +{ > + std::vector<int> r{1,2,3}; > + auto v = r | views::to_input; > + using type = decltype(v); > + static_assert( ranges::input_range<type> && > !ranges::forward_range<type> ); > + > + VERIFY( ranges::equal(v.base(), r) ); > + VERIFY( v.size() == r.size() ); > + VERIFY( v.end() == r.end() ); > + auto it = v.begin(); > + VERIFY( it != r.end() ); > + *it = 42; > + ++it; > + *it = 43; > + it++; > + ranges::iter_swap(v.begin(), it); > + VERIFY( ranges::equal(r, (int[]){3,43,42}) ); > + *it = ranges::iter_move(it); > + VERIFY( it == r.begin() + 2 ); > + VERIFY( r.end() - it == 1 ); > + VERIFY( it - r.end() == -1 ); > +} > + > +void > +test02() > +{ > + int x[] = {1,2,3}; > + __gnu_test::test_input_range<int> rx(x); > + static_assert( !ranges::common_range<decltype(rx)> ); > + auto v = rx | views::to_input; > + using type = decltype(v); > + using type = ranges::ref_view<decltype(rx)>; > +} > + > +int > +main() > +{ > + test01(); > + test02(); > +} > -- > 2.49.0.rc1.37.ge969bc8759 > >
On Tue, 11 Mar 2025, Tomasz Kaminski wrote: > > > On Mon, Mar 10, 2025 at 11:28 PM Patrick Palka <ppalka@redhat.com> wrote: > Tested on x86_64-pc-linux-gnu, does this look OK for trunk? > > -- >8 -- > > libstdc++-v3/ChangeLog: > > * include/bits/version.def (ranges_to_input): Define. > * include/bits/version.h: Regenerate. > * include/std/ranges (ranges::to_input_view): Define for C++26. > (views::__detail::__can_to_input): Likewise. > (views::_ToInput, views::to_input): Likewise. > * testsuite/std/ranges/adaptors/to_input/1.cc: New test. > --- > libstdc++-v3/include/bits/version.def | 8 + > libstdc++-v3/include/bits/version.h | 10 ++ > libstdc++-v3/include/std/ranges | 170 ++++++++++++++++++ > .../std/ranges/adaptors/to_input/1.cc | 58 ++++++ > 4 files changed, 246 insertions(+) > create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > > diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def > index 2af5a54bff2..c2b5283df89 100644 > --- a/libstdc++-v3/include/bits/version.def > +++ b/libstdc++-v3/include/bits/version.def > @@ -1910,6 +1910,14 @@ ftms = { > }; > }; > > +ftms = { > + name = ranges_to_input; > + values = { > + v = 202502; > + cxxmin = 26; > + }; > +}; > + > ftms = { > name = to_string; > values = { > diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h > index 9833023cfdc..775c8642139 100644 > --- a/libstdc++-v3/include/bits/version.h > +++ b/libstdc++-v3/include/bits/version.h > @@ -2120,6 +2120,16 @@ > #endif /* !defined(__cpp_lib_text_encoding) && defined(__glibcxx_want_text_encoding) */ > #undef __glibcxx_want_text_encoding > > +#if !defined(__cpp_lib_ranges_to_input) > +# if (__cplusplus > 202302L) > +# define __glibcxx_ranges_to_input 202502L > +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges_to_input) > +# define __cpp_lib_ranges_to_input 202502L > +# endif > +# endif > +#endif /* !defined(__cpp_lib_ranges_to_input) && defined(__glibcxx_want_ranges_to_input) */ > +#undef __glibcxx_want_ranges_to_input > + > #if !defined(__cpp_lib_to_string) > # if (__cplusplus > 202302L) && _GLIBCXX_HOSTED && (__glibcxx_to_chars) > # define __glibcxx_to_string 202306L > diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges > index e21f5284b46..dd97d276ef0 100644 > --- a/libstdc++-v3/include/std/ranges > +++ b/libstdc++-v3/include/std/ranges > @@ -69,6 +69,7 @@ > #define __glibcxx_want_ranges_slide > #define __glibcxx_want_ranges_stride > #define __glibcxx_want_ranges_to_container > +#define __glibcxx_want_ranges_to_input > #define __glibcxx_want_ranges_zip > #include <bits/version.h> > > @@ -10390,6 +10391,175 @@ namespace ranges > } // namespace ranges > #endif // __cpp_lib_ranges_cache_latest > > +#if __cpp_lib_ranges_to_input // C++ >= 26 > +namespace ranges > +{ > + template<input_range _Vp> > + requires view<_Vp> > + class to_input_view : public view_interface<to_input_view<_Vp>> > + { > + _Vp _M_base = _Vp(); > + > + template<bool _Const> > + class _Iterator; > + > + public: > + to_input_view() requires default_initializable<_Vp> = default; > + > + constexpr explicit > + to_input_view(_Vp __base) > + : _M_base(std::move(__base)) > + { } > + > + constexpr _Vp > + base() const & requires copy_constructible<_Vp> > + { return _M_base; } > + > + constexpr _Vp > + base() && > + { return std::move(_M_base); } > + > + constexpr auto > + begin() requires (!__detail::__simple_view<_Vp>) > + { return _Iterator<false>(ranges::begin(_M_base)); } > + > + constexpr auto > + begin() const requires range<const _Vp> > + { return _Iterator<true>(ranges::begin(_M_base)); } > + > + constexpr auto > + end() requires (!__detail::__simple_view<_Vp>) > + { return ranges::end(_M_base); } > + > + constexpr auto > + end() const requires range<const _Vp> > + { return ranges::end(_M_base); } > + > + constexpr auto > + size() requires sized_range<_Vp> > + { return ranges::size(_M_base); } > + > + constexpr auto > + size() const requires sized_range<const _Vp> > + { return ranges::size(_M_base); } > + }; > + > + template<typename _Range> > + to_input_view(_Range&&) -> to_input_view<views::all_t<_Range>>; > + > + template<input_range _Vp> > + requires view<_Vp> > + template<bool _Const> > + class to_input_view<_Vp>::_Iterator > + { > + using _Base = __maybe_const_t<_Const, _Vp>; > + > + iterator_t<_Base> _M_current = iterator_t<_Base>(); > + > + constexpr explicit > + _Iterator(iterator_t<_Base> __current) > + : _M_current(std::move(__current)) > + { } > + > + friend to_input_view; > + friend _Iterator<!_Const>; > + > + public: > + using difference_type = range_difference_t<_Base>; > + using value_type = range_value_t<_Base>; > + using iterator_concept = input_iterator_tag; > + > + _Iterator() requires default_initializable<iterator_t<_Base>> = default; > + > + _Iterator(_Iterator&&) = default; > + _Iterator& operator=(_Iterator&&) = default; > + > + constexpr > + _Iterator(_Iterator<!_Const> __i) > + requires _Const && convertible_to<iterator_t<_Vp>, iterator_t<_Base>> > + : _M_current(std::move(__i._M_current)) > + { } > + > + constexpr iterator_t<_Base> > + base() && > + { return std::move(_M_current); } > + > + constexpr const iterator_t<_Base>& > + base() const & noexcept > + { return _M_current; } > + > + constexpr decltype(auto) > + operator*() const > + { return *_M_current; } > + > + constexpr _Iterator& > + operator++() > + { > + ++_M_current; > + return *this; > + } > + > + constexpr void > + operator++(int) > + { ++*this; } > + > + friend constexpr bool > + operator==(const _Iterator& __x, const sentinel_t<_Base>& __y) > + { return __x._M_current == __y; } > + > + friend constexpr difference_type > + operator-(const sentinel_t<_Base>& __y, const _Iterator& __x) > + requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>> > + { return __y - __x._M_current; } > + > + friend constexpr difference_type > + operator-(const _Iterator& __x, const sentinel_t<_Base>& __y) > + requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>> > + { return __x._M_current - __y; } > + > + friend constexpr range_rvalue_reference_t<_Base> > + iter_move(const _Iterator& __i) > + noexcept(noexcept(ranges::iter_move(__i._M_current))) > + { return ranges::iter_move(__i._M_current); } > + > + friend constexpr void > + iter_swap(const _Iterator& __x, const _Iterator& __y) > + noexcept(noexcept(ranges::iter_swap(__x._M_current, __y._M_current))) > + requires indirectly_swappable<iterator_t<_Base>> > + { ranges::iter_swap(__x._M_current, __y._M_current); } > + }; > + > + namespace views > + { > + namespace __detail > + { > + template<typename _Tp> > + concept __can_to_input = requires { to_input_view(std::declval<_Tp>()); }; > + } > + > + struct _ToInput : __adaptor::_RangeAdaptorClosure<_ToInput> > + { > + template<viewable_range _Range> > + requires __detail::__can_to_input<_Range> > + constexpr auto > + operator() [[nodiscard]] (_Range&& __r) const > + { > + if constexpr (input_range<_Range> > + && !common_range<_Range> > + && !forward_range<_Range>) > + return views::all(__r); > + else > + return to_input_view(std::forward<_Range>(__r)); > + } > + > + static constexpr bool _S_has_simple_call_op = true; > + }; > + > + inline constexpr _ToInput to_input; > + } > +} // namespace ranges > +#endif // __cpp_lib_ranges_to_input > + > _GLIBCXX_END_NAMESPACE_VERSION > } // namespace std > #endif // library concepts > diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > new file mode 100644 > index 00000000000..1e43281adb4 > --- /dev/null > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > @@ -0,0 +1,58 @@ > +// { dg-do run { target c++26 } } > + > +#include <ranges> > + > +#if __cpp_lib_ranges_to_input != 202502L > +# error "Feature-test macro __cpp_lib_ranges_to_input has wrong value in <ranges>" > +#endif > + > +#include <algorithm> > +#include <vector> > +#include <testsuite_hooks.h> > +#include <testsuite_iterators.h> > + > +namespace ranges = std::ranges; > +namespace views = std::views; > + > +void > +test01() > +{ > + std::vector<int> r{1,2,3}; > + auto v = r | views::to_input; > + using type = decltype(v); > + static_assert( ranges::input_range<type> && !ranges::forward_range<type> ); > + > + VERIFY( ranges::equal(v.base(), r) ); > + VERIFY( v.size() == r.size() ); > + VERIFY( v.end() == r.end() ); > + auto it = v.begin(); > + VERIFY( it != r.end() ); > + *it = 42; > + ++it; > + *it = 43; > + it++; > + ranges::iter_swap(v.begin(), it); > > As far as I can tell this is not invoking the iter_swap friend function, and instead defaulting to swap by 3 iter_moves. > We cannot copy the iterator, but you could replace v.begin(), with views::to_input(v).begin() to call the overload, and > have the same effect. v is the to_input_view, not the vector, so both arguments are to_input_view iterators and I'd expect the friend function gets used here? Looks like it is, demonstrated by a static_assert(false) added to its body: In file included from src/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc:3: /scratchpad/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/ranges: In instantiation of ‘constexpr void std::ranges::iter_swap(const to_input_view<ref_view<std::__debug::vector<int> > >::_Iterator<true>&, const to_input_view<ref_view<std::__debug::vector<int> > >::_Iterator<true>&) requires indirectly_swappable<decltype(std::ranges::__access::__begin((declval<typename std::__conditional<_Const>::type<const _Vp, _Vp>&>)())), decltype(std::ranges::__access::__begin((declval<typename std::__conditional<_Const>::type<const _Vp, _Vp>&>)()))>’: /scratchpad/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/iterator_concepts.h:949:15: required from ‘constexpr void std::ranges::__iswap::_IterSwap::operator()(_Tp&&, _Up&&) const [with _Tp = std::ranges::to_input_view<std::ranges::ref_view<std::__debug::vector<int> > >::_Iterator<true>; _Up = std::ranges::to_input_view<std::ranges::ref_view<std::__debug::vector<int> > >::_Iterator<true>&]’ 949 | iter_swap(static_cast<_Tp&&>(__e1), static_cast<_Up&&>(__e2)); | ~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ src/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc:34:20: required from here 34 | ranges::iter_swap(v.begin(), it); | ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~ /scratchpad/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/ranges:10529:21: error: static assertion failed 10529 | { static_assert(false); ranges::iter_swap(__x._M_current, __y._M_current); } | ^~~~~ /scratchpad/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/ranges:10529:21: note: ‘false’ evaluates to false > + VERIFY( ranges::equal(r, (int[]){3,43,42}) ); > + *it = ranges::iter_move(it); > + VERIFY( it == r.begin() + 2 ); > + VERIFY( r.end() - it == 1 ); > + VERIFY( it - r.end() == -1 ); > +} > + > +void > +test02() > +{ > + int x[] = {1,2,3}; > + __gnu_test::test_input_range<int> rx(x); > + static_assert( !ranges::common_range<decltype(rx)> ); > + auto v = rx | views::to_input; > + using type = decltype(v); > + using type = ranges::ref_view<decltype(rx)>; > > I would prefer a more direct representation of this check, as static_assert(std::is_same_v<decltype(views::all(rx)), decltype())>); Sounds good. The 'using' technique doesn't add much here since the type names are short and we don't subsequently use the alias anywhere. > +} > + > +int > +main() > +{ > + test01(); > + test02(); > +} > -- > 2.49.0.rc1.37.ge969bc8759 > > >
On Tue, Mar 11, 2025 at 5:04 PM Patrick Palka <ppalka@redhat.com> wrote: > On Tue, 11 Mar 2025, Tomasz Kaminski wrote: > > > > > > > On Mon, Mar 10, 2025 at 11:28 PM Patrick Palka <ppalka@redhat.com> > wrote: > > Tested on x86_64-pc-linux-gnu, does this look OK for trunk? > > > > -- >8 -- > > > > libstdc++-v3/ChangeLog: > > > > * include/bits/version.def (ranges_to_input): Define. > > * include/bits/version.h: Regenerate. > > * include/std/ranges (ranges::to_input_view): Define for > C++26. > > (views::__detail::__can_to_input): Likewise. > > (views::_ToInput, views::to_input): Likewise. > > * testsuite/std/ranges/adaptors/to_input/1.cc: New test. > > --- > > libstdc++-v3/include/bits/version.def | 8 + > > libstdc++-v3/include/bits/version.h | 10 ++ > > libstdc++-v3/include/std/ranges | 170 > ++++++++++++++++++ > > .../std/ranges/adaptors/to_input/1.cc | 58 ++++++ > > 4 files changed, 246 insertions(+) > > create mode 100644 > libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > > > > diff --git a/libstdc++-v3/include/bits/version.def > b/libstdc++-v3/include/bits/version.def > > index 2af5a54bff2..c2b5283df89 100644 > > --- a/libstdc++-v3/include/bits/version.def > > +++ b/libstdc++-v3/include/bits/version.def > > @@ -1910,6 +1910,14 @@ ftms = { > > }; > > }; > > > > +ftms = { > > + name = ranges_to_input; > > + values = { > > + v = 202502; > > + cxxmin = 26; > > + }; > > +}; > > + > > ftms = { > > name = to_string; > > values = { > > diff --git a/libstdc++-v3/include/bits/version.h > b/libstdc++-v3/include/bits/version.h > > index 9833023cfdc..775c8642139 100644 > > --- a/libstdc++-v3/include/bits/version.h > > +++ b/libstdc++-v3/include/bits/version.h > > @@ -2120,6 +2120,16 @@ > > #endif /* !defined(__cpp_lib_text_encoding) && > defined(__glibcxx_want_text_encoding) */ > > #undef __glibcxx_want_text_encoding > > > > +#if !defined(__cpp_lib_ranges_to_input) > > +# if (__cplusplus > 202302L) > > +# define __glibcxx_ranges_to_input 202502L > > +# if defined(__glibcxx_want_all) || > defined(__glibcxx_want_ranges_to_input) > > +# define __cpp_lib_ranges_to_input 202502L > > +# endif > > +# endif > > +#endif /* !defined(__cpp_lib_ranges_to_input) && > defined(__glibcxx_want_ranges_to_input) */ > > +#undef __glibcxx_want_ranges_to_input > > + > > #if !defined(__cpp_lib_to_string) > > # if (__cplusplus > 202302L) && _GLIBCXX_HOSTED && > (__glibcxx_to_chars) > > # define __glibcxx_to_string 202306L > > diff --git a/libstdc++-v3/include/std/ranges > b/libstdc++-v3/include/std/ranges > > index e21f5284b46..dd97d276ef0 100644 > > --- a/libstdc++-v3/include/std/ranges > > +++ b/libstdc++-v3/include/std/ranges > > @@ -69,6 +69,7 @@ > > #define __glibcxx_want_ranges_slide > > #define __glibcxx_want_ranges_stride > > #define __glibcxx_want_ranges_to_container > > +#define __glibcxx_want_ranges_to_input > > #define __glibcxx_want_ranges_zip > > #include <bits/version.h> > > > > @@ -10390,6 +10391,175 @@ namespace ranges > > } // namespace ranges > > #endif // __cpp_lib_ranges_cache_latest > > > > +#if __cpp_lib_ranges_to_input // C++ >= 26 > > +namespace ranges > > +{ > > + template<input_range _Vp> > > + requires view<_Vp> > > + class to_input_view : public view_interface<to_input_view<_Vp>> > > + { > > + _Vp _M_base = _Vp(); > > + > > + template<bool _Const> > > + class _Iterator; > > + > > + public: > > + to_input_view() requires default_initializable<_Vp> = default; > > + > > + constexpr explicit > > + to_input_view(_Vp __base) > > + : _M_base(std::move(__base)) > > + { } > > + > > + constexpr _Vp > > + base() const & requires copy_constructible<_Vp> > > + { return _M_base; } > > + > > + constexpr _Vp > > + base() && > > + { return std::move(_M_base); } > > + > > + constexpr auto > > + begin() requires (!__detail::__simple_view<_Vp>) > > + { return _Iterator<false>(ranges::begin(_M_base)); } > > + > > + constexpr auto > > + begin() const requires range<const _Vp> > > + { return _Iterator<true>(ranges::begin(_M_base)); } > > + > > + constexpr auto > > + end() requires (!__detail::__simple_view<_Vp>) > > + { return ranges::end(_M_base); } > > + > > + constexpr auto > > + end() const requires range<const _Vp> > > + { return ranges::end(_M_base); } > > + > > + constexpr auto > > + size() requires sized_range<_Vp> > > + { return ranges::size(_M_base); } > > + > > + constexpr auto > > + size() const requires sized_range<const _Vp> > > + { return ranges::size(_M_base); } > > + }; > > + > > + template<typename _Range> > > + to_input_view(_Range&&) -> > to_input_view<views::all_t<_Range>>; > > + > > + template<input_range _Vp> > > + requires view<_Vp> > > + template<bool _Const> > > + class to_input_view<_Vp>::_Iterator > > + { > > + using _Base = __maybe_const_t<_Const, _Vp>; > > + > > + iterator_t<_Base> _M_current = iterator_t<_Base>(); > > + > > + constexpr explicit > > + _Iterator(iterator_t<_Base> __current) > > + : _M_current(std::move(__current)) > > + { } > > + > > + friend to_input_view; > > + friend _Iterator<!_Const>; > > + > > + public: > > + using difference_type = range_difference_t<_Base>; > > + using value_type = range_value_t<_Base>; > > + using iterator_concept = input_iterator_tag; > > + > > + _Iterator() requires default_initializable<iterator_t<_Base>> > = default; > > + > > + _Iterator(_Iterator&&) = default; > > + _Iterator& operator=(_Iterator&&) = default; > > + > > + constexpr > > + _Iterator(_Iterator<!_Const> __i) > > + requires _Const && convertible_to<iterator_t<_Vp>, > iterator_t<_Base>> > > + : _M_current(std::move(__i._M_current)) > > + { } > > + > > + constexpr iterator_t<_Base> > > + base() && > > + { return std::move(_M_current); } > > + > > + constexpr const iterator_t<_Base>& > > + base() const & noexcept > > + { return _M_current; } > > + > > + constexpr decltype(auto) > > + operator*() const > > + { return *_M_current; } > > + > > + constexpr _Iterator& > > + operator++() > > + { > > + ++_M_current; > > + return *this; > > + } > > + > > + constexpr void > > + operator++(int) > > + { ++*this; } > > + > > + friend constexpr bool > > + operator==(const _Iterator& __x, const sentinel_t<_Base>& __y) > > + { return __x._M_current == __y; } > > + > > + friend constexpr difference_type > > + operator-(const sentinel_t<_Base>& __y, const _Iterator& __x) > > + requires sized_sentinel_for<sentinel_t<_Base>, > iterator_t<_Base>> > > + { return __y - __x._M_current; } > > + > > + friend constexpr difference_type > > + operator-(const _Iterator& __x, const sentinel_t<_Base>& __y) > > + requires sized_sentinel_for<sentinel_t<_Base>, > iterator_t<_Base>> > > + { return __x._M_current - __y; } > > + > > + friend constexpr range_rvalue_reference_t<_Base> > > + iter_move(const _Iterator& __i) > > + noexcept(noexcept(ranges::iter_move(__i._M_current))) > > + { return ranges::iter_move(__i._M_current); } > > + > > + friend constexpr void > > + iter_swap(const _Iterator& __x, const _Iterator& __y) > > + noexcept(noexcept(ranges::iter_swap(__x._M_current, > __y._M_current))) > > + requires indirectly_swappable<iterator_t<_Base>> > > + { ranges::iter_swap(__x._M_current, __y._M_current); } > > + }; > > + > > + namespace views > > + { > > + namespace __detail > > + { > > + template<typename _Tp> > > + concept __can_to_input = requires { > to_input_view(std::declval<_Tp>()); }; > > + } > > + > > + struct _ToInput : __adaptor::_RangeAdaptorClosure<_ToInput> > > + { > > + template<viewable_range _Range> > > + requires __detail::__can_to_input<_Range> > > + constexpr auto > > + operator() [[nodiscard]] (_Range&& __r) const > > + { > > + if constexpr (input_range<_Range> > > + && !common_range<_Range> > > + && !forward_range<_Range>) > > + return views::all(__r); > > + else > > + return to_input_view(std::forward<_Range>(__r)); > > + } > > + > > + static constexpr bool _S_has_simple_call_op = true; > > + }; > > + > > + inline constexpr _ToInput to_input; > > + } > > +} // namespace ranges > > +#endif // __cpp_lib_ranges_to_input > > + > > _GLIBCXX_END_NAMESPACE_VERSION > > } // namespace std > > #endif // library concepts > > diff --git > a/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > b/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > > new file mode 100644 > > index 00000000000..1e43281adb4 > > --- /dev/null > > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > > @@ -0,0 +1,58 @@ > > +// { dg-do run { target c++26 } } > > + > > +#include <ranges> > > + > > +#if __cpp_lib_ranges_to_input != 202502L > > +# error "Feature-test macro __cpp_lib_ranges_to_input has wrong > value in <ranges>" > > +#endif > > + > > +#include <algorithm> > > +#include <vector> > > +#include <testsuite_hooks.h> > > +#include <testsuite_iterators.h> > > + > > +namespace ranges = std::ranges; > > +namespace views = std::views; > > + > > +void > > +test01() > > +{ > > + std::vector<int> r{1,2,3}; > > + auto v = r | views::to_input; > > + using type = decltype(v); > > + static_assert( ranges::input_range<type> && > !ranges::forward_range<type> ); > > + > > + VERIFY( ranges::equal(v.base(), r) ); > > + VERIFY( v.size() == r.size() ); > > + VERIFY( v.end() == r.end() ); > > + auto it = v.begin(); > > + VERIFY( it != r.end() ); > > + *it = 42; > > + ++it; > > + *it = 43; > > + it++; > > + ranges::iter_swap(v.begin(), it); > > > > As far as I can tell this is not invoking the iter_swap friend function, > and instead defaulting to swap by 3 iter_moves. > > We cannot copy the iterator, but you could replace v.begin(), with > views::to_input(v).begin() to call the overload, and > > have the same effect. > > v is the to_input_view, not the vector, so both arguments are > to_input_view iterators and I'd expect the friend function gets > used here? Looks like it is, demonstrated by a static_assert(false) > added to its body: > Indeed, I got confused by the name (v being for vector), and the friend overload is called. We are also calling begin on input_range twice, which is not guaranteed to work in general, but seem to be well-defined in this case. > > In file included from > src/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc:3: > /scratchpad/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/ranges: In > instantiation of ‘constexpr void std::ranges::iter_swap(const > to_input_view<ref_view<std::__debug::vector<int> > >::_Iterator<true>&, > const to_input_view<ref_view<std::__debug::vector<int> > > >::_Iterator<true>&) requires > indirectly_swappable<decltype(std::ranges::__access::__begin((declval<typename > std::__conditional<_Const>::type<const _Vp, _Vp>&>)())), > decltype(std::ranges::__access::__begin((declval<typename > std::__conditional<_Const>::type<const _Vp, _Vp>&>)()))>’: > /scratchpad/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/iterator_concepts.h:949:15: > required from ‘constexpr void > std::ranges::__iswap::_IterSwap::operator()(_Tp&&, _Up&&) const [with _Tp = > std::ranges::to_input_view<std::ranges::ref_view<std::__debug::vector<int> > > >::_Iterator<true>; _Up = > std::ranges::to_input_view<std::ranges::ref_view<std::__debug::vector<int> > > >::_Iterator<true>&]’ > 949 | iter_swap(static_cast<_Tp&&>(__e1), > static_cast<_Up&&>(__e2)); > | > ~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > src/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc:34:20: > required from here > 34 | ranges::iter_swap(v.begin(), it); > | ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~ > /scratchpad/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/ranges:10529:21: > error: static assertion failed > 10529 | { static_assert(false); ranges::iter_swap(__x._M_current, > __y._M_current); } > | ^~~~~ > /scratchpad/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/ranges:10529:21: > note: ‘false’ evaluates to false > > > + VERIFY( ranges::equal(r, (int[]){3,43,42}) ); > > + *it = ranges::iter_move(it); > > + VERIFY( it == r.begin() + 2 ); > > + VERIFY( r.end() - it == 1 ); > > + VERIFY( it - r.end() == -1 ); > > +} > > + > > +void > > +test02() > > +{ > > + int x[] = {1,2,3}; > > + __gnu_test::test_input_range<int> rx(x); > > + static_assert( !ranges::common_range<decltype(rx)> ); > > + auto v = rx | views::to_input; > > + using type = decltype(v); > > + using type = ranges::ref_view<decltype(rx)>; > > > > I would prefer a more direct representation of this check, as > static_assert(std::is_same_v<decltype(views::all(rx)), decltype())>); > > Sounds good. The 'using' technique doesn't add much here since the type > names are short and we don't subsequently use the alias anywhere. > > > +} > > + > > +int > > +main() > > +{ > > + test01(); > > + test02(); > > +} > > -- > > 2.49.0.rc1.37.ge969bc8759 > > > > > >
On Tue, 11 Mar 2025, Tomasz Kaminski wrote: > > > On Mon, Mar 10, 2025 at 11:28 PM Patrick Palka <ppalka@redhat.com> wrote: > Tested on x86_64-pc-linux-gnu, does this look OK for trunk? > > -- >8 -- > > libstdc++-v3/ChangeLog: > > * include/bits/version.def (ranges_to_input): Define. > * include/bits/version.h: Regenerate. > * include/std/ranges (ranges::to_input_view): Define for C++26. > (views::__detail::__can_to_input): Likewise. > (views::_ToInput, views::to_input): Likewise. > * testsuite/std/ranges/adaptors/to_input/1.cc: New test. > --- > libstdc++-v3/include/bits/version.def | 8 + > libstdc++-v3/include/bits/version.h | 10 ++ > libstdc++-v3/include/std/ranges | 170 ++++++++++++++++++ > .../std/ranges/adaptors/to_input/1.cc | 58 ++++++ > 4 files changed, 246 insertions(+) > create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > > diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def > index 2af5a54bff2..c2b5283df89 100644 > --- a/libstdc++-v3/include/bits/version.def > +++ b/libstdc++-v3/include/bits/version.def > @@ -1910,6 +1910,14 @@ ftms = { > }; > }; > > +ftms = { > + name = ranges_to_input; > + values = { > + v = 202502; > + cxxmin = 26; > + }; > +}; > + > ftms = { > name = to_string; > values = { > diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h > index 9833023cfdc..775c8642139 100644 > --- a/libstdc++-v3/include/bits/version.h > +++ b/libstdc++-v3/include/bits/version.h > @@ -2120,6 +2120,16 @@ > #endif /* !defined(__cpp_lib_text_encoding) && defined(__glibcxx_want_text_encoding) */ > #undef __glibcxx_want_text_encoding > > +#if !defined(__cpp_lib_ranges_to_input) > +# if (__cplusplus > 202302L) > +# define __glibcxx_ranges_to_input 202502L > +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges_to_input) > +# define __cpp_lib_ranges_to_input 202502L > +# endif > +# endif > +#endif /* !defined(__cpp_lib_ranges_to_input) && defined(__glibcxx_want_ranges_to_input) */ > +#undef __glibcxx_want_ranges_to_input > + > #if !defined(__cpp_lib_to_string) > # if (__cplusplus > 202302L) && _GLIBCXX_HOSTED && (__glibcxx_to_chars) > # define __glibcxx_to_string 202306L > diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges > index e21f5284b46..dd97d276ef0 100644 > --- a/libstdc++-v3/include/std/ranges > +++ b/libstdc++-v3/include/std/ranges > @@ -69,6 +69,7 @@ > #define __glibcxx_want_ranges_slide > #define __glibcxx_want_ranges_stride > #define __glibcxx_want_ranges_to_container > +#define __glibcxx_want_ranges_to_input > #define __glibcxx_want_ranges_zip > #include <bits/version.h> > > @@ -10390,6 +10391,175 @@ namespace ranges > } // namespace ranges > #endif // __cpp_lib_ranges_cache_latest > > +#if __cpp_lib_ranges_to_input // C++ >= 26 > +namespace ranges > +{ > + template<input_range _Vp> > + requires view<_Vp> > + class to_input_view : public view_interface<to_input_view<_Vp>> > + { > + _Vp _M_base = _Vp(); > + > + template<bool _Const> > + class _Iterator; > + > + public: > + to_input_view() requires default_initializable<_Vp> = default; > + > + constexpr explicit > + to_input_view(_Vp __base) > + : _M_base(std::move(__base)) > + { } > + > + constexpr _Vp > + base() const & requires copy_constructible<_Vp> > + { return _M_base; } > + > + constexpr _Vp > + base() && > + { return std::move(_M_base); } > + > + constexpr auto > + begin() requires (!__detail::__simple_view<_Vp>) > + { return _Iterator<false>(ranges::begin(_M_base)); } > + > + constexpr auto > + begin() const requires range<const _Vp> > + { return _Iterator<true>(ranges::begin(_M_base)); } > + > + constexpr auto > + end() requires (!__detail::__simple_view<_Vp>) > + { return ranges::end(_M_base); } > + > + constexpr auto > + end() const requires range<const _Vp> > + { return ranges::end(_M_base); } > + > + constexpr auto > + size() requires sized_range<_Vp> > + { return ranges::size(_M_base); } > + > + constexpr auto > + size() const requires sized_range<const _Vp> > + { return ranges::size(_M_base); } > + }; > + > + template<typename _Range> > + to_input_view(_Range&&) -> to_input_view<views::all_t<_Range>>; > + > + template<input_range _Vp> > + requires view<_Vp> > + template<bool _Const> > + class to_input_view<_Vp>::_Iterator > + { > + using _Base = __maybe_const_t<_Const, _Vp>; > + > + iterator_t<_Base> _M_current = iterator_t<_Base>(); > + > + constexpr explicit > + _Iterator(iterator_t<_Base> __current) > + : _M_current(std::move(__current)) > + { } > + > + friend to_input_view; > + friend _Iterator<!_Const>; > + > + public: > + using difference_type = range_difference_t<_Base>; > + using value_type = range_value_t<_Base>; > + using iterator_concept = input_iterator_tag; > + > + _Iterator() requires default_initializable<iterator_t<_Base>> = default; > + > + _Iterator(_Iterator&&) = default; > + _Iterator& operator=(_Iterator&&) = default; > + > + constexpr > + _Iterator(_Iterator<!_Const> __i) > + requires _Const && convertible_to<iterator_t<_Vp>, iterator_t<_Base>> > + : _M_current(std::move(__i._M_current)) > + { } > + > + constexpr iterator_t<_Base> > + base() && > + { return std::move(_M_current); } > + > + constexpr const iterator_t<_Base>& > + base() const & noexcept > + { return _M_current; } > + > + constexpr decltype(auto) > + operator*() const > + { return *_M_current; } > + > + constexpr _Iterator& > + operator++() > + { > + ++_M_current; > + return *this; > + } > + > + constexpr void > + operator++(int) > + { ++*this; } > + > + friend constexpr bool > + operator==(const _Iterator& __x, const sentinel_t<_Base>& __y) > + { return __x._M_current == __y; } > + > + friend constexpr difference_type > + operator-(const sentinel_t<_Base>& __y, const _Iterator& __x) > + requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>> > + { return __y - __x._M_current; } > + > + friend constexpr difference_type > + operator-(const _Iterator& __x, const sentinel_t<_Base>& __y) > + requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>> > + { return __x._M_current - __y; } > + > + friend constexpr range_rvalue_reference_t<_Base> > + iter_move(const _Iterator& __i) > + noexcept(noexcept(ranges::iter_move(__i._M_current))) > + { return ranges::iter_move(__i._M_current); } > + > + friend constexpr void > + iter_swap(const _Iterator& __x, const _Iterator& __y) > + noexcept(noexcept(ranges::iter_swap(__x._M_current, __y._M_current))) > + requires indirectly_swappable<iterator_t<_Base>> > + { ranges::iter_swap(__x._M_current, __y._M_current); } > + }; > + > + namespace views > + { > + namespace __detail > + { > + template<typename _Tp> > + concept __can_to_input = requires { to_input_view(std::declval<_Tp>()); }; > + } > + > + struct _ToInput : __adaptor::_RangeAdaptorClosure<_ToInput> > + { > + template<viewable_range _Range> > + requires __detail::__can_to_input<_Range> > + constexpr auto > + operator() [[nodiscard]] (_Range&& __r) const > + { > + if constexpr (input_range<_Range> > > This branch does not create an `to_input_view`, so we do not require it to be well formed (__can_to_input). FWIW I see this branch as a compile time shortcut for reducing template instantiation rather than logic that extends the set of types accepted by this adaptor, which is why it's not reflected in the constraints. Another example of this is in views::take/drop, which contain a lot of shortcuts to avoid returning (and instantiating) a take/drop_view and instead reuse the underlying range type, but it's ultimately constrained by the corresponding take/drop_view expression being well-formed. > The only requirement imposed by constraints on that view type is `input_range`, so this is not observable. > Maybe it would be better to remove this repeated check? I think I prefer mirroring the wording here for sake of maintainability, so that (in general) if the wording gets changed it's easier to see what needs changed in the implementation. Note the repeated check should be very cheap compile-time wise since the front end caches constraints aggressively. > + && !common_range<_Range> > + && !forward_range<_Range>) > + return views::all(__r); > + else > + return to_input_view(std::forward<_Range>(__r)); > + } > + > + static constexpr bool _S_has_simple_call_op = true; > + }; > + > + inline constexpr _ToInput to_input; > + } > +} // namespace ranges > +#endif // __cpp_lib_ranges_to_input > + > _GLIBCXX_END_NAMESPACE_VERSION > } // namespace std > #endif // library concepts > diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > new file mode 100644 > index 00000000000..1e43281adb4 > --- /dev/null > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > @@ -0,0 +1,58 @@ > +// { dg-do run { target c++26 } } > + > +#include <ranges> > + > +#if __cpp_lib_ranges_to_input != 202502L > +# error "Feature-test macro __cpp_lib_ranges_to_input has wrong value in <ranges>" > +#endif > + > +#include <algorithm> > +#include <vector> > +#include <testsuite_hooks.h> > +#include <testsuite_iterators.h> > + > +namespace ranges = std::ranges; > +namespace views = std::views; > + > +void > +test01() > +{ > + std::vector<int> r{1,2,3}; > + auto v = r | views::to_input; > + using type = decltype(v); > + static_assert( ranges::input_range<type> && !ranges::forward_range<type> ); > + > + VERIFY( ranges::equal(v.base(), r) ); > + VERIFY( v.size() == r.size() ); > + VERIFY( v.end() == r.end() ); > + auto it = v.begin(); > + VERIFY( it != r.end() ); > + *it = 42; > + ++it; > + *it = 43; > + it++; > + ranges::iter_swap(v.begin(), it); > + VERIFY( ranges::equal(r, (int[]){3,43,42}) ); > + *it = ranges::iter_move(it); > + VERIFY( it == r.begin() + 2 ); > + VERIFY( r.end() - it == 1 ); > + VERIFY( it - r.end() == -1 ); > +} > + > +void > +test02() > +{ > + int x[] = {1,2,3}; > + __gnu_test::test_input_range<int> rx(x); > + static_assert( !ranges::common_range<decltype(rx)> ); > + auto v = rx | views::to_input; > + using type = decltype(v); > + using type = ranges::ref_view<decltype(rx)>; > +} > + > +int > +main() > +{ > + test01(); > + test02(); > +} > -- > 2.49.0.rc1.37.ge969bc8759 > > >
On Tue, 11 Mar 2025, Tomasz Kaminski wrote: > > > On Tue, Mar 11, 2025 at 5:04 PM Patrick Palka <ppalka@redhat.com> wrote: > On Tue, 11 Mar 2025, Tomasz Kaminski wrote: > > > > > > > On Mon, Mar 10, 2025 at 11:28 PM Patrick Palka <ppalka@redhat.com> wrote: > > Tested on x86_64-pc-linux-gnu, does this look OK for trunk? > > > > -- >8 -- > > > > libstdc++-v3/ChangeLog: > > > > * include/bits/version.def (ranges_to_input): Define. > > * include/bits/version.h: Regenerate. > > * include/std/ranges (ranges::to_input_view): Define for C++26. > > (views::__detail::__can_to_input): Likewise. > > (views::_ToInput, views::to_input): Likewise. > > * testsuite/std/ranges/adaptors/to_input/1.cc: New test. > > --- > > libstdc++-v3/include/bits/version.def | 8 + > > libstdc++-v3/include/bits/version.h | 10 ++ > > libstdc++-v3/include/std/ranges | 170 ++++++++++++++++++ > > .../std/ranges/adaptors/to_input/1.cc | 58 ++++++ > > 4 files changed, 246 insertions(+) > > create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > > > > diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def > > index 2af5a54bff2..c2b5283df89 100644 > > --- a/libstdc++-v3/include/bits/version.def > > +++ b/libstdc++-v3/include/bits/version.def > > @@ -1910,6 +1910,14 @@ ftms = { > > }; > > }; > > > > +ftms = { > > + name = ranges_to_input; > > + values = { > > + v = 202502; > > + cxxmin = 26; > > + }; > > +}; > > + > > ftms = { > > name = to_string; > > values = { > > diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h > > index 9833023cfdc..775c8642139 100644 > > --- a/libstdc++-v3/include/bits/version.h > > +++ b/libstdc++-v3/include/bits/version.h > > @@ -2120,6 +2120,16 @@ > > #endif /* !defined(__cpp_lib_text_encoding) && defined(__glibcxx_want_text_encoding) */ > > #undef __glibcxx_want_text_encoding > > > > +#if !defined(__cpp_lib_ranges_to_input) > > +# if (__cplusplus > 202302L) > > +# define __glibcxx_ranges_to_input 202502L > > +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges_to_input) > > +# define __cpp_lib_ranges_to_input 202502L > > +# endif > > +# endif > > +#endif /* !defined(__cpp_lib_ranges_to_input) && defined(__glibcxx_want_ranges_to_input) */ > > +#undef __glibcxx_want_ranges_to_input > > + > > #if !defined(__cpp_lib_to_string) > > # if (__cplusplus > 202302L) && _GLIBCXX_HOSTED && (__glibcxx_to_chars) > > # define __glibcxx_to_string 202306L > > diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges > > index e21f5284b46..dd97d276ef0 100644 > > --- a/libstdc++-v3/include/std/ranges > > +++ b/libstdc++-v3/include/std/ranges > > @@ -69,6 +69,7 @@ > > #define __glibcxx_want_ranges_slide > > #define __glibcxx_want_ranges_stride > > #define __glibcxx_want_ranges_to_container > > +#define __glibcxx_want_ranges_to_input > > #define __glibcxx_want_ranges_zip > > #include <bits/version.h> > > > > @@ -10390,6 +10391,175 @@ namespace ranges > > } // namespace ranges > > #endif // __cpp_lib_ranges_cache_latest > > > > +#if __cpp_lib_ranges_to_input // C++ >= 26 > > +namespace ranges > > +{ > > + template<input_range _Vp> > > + requires view<_Vp> > > + class to_input_view : public view_interface<to_input_view<_Vp>> > > + { > > + _Vp _M_base = _Vp(); > > + > > + template<bool _Const> > > + class _Iterator; > > + > > + public: > > + to_input_view() requires default_initializable<_Vp> = default; > > + > > + constexpr explicit > > + to_input_view(_Vp __base) > > + : _M_base(std::move(__base)) > > + { } > > + > > + constexpr _Vp > > + base() const & requires copy_constructible<_Vp> > > + { return _M_base; } > > + > > + constexpr _Vp > > + base() && > > + { return std::move(_M_base); } > > + > > + constexpr auto > > + begin() requires (!__detail::__simple_view<_Vp>) > > + { return _Iterator<false>(ranges::begin(_M_base)); } > > + > > + constexpr auto > > + begin() const requires range<const _Vp> > > + { return _Iterator<true>(ranges::begin(_M_base)); } > > + > > + constexpr auto > > + end() requires (!__detail::__simple_view<_Vp>) > > + { return ranges::end(_M_base); } > > + > > + constexpr auto > > + end() const requires range<const _Vp> > > + { return ranges::end(_M_base); } > > + > > + constexpr auto > > + size() requires sized_range<_Vp> > > + { return ranges::size(_M_base); } > > + > > + constexpr auto > > + size() const requires sized_range<const _Vp> > > + { return ranges::size(_M_base); } > > + }; > > + > > + template<typename _Range> > > + to_input_view(_Range&&) -> to_input_view<views::all_t<_Range>>; > > + > > + template<input_range _Vp> > > + requires view<_Vp> > > + template<bool _Const> > > + class to_input_view<_Vp>::_Iterator > > + { > > + using _Base = __maybe_const_t<_Const, _Vp>; > > + > > + iterator_t<_Base> _M_current = iterator_t<_Base>(); > > + > > + constexpr explicit > > + _Iterator(iterator_t<_Base> __current) > > + : _M_current(std::move(__current)) > > + { } > > + > > + friend to_input_view; > > + friend _Iterator<!_Const>; > > + > > + public: > > + using difference_type = range_difference_t<_Base>; > > + using value_type = range_value_t<_Base>; > > + using iterator_concept = input_iterator_tag; > > + > > + _Iterator() requires default_initializable<iterator_t<_Base>> = default; > > + > > + _Iterator(_Iterator&&) = default; > > + _Iterator& operator=(_Iterator&&) = default; > > + > > + constexpr > > + _Iterator(_Iterator<!_Const> __i) > > + requires _Const && convertible_to<iterator_t<_Vp>, iterator_t<_Base>> > > + : _M_current(std::move(__i._M_current)) > > + { } > > + > > + constexpr iterator_t<_Base> > > + base() && > > + { return std::move(_M_current); } > > + > > + constexpr const iterator_t<_Base>& > > + base() const & noexcept > > + { return _M_current; } > > + > > + constexpr decltype(auto) > > + operator*() const > > + { return *_M_current; } > > + > > + constexpr _Iterator& > > + operator++() > > + { > > + ++_M_current; > > + return *this; > > + } > > + > > + constexpr void > > + operator++(int) > > + { ++*this; } > > + > > + friend constexpr bool > > + operator==(const _Iterator& __x, const sentinel_t<_Base>& __y) > > + { return __x._M_current == __y; } > > + > > + friend constexpr difference_type > > + operator-(const sentinel_t<_Base>& __y, const _Iterator& __x) > > + requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>> > > + { return __y - __x._M_current; } > > + > > + friend constexpr difference_type > > + operator-(const _Iterator& __x, const sentinel_t<_Base>& __y) > > + requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>> > > + { return __x._M_current - __y; } > > + > > + friend constexpr range_rvalue_reference_t<_Base> > > + iter_move(const _Iterator& __i) > > + noexcept(noexcept(ranges::iter_move(__i._M_current))) > > + { return ranges::iter_move(__i._M_current); } > > + > > + friend constexpr void > > + iter_swap(const _Iterator& __x, const _Iterator& __y) > > + noexcept(noexcept(ranges::iter_swap(__x._M_current, __y._M_current))) > > + requires indirectly_swappable<iterator_t<_Base>> > > + { ranges::iter_swap(__x._M_current, __y._M_current); } > > + }; > > + > > + namespace views > > + { > > + namespace __detail > > + { > > + template<typename _Tp> > > + concept __can_to_input = requires { to_input_view(std::declval<_Tp>()); }; > > + } > > + > > + struct _ToInput : __adaptor::_RangeAdaptorClosure<_ToInput> > > + { > > + template<viewable_range _Range> > > + requires __detail::__can_to_input<_Range> > > + constexpr auto > > + operator() [[nodiscard]] (_Range&& __r) const > > + { > > + if constexpr (input_range<_Range> > > + && !common_range<_Range> > > + && !forward_range<_Range>) > > + return views::all(__r); > > + else > > + return to_input_view(std::forward<_Range>(__r)); > > + } > > + > > + static constexpr bool _S_has_simple_call_op = true; > > + }; > > + > > + inline constexpr _ToInput to_input; > > + } > > +} // namespace ranges > > +#endif // __cpp_lib_ranges_to_input > > + > > _GLIBCXX_END_NAMESPACE_VERSION > > } // namespace std > > #endif // library concepts > > diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > > new file mode 100644 > > index 00000000000..1e43281adb4 > > --- /dev/null > > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > > @@ -0,0 +1,58 @@ > > +// { dg-do run { target c++26 } } > > + > > +#include <ranges> > > + > > +#if __cpp_lib_ranges_to_input != 202502L > > +# error "Feature-test macro __cpp_lib_ranges_to_input has wrong value in <ranges>" > > +#endif > > + > > +#include <algorithm> > > +#include <vector> > > +#include <testsuite_hooks.h> > > +#include <testsuite_iterators.h> > > + > > +namespace ranges = std::ranges; > > +namespace views = std::views; > > + > > +void > > +test01() > > +{ > > + std::vector<int> r{1,2,3}; > > + auto v = r | views::to_input; > > + using type = decltype(v); > > + static_assert( ranges::input_range<type> && !ranges::forward_range<type> ); > > + > > + VERIFY( ranges::equal(v.base(), r) ); > > + VERIFY( v.size() == r.size() ); > > + VERIFY( v.end() == r.end() ); > > + auto it = v.begin(); > > + VERIFY( it != r.end() ); > > + *it = 42; > > + ++it; > > + *it = 43; > > + it++; > > + ranges::iter_swap(v.begin(), it); > > > > As far as I can tell this is not invoking the iter_swap friend function, and instead defaulting to swap by 3 iter_moves. > > We cannot copy the iterator, but you could replace v.begin(), with views::to_input(v).begin() to call the overload, and > > have the same effect. > > v is the to_input_view, not the vector, so both arguments are > to_input_view iterators and I'd expect the friend function gets > used here? Looks like it is, demonstrated by a static_assert(false) > added to its body: > > Indeed, I got confused by the name (v being for vector), and the friend overload is called. The range adaptor tests tend to use 'v' as the name of the adapted view FWIW. > We are also calling begin on input_range twice, which is not guaranteed to work in general, > but seem to be well-defined in this case. Hmm good point -- while the to_input_view iterator is always move-only, it's not always single pass, the latter property is inherited from the underlying range. Here's an updated patch that uses std::same_as in the testcase, and slightly extends the testcase to verify views::to_input is idempotent. I also added a missing forward in the views::all branch of to_input. -- >8 -- libstdc++-v3/ChangeLog: * include/bits/version.def (ranges_to_input): Define. * include/bits/version.h: Regenerate. * include/std/ranges (ranges::to_input_view): Define for C++26. (views::__detail::__can_to_input): Likewise. (views::_ToInput, views::to_input): Likewise. * testsuite/std/ranges/adaptors/to_input/1.cc: New test. --- libstdc++-v3/include/bits/version.def | 8 + libstdc++-v3/include/bits/version.h | 10 ++ libstdc++-v3/include/std/ranges | 170 ++++++++++++++++++ .../std/ranges/adaptors/to_input/1.cc | 59 ++++++ 4 files changed, 247 insertions(+) create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 2af5a54bff2..c2b5283df89 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1910,6 +1910,14 @@ ftms = { }; }; +ftms = { + name = ranges_to_input; + values = { + v = 202502; + cxxmin = 26; + }; +}; + ftms = { name = to_string; values = { diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 9833023cfdc..775c8642139 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -2120,6 +2120,16 @@ #endif /* !defined(__cpp_lib_text_encoding) && defined(__glibcxx_want_text_encoding) */ #undef __glibcxx_want_text_encoding +#if !defined(__cpp_lib_ranges_to_input) +# if (__cplusplus > 202302L) +# define __glibcxx_ranges_to_input 202502L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges_to_input) +# define __cpp_lib_ranges_to_input 202502L +# endif +# endif +#endif /* !defined(__cpp_lib_ranges_to_input) && defined(__glibcxx_want_ranges_to_input) */ +#undef __glibcxx_want_ranges_to_input + #if !defined(__cpp_lib_to_string) # if (__cplusplus > 202302L) && _GLIBCXX_HOSTED && (__glibcxx_to_chars) # define __glibcxx_to_string 202306L diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index e21f5284b46..c2a2d6f4e05 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -69,6 +69,7 @@ #define __glibcxx_want_ranges_slide #define __glibcxx_want_ranges_stride #define __glibcxx_want_ranges_to_container +#define __glibcxx_want_ranges_to_input #define __glibcxx_want_ranges_zip #include <bits/version.h> @@ -10390,6 +10391,175 @@ namespace ranges } // namespace ranges #endif // __cpp_lib_ranges_cache_latest +#if __cpp_lib_ranges_to_input // C++ >= 26 +namespace ranges +{ + template<input_range _Vp> + requires view<_Vp> + class to_input_view : public view_interface<to_input_view<_Vp>> + { + _Vp _M_base = _Vp(); + + template<bool _Const> + class _Iterator; + + public: + to_input_view() requires default_initializable<_Vp> = default; + + constexpr explicit + to_input_view(_Vp __base) + : _M_base(std::move(__base)) + { } + + constexpr _Vp + base() const & requires copy_constructible<_Vp> + { return _M_base; } + + constexpr _Vp + base() && + { return std::move(_M_base); } + + constexpr auto + begin() requires (!__detail::__simple_view<_Vp>) + { return _Iterator<false>(ranges::begin(_M_base)); } + + constexpr auto + begin() const requires range<const _Vp> + { return _Iterator<true>(ranges::begin(_M_base)); } + + constexpr auto + end() requires (!__detail::__simple_view<_Vp>) + { return ranges::end(_M_base); } + + constexpr auto + end() const requires range<const _Vp> + { return ranges::end(_M_base); } + + constexpr auto + size() requires sized_range<_Vp> + { return ranges::size(_M_base); } + + constexpr auto + size() const requires sized_range<const _Vp> + { return ranges::size(_M_base); } + }; + + template<typename _Range> + to_input_view(_Range&&) -> to_input_view<views::all_t<_Range>>; + + template<input_range _Vp> + requires view<_Vp> + template<bool _Const> + class to_input_view<_Vp>::_Iterator + { + using _Base = __maybe_const_t<_Const, _Vp>; + + iterator_t<_Base> _M_current = iterator_t<_Base>(); + + constexpr explicit + _Iterator(iterator_t<_Base> __current) + : _M_current(std::move(__current)) + { } + + friend to_input_view; + friend _Iterator<!_Const>; + + public: + using difference_type = range_difference_t<_Base>; + using value_type = range_value_t<_Base>; + using iterator_concept = input_iterator_tag; + + _Iterator() requires default_initializable<iterator_t<_Base>> = default; + + _Iterator(_Iterator&&) = default; + _Iterator& operator=(_Iterator&&) = default; + + constexpr + _Iterator(_Iterator<!_Const> __i) + requires _Const && convertible_to<iterator_t<_Vp>, iterator_t<_Base>> + : _M_current(std::move(__i._M_current)) + { } + + constexpr iterator_t<_Base> + base() && + { return std::move(_M_current); } + + constexpr const iterator_t<_Base>& + base() const & noexcept + { return _M_current; } + + constexpr decltype(auto) + operator*() const + { return *_M_current; } + + constexpr _Iterator& + operator++() + { + ++_M_current; + return *this; + } + + constexpr void + operator++(int) + { ++*this; } + + friend constexpr bool + operator==(const _Iterator& __x, const sentinel_t<_Base>& __y) + { return __x._M_current == __y; } + + friend constexpr difference_type + operator-(const sentinel_t<_Base>& __y, const _Iterator& __x) + requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>> + { return __y - __x._M_current; } + + friend constexpr difference_type + operator-(const _Iterator& __x, const sentinel_t<_Base>& __y) + requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>> + { return __x._M_current - __y; } + + friend constexpr range_rvalue_reference_t<_Base> + iter_move(const _Iterator& __i) + noexcept(noexcept(ranges::iter_move(__i._M_current))) + { return ranges::iter_move(__i._M_current); } + + friend constexpr void + iter_swap(const _Iterator& __x, const _Iterator& __y) + noexcept(noexcept(ranges::iter_swap(__x._M_current, __y._M_current))) + requires indirectly_swappable<iterator_t<_Base>> + { ranges::iter_swap(__x._M_current, __y._M_current); } + }; + + namespace views + { + namespace __detail + { + template<typename _Tp> + concept __can_to_input = requires { to_input_view(std::declval<_Tp>()); }; + } + + struct _ToInput : __adaptor::_RangeAdaptorClosure<_ToInput> + { + template<viewable_range _Range> + requires __detail::__can_to_input<_Range> + constexpr auto + operator() [[nodiscard]] (_Range&& __r) const + { + if constexpr (input_range<_Range> + && !common_range<_Range> + && !forward_range<_Range>) + return views::all(std::forward<_Range>(__r)); + else + return to_input_view(std::forward<_Range>(__r)); + } + + static constexpr bool _S_has_simple_call_op = true; + }; + + inline constexpr _ToInput to_input; + } +} // namespace ranges +#endif // __cpp_lib_ranges_to_input + _GLIBCXX_END_NAMESPACE_VERSION } // namespace std #endif // library concepts diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc new file mode 100644 index 00000000000..cde368a2f64 --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc @@ -0,0 +1,59 @@ +// { dg-do run { target c++26 } } + +#include <ranges> + +#if __cpp_lib_ranges_to_input != 202502L +# error "Feature-test macro __cpp_lib_ranges_to_input has wrong value in <ranges>" +#endif + +#include <algorithm> +#include <vector> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> + +namespace ranges = std::ranges; +namespace views = std::views; + +void +test01() +{ + std::vector<int> r{1,2,3}; + auto v = r | views::to_input; + using type = decltype(v); + static_assert( ranges::input_range<type> && !ranges::forward_range<type> ); + + VERIFY( ranges::equal(v.base(), r) ); + VERIFY( v.size() == r.size() ); + VERIFY( v.end() == r.end() ); + auto it = v.begin(); + VERIFY( it != r.end() ); + *it = 42; + ++it; + *it = 43; + it++; + ranges::iter_swap(v.begin(), it); + VERIFY( ranges::equal(r, (int[]){3,43,42}) ); + *it = ranges::iter_move(it); + VERIFY( it == r.begin() + 2 ); + VERIFY( r.end() - it == 1 ); + VERIFY( it - r.end() == -1 ); +} + +void +test02() +{ + int x[] = {1,2,3}; + __gnu_test::test_input_range<int> rx(x); + static_assert( !ranges::common_range<decltype(rx)> ); + auto v = rx | views::to_input; + static_assert( std::same_as<decltype(v), decltype(views::all(rx))> ); + static_assert( std::same_as<decltype(x | views::to_input), + decltype(x | views::to_input | views::to_input)> ); +} + +int +main() +{ + test01(); + test02(); +}
On Tue, Mar 11, 2025 at 5:54 PM Patrick Palka <ppalka@redhat.com> wrote: > On Tue, 11 Mar 2025, Tomasz Kaminski wrote: > > > > > > > On Tue, Mar 11, 2025 at 5:04 PM Patrick Palka <ppalka@redhat.com> wrote: > > On Tue, 11 Mar 2025, Tomasz Kaminski wrote: > > > > > > > > > > > On Mon, Mar 10, 2025 at 11:28 PM Patrick Palka < > ppalka@redhat.com> wrote: > > > Tested on x86_64-pc-linux-gnu, does this look OK for trunk? > > > > > > -- >8 -- > > > > > > libstdc++-v3/ChangeLog: > > > > > > * include/bits/version.def (ranges_to_input): > Define. > > > * include/bits/version.h: Regenerate. > > > * include/std/ranges (ranges::to_input_view): > Define for C++26. > > > (views::__detail::__can_to_input): Likewise. > > > (views::_ToInput, views::to_input): Likewise. > > > * testsuite/std/ranges/adaptors/to_input/1.cc: New > test. > > > --- > > > libstdc++-v3/include/bits/version.def | 8 + > > > libstdc++-v3/include/bits/version.h | 10 ++ > > > libstdc++-v3/include/std/ranges | 170 > ++++++++++++++++++ > > > .../std/ranges/adaptors/to_input/1.cc | 58 ++++++ > > > 4 files changed, 246 insertions(+) > > > create mode 100644 > libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > > > > > > diff --git a/libstdc++-v3/include/bits/version.def > b/libstdc++-v3/include/bits/version.def > > > index 2af5a54bff2..c2b5283df89 100644 > > > --- a/libstdc++-v3/include/bits/version.def > > > +++ b/libstdc++-v3/include/bits/version.def > > > @@ -1910,6 +1910,14 @@ ftms = { > > > }; > > > }; > > > > > > +ftms = { > > > + name = ranges_to_input; > > > + values = { > > > + v = 202502; > > > + cxxmin = 26; > > > + }; > > > +}; > > > + > > > ftms = { > > > name = to_string; > > > values = { > > > diff --git a/libstdc++-v3/include/bits/version.h > b/libstdc++-v3/include/bits/version.h > > > index 9833023cfdc..775c8642139 100644 > > > --- a/libstdc++-v3/include/bits/version.h > > > +++ b/libstdc++-v3/include/bits/version.h > > > @@ -2120,6 +2120,16 @@ > > > #endif /* !defined(__cpp_lib_text_encoding) && > defined(__glibcxx_want_text_encoding) */ > > > #undef __glibcxx_want_text_encoding > > > > > > +#if !defined(__cpp_lib_ranges_to_input) > > > +# if (__cplusplus > 202302L) > > > +# define __glibcxx_ranges_to_input 202502L > > > +# if defined(__glibcxx_want_all) || > defined(__glibcxx_want_ranges_to_input) > > > +# define __cpp_lib_ranges_to_input 202502L > > > +# endif > > > +# endif > > > +#endif /* !defined(__cpp_lib_ranges_to_input) && > defined(__glibcxx_want_ranges_to_input) */ > > > +#undef __glibcxx_want_ranges_to_input > > > + > > > #if !defined(__cpp_lib_to_string) > > > # if (__cplusplus > 202302L) && _GLIBCXX_HOSTED && > (__glibcxx_to_chars) > > > # define __glibcxx_to_string 202306L > > > diff --git a/libstdc++-v3/include/std/ranges > b/libstdc++-v3/include/std/ranges > > > index e21f5284b46..dd97d276ef0 100644 > > > --- a/libstdc++-v3/include/std/ranges > > > +++ b/libstdc++-v3/include/std/ranges > > > @@ -69,6 +69,7 @@ > > > #define __glibcxx_want_ranges_slide > > > #define __glibcxx_want_ranges_stride > > > #define __glibcxx_want_ranges_to_container > > > +#define __glibcxx_want_ranges_to_input > > > #define __glibcxx_want_ranges_zip > > > #include <bits/version.h> > > > > > > @@ -10390,6 +10391,175 @@ namespace ranges > > > } // namespace ranges > > > #endif // __cpp_lib_ranges_cache_latest > > > > > > +#if __cpp_lib_ranges_to_input // C++ >= 26 > > > +namespace ranges > > > +{ > > > + template<input_range _Vp> > > > + requires view<_Vp> > > > + class to_input_view : public > view_interface<to_input_view<_Vp>> > > > + { > > > + _Vp _M_base = _Vp(); > > > + > > > + template<bool _Const> > > > + class _Iterator; > > > + > > > + public: > > > + to_input_view() requires default_initializable<_Vp> = > default; > > > + > > > + constexpr explicit > > > + to_input_view(_Vp __base) > > > + : _M_base(std::move(__base)) > > > + { } > > > + > > > + constexpr _Vp > > > + base() const & requires copy_constructible<_Vp> > > > + { return _M_base; } > > > + > > > + constexpr _Vp > > > + base() && > > > + { return std::move(_M_base); } > > > + > > > + constexpr auto > > > + begin() requires (!__detail::__simple_view<_Vp>) > > > + { return _Iterator<false>(ranges::begin(_M_base)); } > > > + > > > + constexpr auto > > > + begin() const requires range<const _Vp> > > > + { return _Iterator<true>(ranges::begin(_M_base)); } > > > + > > > + constexpr auto > > > + end() requires (!__detail::__simple_view<_Vp>) > > > + { return ranges::end(_M_base); } > > > + > > > + constexpr auto > > > + end() const requires range<const _Vp> > > > + { return ranges::end(_M_base); } > > > + > > > + constexpr auto > > > + size() requires sized_range<_Vp> > > > + { return ranges::size(_M_base); } > > > + > > > + constexpr auto > > > + size() const requires sized_range<const _Vp> > > > + { return ranges::size(_M_base); } > > > + }; > > > + > > > + template<typename _Range> > > > + to_input_view(_Range&&) -> > to_input_view<views::all_t<_Range>>; > > > + > > > + template<input_range _Vp> > > > + requires view<_Vp> > > > + template<bool _Const> > > > + class to_input_view<_Vp>::_Iterator > > > + { > > > + using _Base = __maybe_const_t<_Const, _Vp>; > > > + > > > + iterator_t<_Base> _M_current = iterator_t<_Base>(); > > > + > > > + constexpr explicit > > > + _Iterator(iterator_t<_Base> __current) > > > + : _M_current(std::move(__current)) > > > + { } > > > + > > > + friend to_input_view; > > > + friend _Iterator<!_Const>; > > > + > > > + public: > > > + using difference_type = range_difference_t<_Base>; > > > + using value_type = range_value_t<_Base>; > > > + using iterator_concept = input_iterator_tag; > > > + > > > + _Iterator() requires > default_initializable<iterator_t<_Base>> = default; > > > + > > > + _Iterator(_Iterator&&) = default; > > > + _Iterator& operator=(_Iterator&&) = default; > > > + > > > + constexpr > > > + _Iterator(_Iterator<!_Const> __i) > > > + requires _Const && convertible_to<iterator_t<_Vp>, > iterator_t<_Base>> > > > + : _M_current(std::move(__i._M_current)) > > > + { } > > > + > > > + constexpr iterator_t<_Base> > > > + base() && > > > + { return std::move(_M_current); } > > > + > > > + constexpr const iterator_t<_Base>& > > > + base() const & noexcept > > > + { return _M_current; } > > > + > > > + constexpr decltype(auto) > > > + operator*() const > > > + { return *_M_current; } > > > + > > > + constexpr _Iterator& > > > + operator++() > > > + { > > > + ++_M_current; > > > + return *this; > > > + } > > > + > > > + constexpr void > > > + operator++(int) > > > + { ++*this; } > > > + > > > + friend constexpr bool > > > + operator==(const _Iterator& __x, const > sentinel_t<_Base>& __y) > > > + { return __x._M_current == __y; } > > > + > > > + friend constexpr difference_type > > > + operator-(const sentinel_t<_Base>& __y, const > _Iterator& __x) > > > + requires sized_sentinel_for<sentinel_t<_Base>, > iterator_t<_Base>> > > > + { return __y - __x._M_current; } > > > + > > > + friend constexpr difference_type > > > + operator-(const _Iterator& __x, const > sentinel_t<_Base>& __y) > > > + requires sized_sentinel_for<sentinel_t<_Base>, > iterator_t<_Base>> > > > + { return __x._M_current - __y; } > > > + > > > + friend constexpr range_rvalue_reference_t<_Base> > > > + iter_move(const _Iterator& __i) > > > + > noexcept(noexcept(ranges::iter_move(__i._M_current))) > > > + { return ranges::iter_move(__i._M_current); } > > > + > > > + friend constexpr void > > > + iter_swap(const _Iterator& __x, const _Iterator& __y) > > > + noexcept(noexcept(ranges::iter_swap(__x._M_current, > __y._M_current))) > > > + requires indirectly_swappable<iterator_t<_Base>> > > > + { ranges::iter_swap(__x._M_current, __y._M_current); } > > > + }; > > > + > > > + namespace views > > > + { > > > + namespace __detail > > > + { > > > + template<typename _Tp> > > > + concept __can_to_input = requires { > to_input_view(std::declval<_Tp>()); }; > > > + } > > > + > > > + struct _ToInput : > __adaptor::_RangeAdaptorClosure<_ToInput> > > > + { > > > + template<viewable_range _Range> > > > + requires __detail::__can_to_input<_Range> > > > + constexpr auto > > > + operator() [[nodiscard]] (_Range&& __r) const > > > + { > > > + if constexpr (input_range<_Range> > > > + && !common_range<_Range> > > > + && !forward_range<_Range>) > > > + return views::all(__r); > > > + else > > > + return > to_input_view(std::forward<_Range>(__r)); > > > + } > > > + > > > + static constexpr bool _S_has_simple_call_op = true; > > > + }; > > > + > > > + inline constexpr _ToInput to_input; > > > + } > > > +} // namespace ranges > > > +#endif // __cpp_lib_ranges_to_input > > > + > > > _GLIBCXX_END_NAMESPACE_VERSION > > > } // namespace std > > > #endif // library concepts > > > diff --git > a/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > b/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > > > new file mode 100644 > > > index 00000000000..1e43281adb4 > > > --- /dev/null > > > +++ > b/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > > > @@ -0,0 +1,58 @@ > > > +// { dg-do run { target c++26 } } > > > + > > > +#include <ranges> > > > + > > > +#if __cpp_lib_ranges_to_input != 202502L > > > +# error "Feature-test macro __cpp_lib_ranges_to_input has > wrong value in <ranges>" > > > +#endif > > > + > > > +#include <algorithm> > > > +#include <vector> > > > +#include <testsuite_hooks.h> > > > +#include <testsuite_iterators.h> > > > + > > > +namespace ranges = std::ranges; > > > +namespace views = std::views; > > > + > > > +void > > > +test01() > > > +{ > > > + std::vector<int> r{1,2,3}; > > > + auto v = r | views::to_input; > > > + using type = decltype(v); > > > + static_assert( ranges::input_range<type> && > !ranges::forward_range<type> ); > > > + > > > + VERIFY( ranges::equal(v.base(), r) ); > > > + VERIFY( v.size() == r.size() ); > > > + VERIFY( v.end() == r.end() ); > > > + auto it = v.begin(); > > > + VERIFY( it != r.end() ); > > > + *it = 42; > > > + ++it; > > > + *it = 43; > > > + it++; > > > + ranges::iter_swap(v.begin(), it); > > > > > > As far as I can tell this is not invoking the iter_swap friend > function, and instead defaulting to swap by 3 iter_moves. > > > We cannot copy the iterator, but you could replace v.begin(), > with views::to_input(v).begin() to call the overload, and > > > have the same effect. > > > > v is the to_input_view, not the vector, so both arguments are > > to_input_view iterators and I'd expect the friend function gets > > used here? Looks like it is, demonstrated by a > static_assert(false) > > added to its body: > > > > Indeed, I got confused by the name (v being for vector), and the friend > overload is called. > > The range adaptor tests tend to use 'v' as the name of the adapted view > FWIW. > > > We are also calling begin on input_range twice, which is not guaranteed > to work in general, > > but seem to be well-defined in this case. > > Hmm good point -- while the to_input_view iterator is always move-only, > it's not always single pass, the latter property is inherited from the > underlying range. > > Here's an updated patch that uses std::same_as in the testcase, and > slightly extends the testcase to verify views::to_input is idempotent. > I also added a missing forward in the views::all branch of to_input. > LGTM. > > -- >8 -- > > libstdc++-v3/ChangeLog: > > * include/bits/version.def (ranges_to_input): Define. > * include/bits/version.h: Regenerate. > * include/std/ranges (ranges::to_input_view): Define for C++26. > (views::__detail::__can_to_input): Likewise. > (views::_ToInput, views::to_input): Likewise. > * testsuite/std/ranges/adaptors/to_input/1.cc: New test. > --- > libstdc++-v3/include/bits/version.def | 8 + > libstdc++-v3/include/bits/version.h | 10 ++ > libstdc++-v3/include/std/ranges | 170 ++++++++++++++++++ > .../std/ranges/adaptors/to_input/1.cc | 59 ++++++ > 4 files changed, 247 insertions(+) > create mode 100644 > libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > > diff --git a/libstdc++-v3/include/bits/version.def > b/libstdc++-v3/include/bits/version.def > index 2af5a54bff2..c2b5283df89 100644 > --- a/libstdc++-v3/include/bits/version.def > +++ b/libstdc++-v3/include/bits/version.def > @@ -1910,6 +1910,14 @@ ftms = { > }; > }; > > +ftms = { > + name = ranges_to_input; > + values = { > + v = 202502; > + cxxmin = 26; > + }; > +}; > + > ftms = { > name = to_string; > values = { > diff --git a/libstdc++-v3/include/bits/version.h > b/libstdc++-v3/include/bits/version.h > index 9833023cfdc..775c8642139 100644 > --- a/libstdc++-v3/include/bits/version.h > +++ b/libstdc++-v3/include/bits/version.h > @@ -2120,6 +2120,16 @@ > #endif /* !defined(__cpp_lib_text_encoding) && > defined(__glibcxx_want_text_encoding) */ > #undef __glibcxx_want_text_encoding > > +#if !defined(__cpp_lib_ranges_to_input) > +# if (__cplusplus > 202302L) > +# define __glibcxx_ranges_to_input 202502L > +# if defined(__glibcxx_want_all) || > defined(__glibcxx_want_ranges_to_input) > +# define __cpp_lib_ranges_to_input 202502L > +# endif > +# endif > +#endif /* !defined(__cpp_lib_ranges_to_input) && > defined(__glibcxx_want_ranges_to_input) */ > +#undef __glibcxx_want_ranges_to_input > + > #if !defined(__cpp_lib_to_string) > # if (__cplusplus > 202302L) && _GLIBCXX_HOSTED && (__glibcxx_to_chars) > # define __glibcxx_to_string 202306L > diff --git a/libstdc++-v3/include/std/ranges > b/libstdc++-v3/include/std/ranges > index e21f5284b46..c2a2d6f4e05 100644 > --- a/libstdc++-v3/include/std/ranges > +++ b/libstdc++-v3/include/std/ranges > @@ -69,6 +69,7 @@ > #define __glibcxx_want_ranges_slide > #define __glibcxx_want_ranges_stride > #define __glibcxx_want_ranges_to_container > +#define __glibcxx_want_ranges_to_input > #define __glibcxx_want_ranges_zip > #include <bits/version.h> > > @@ -10390,6 +10391,175 @@ namespace ranges > } // namespace ranges > #endif // __cpp_lib_ranges_cache_latest > > +#if __cpp_lib_ranges_to_input // C++ >= 26 > +namespace ranges > +{ > + template<input_range _Vp> > + requires view<_Vp> > + class to_input_view : public view_interface<to_input_view<_Vp>> > + { > + _Vp _M_base = _Vp(); > + > + template<bool _Const> > + class _Iterator; > + > + public: > + to_input_view() requires default_initializable<_Vp> = default; > + > + constexpr explicit > + to_input_view(_Vp __base) > + : _M_base(std::move(__base)) > + { } > + > + constexpr _Vp > + base() const & requires copy_constructible<_Vp> > + { return _M_base; } > + > + constexpr _Vp > + base() && > + { return std::move(_M_base); } > + > + constexpr auto > + begin() requires (!__detail::__simple_view<_Vp>) > + { return _Iterator<false>(ranges::begin(_M_base)); } > + > + constexpr auto > + begin() const requires range<const _Vp> > + { return _Iterator<true>(ranges::begin(_M_base)); } > + > + constexpr auto > + end() requires (!__detail::__simple_view<_Vp>) > + { return ranges::end(_M_base); } > + > + constexpr auto > + end() const requires range<const _Vp> > + { return ranges::end(_M_base); } > + > + constexpr auto > + size() requires sized_range<_Vp> > + { return ranges::size(_M_base); } > + > + constexpr auto > + size() const requires sized_range<const _Vp> > + { return ranges::size(_M_base); } > + }; > + > + template<typename _Range> > + to_input_view(_Range&&) -> to_input_view<views::all_t<_Range>>; > + > + template<input_range _Vp> > + requires view<_Vp> > + template<bool _Const> > + class to_input_view<_Vp>::_Iterator > + { > + using _Base = __maybe_const_t<_Const, _Vp>; > + > + iterator_t<_Base> _M_current = iterator_t<_Base>(); > + > + constexpr explicit > + _Iterator(iterator_t<_Base> __current) > + : _M_current(std::move(__current)) > + { } > + > + friend to_input_view; > + friend _Iterator<!_Const>; > + > + public: > + using difference_type = range_difference_t<_Base>; > + using value_type = range_value_t<_Base>; > + using iterator_concept = input_iterator_tag; > + > + _Iterator() requires default_initializable<iterator_t<_Base>> = > default; > + > + _Iterator(_Iterator&&) = default; > + _Iterator& operator=(_Iterator&&) = default; > + > + constexpr > + _Iterator(_Iterator<!_Const> __i) > + requires _Const && convertible_to<iterator_t<_Vp>, > iterator_t<_Base>> > + : _M_current(std::move(__i._M_current)) > + { } > + > + constexpr iterator_t<_Base> > + base() && > + { return std::move(_M_current); } > + > + constexpr const iterator_t<_Base>& > + base() const & noexcept > + { return _M_current; } > + > + constexpr decltype(auto) > + operator*() const > + { return *_M_current; } > + > + constexpr _Iterator& > + operator++() > + { > + ++_M_current; > + return *this; > + } > + > + constexpr void > + operator++(int) > + { ++*this; } > + > + friend constexpr bool > + operator==(const _Iterator& __x, const sentinel_t<_Base>& __y) > + { return __x._M_current == __y; } > + > + friend constexpr difference_type > + operator-(const sentinel_t<_Base>& __y, const _Iterator& __x) > + requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>> > + { return __y - __x._M_current; } > + > + friend constexpr difference_type > + operator-(const _Iterator& __x, const sentinel_t<_Base>& __y) > + requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>> > + { return __x._M_current - __y; } > + > + friend constexpr range_rvalue_reference_t<_Base> > + iter_move(const _Iterator& __i) > + noexcept(noexcept(ranges::iter_move(__i._M_current))) > + { return ranges::iter_move(__i._M_current); } > + > + friend constexpr void > + iter_swap(const _Iterator& __x, const _Iterator& __y) > + noexcept(noexcept(ranges::iter_swap(__x._M_current, > __y._M_current))) > + requires indirectly_swappable<iterator_t<_Base>> > + { ranges::iter_swap(__x._M_current, __y._M_current); } > + }; > + > + namespace views > + { > + namespace __detail > + { > + template<typename _Tp> > + concept __can_to_input = requires { > to_input_view(std::declval<_Tp>()); }; > + } > + > + struct _ToInput : __adaptor::_RangeAdaptorClosure<_ToInput> > + { > + template<viewable_range _Range> > + requires __detail::__can_to_input<_Range> > + constexpr auto > + operator() [[nodiscard]] (_Range&& __r) const > + { > + if constexpr (input_range<_Range> > + && !common_range<_Range> > + && !forward_range<_Range>) > + return views::all(std::forward<_Range>(__r)); > + else > + return to_input_view(std::forward<_Range>(__r)); > + } > + > + static constexpr bool _S_has_simple_call_op = true; > + }; > + > + inline constexpr _ToInput to_input; > + } > +} // namespace ranges > +#endif // __cpp_lib_ranges_to_input > + > _GLIBCXX_END_NAMESPACE_VERSION > } // namespace std > #endif // library concepts > diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > b/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > new file mode 100644 > index 00000000000..cde368a2f64 > --- /dev/null > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc > @@ -0,0 +1,59 @@ > +// { dg-do run { target c++26 } } > + > +#include <ranges> > + > +#if __cpp_lib_ranges_to_input != 202502L > +# error "Feature-test macro __cpp_lib_ranges_to_input has wrong value in > <ranges>" > +#endif > + > +#include <algorithm> > +#include <vector> > +#include <testsuite_hooks.h> > +#include <testsuite_iterators.h> > + > +namespace ranges = std::ranges; > +namespace views = std::views; > + > +void > +test01() > +{ > + std::vector<int> r{1,2,3}; > + auto v = r | views::to_input; > + using type = decltype(v); > + static_assert( ranges::input_range<type> && > !ranges::forward_range<type> ); > + > + VERIFY( ranges::equal(v.base(), r) ); > + VERIFY( v.size() == r.size() ); > + VERIFY( v.end() == r.end() ); > + auto it = v.begin(); > + VERIFY( it != r.end() ); > + *it = 42; > + ++it; > + *it = 43; > + it++; > + ranges::iter_swap(v.begin(), it); > + VERIFY( ranges::equal(r, (int[]){3,43,42}) ); > + *it = ranges::iter_move(it); > + VERIFY( it == r.begin() + 2 ); > + VERIFY( r.end() - it == 1 ); > + VERIFY( it - r.end() == -1 ); > +} > + > +void > +test02() > +{ > + int x[] = {1,2,3}; > + __gnu_test::test_input_range<int> rx(x); > + static_assert( !ranges::common_range<decltype(rx)> ); > + auto v = rx | views::to_input; > + static_assert( std::same_as<decltype(v), decltype(views::all(rx))> ); > + static_assert( std::same_as<decltype(x | views::to_input), > + decltype(x | views::to_input | > views::to_input)> ); > +} > + > +int > +main() > +{ > + test01(); > + test02(); > +} > -- > 2.49.0.rc1.37.ge969bc8759 > > > > > In file included from > src/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc:3: > > > /scratchpad/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/ranges: In > instantiation of ‘constexpr void std::ranges::iter_swap(const > to_input_view<ref_view<std::__debug::vector<int> > > > >::_Iterator<true>&, const > to_input_view<ref_view<std::__debug::vector<int> > >::_Iterator<true>&) > requires > indirectly_swappable<decltype(std::ranges::__access::__begin((declval<typename > > std::__conditional<_Const>::type<const _Vp, _Vp>&>)())), > decltype(std::ranges::__access::__begin((declval<typename > std::__conditional<_Const>::type<const _Vp, _Vp>&>)()))>’: > > > /scratchpad/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/iterator_concepts.h:949:15: > required from ‘constexpr void > std::ranges::__iswap::_IterSwap::operator()(_Tp&&, _Up&&) const > > [with _Tp = > std::ranges::to_input_view<std::ranges::ref_view<std::__debug::vector<int> > > >::_Iterator<true>; _Up = > std::ranges::to_input_view<std::ranges::ref_view<std::__debug::vector<int> > > > >::_Iterator<true>&]’ > > 949 | iter_swap(static_cast<_Tp&&>(__e1), > static_cast<_Up&&>(__e2)); > > | > ~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > > > src/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc:34:20: > required from here > > 34 | ranges::iter_swap(v.begin(), it); > > | ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~ > > > /scratchpad/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/ranges:10529:21: > error: static assertion failed > > 10529 | { static_assert(false); > ranges::iter_swap(__x._M_current, __y._M_current); } > > | ^~~~~ > > > /scratchpad/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/ranges:10529:21: > note: ‘false’ evaluates to false > > > > > + VERIFY( ranges::equal(r, (int[]){3,43,42}) ); > > > + *it = ranges::iter_move(it); > > > + VERIFY( it == r.begin() + 2 ); > > > + VERIFY( r.end() - it == 1 ); > > > + VERIFY( it - r.end() == -1 ); > > > +} > > > + > > > +void > > > +test02() > > > +{ > > > + int x[] = {1,2,3}; > > > + __gnu_test::test_input_range<int> rx(x); > > > + static_assert( !ranges::common_range<decltype(rx)> ); > > > + auto v = rx | views::to_input; > > > + using type = decltype(v); > > > + using type = ranges::ref_view<decltype(rx)>; > > > > > > I would prefer a more direct representation of this check, as > static_assert(std::is_same_v<decltype(views::all(rx)), decltype())>); > > > > Sounds good. The 'using' technique doesn't add much here since > the type > > names are short and we don't subsequently use the alias anywhere. > > > > > +} > > > + > > > +int > > > +main() > > > +{ > > > + test01(); > > > + test02(); > > > +} > > > -- > > > 2.49.0.rc1.37.ge969bc8759 > > > > > > > > > > > > > > >
On 11/03/25 12:54 -0400, Patrick Palka wrote: >Hmm good point -- while the to_input_view iterator is always move-only, >it's not always single pass, the latter property is inherited from the >underlying range. > >Here's an updated patch that uses std::same_as in the testcase, and >slightly extends the testcase to verify views::to_input is idempotent. >I also added a missing forward in the views::all branch of to_input. I definitely prefer the std::same_as test rather than redefining the 'type' alias. OK for trunk, thanks.
diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 2af5a54bff2..c2b5283df89 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1910,6 +1910,14 @@ ftms = { }; }; +ftms = { + name = ranges_to_input; + values = { + v = 202502; + cxxmin = 26; + }; +}; + ftms = { name = to_string; values = { diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 9833023cfdc..775c8642139 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -2120,6 +2120,16 @@ #endif /* !defined(__cpp_lib_text_encoding) && defined(__glibcxx_want_text_encoding) */ #undef __glibcxx_want_text_encoding +#if !defined(__cpp_lib_ranges_to_input) +# if (__cplusplus > 202302L) +# define __glibcxx_ranges_to_input 202502L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges_to_input) +# define __cpp_lib_ranges_to_input 202502L +# endif +# endif +#endif /* !defined(__cpp_lib_ranges_to_input) && defined(__glibcxx_want_ranges_to_input) */ +#undef __glibcxx_want_ranges_to_input + #if !defined(__cpp_lib_to_string) # if (__cplusplus > 202302L) && _GLIBCXX_HOSTED && (__glibcxx_to_chars) # define __glibcxx_to_string 202306L diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index e21f5284b46..dd97d276ef0 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -69,6 +69,7 @@ #define __glibcxx_want_ranges_slide #define __glibcxx_want_ranges_stride #define __glibcxx_want_ranges_to_container +#define __glibcxx_want_ranges_to_input #define __glibcxx_want_ranges_zip #include <bits/version.h> @@ -10390,6 +10391,175 @@ namespace ranges } // namespace ranges #endif // __cpp_lib_ranges_cache_latest +#if __cpp_lib_ranges_to_input // C++ >= 26 +namespace ranges +{ + template<input_range _Vp> + requires view<_Vp> + class to_input_view : public view_interface<to_input_view<_Vp>> + { + _Vp _M_base = _Vp(); + + template<bool _Const> + class _Iterator; + + public: + to_input_view() requires default_initializable<_Vp> = default; + + constexpr explicit + to_input_view(_Vp __base) + : _M_base(std::move(__base)) + { } + + constexpr _Vp + base() const & requires copy_constructible<_Vp> + { return _M_base; } + + constexpr _Vp + base() && + { return std::move(_M_base); } + + constexpr auto + begin() requires (!__detail::__simple_view<_Vp>) + { return _Iterator<false>(ranges::begin(_M_base)); } + + constexpr auto + begin() const requires range<const _Vp> + { return _Iterator<true>(ranges::begin(_M_base)); } + + constexpr auto + end() requires (!__detail::__simple_view<_Vp>) + { return ranges::end(_M_base); } + + constexpr auto + end() const requires range<const _Vp> + { return ranges::end(_M_base); } + + constexpr auto + size() requires sized_range<_Vp> + { return ranges::size(_M_base); } + + constexpr auto + size() const requires sized_range<const _Vp> + { return ranges::size(_M_base); } + }; + + template<typename _Range> + to_input_view(_Range&&) -> to_input_view<views::all_t<_Range>>; + + template<input_range _Vp> + requires view<_Vp> + template<bool _Const> + class to_input_view<_Vp>::_Iterator + { + using _Base = __maybe_const_t<_Const, _Vp>; + + iterator_t<_Base> _M_current = iterator_t<_Base>(); + + constexpr explicit + _Iterator(iterator_t<_Base> __current) + : _M_current(std::move(__current)) + { } + + friend to_input_view; + friend _Iterator<!_Const>; + + public: + using difference_type = range_difference_t<_Base>; + using value_type = range_value_t<_Base>; + using iterator_concept = input_iterator_tag; + + _Iterator() requires default_initializable<iterator_t<_Base>> = default; + + _Iterator(_Iterator&&) = default; + _Iterator& operator=(_Iterator&&) = default; + + constexpr + _Iterator(_Iterator<!_Const> __i) + requires _Const && convertible_to<iterator_t<_Vp>, iterator_t<_Base>> + : _M_current(std::move(__i._M_current)) + { } + + constexpr iterator_t<_Base> + base() && + { return std::move(_M_current); } + + constexpr const iterator_t<_Base>& + base() const & noexcept + { return _M_current; } + + constexpr decltype(auto) + operator*() const + { return *_M_current; } + + constexpr _Iterator& + operator++() + { + ++_M_current; + return *this; + } + + constexpr void + operator++(int) + { ++*this; } + + friend constexpr bool + operator==(const _Iterator& __x, const sentinel_t<_Base>& __y) + { return __x._M_current == __y; } + + friend constexpr difference_type + operator-(const sentinel_t<_Base>& __y, const _Iterator& __x) + requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>> + { return __y - __x._M_current; } + + friend constexpr difference_type + operator-(const _Iterator& __x, const sentinel_t<_Base>& __y) + requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>> + { return __x._M_current - __y; } + + friend constexpr range_rvalue_reference_t<_Base> + iter_move(const _Iterator& __i) + noexcept(noexcept(ranges::iter_move(__i._M_current))) + { return ranges::iter_move(__i._M_current); } + + friend constexpr void + iter_swap(const _Iterator& __x, const _Iterator& __y) + noexcept(noexcept(ranges::iter_swap(__x._M_current, __y._M_current))) + requires indirectly_swappable<iterator_t<_Base>> + { ranges::iter_swap(__x._M_current, __y._M_current); } + }; + + namespace views + { + namespace __detail + { + template<typename _Tp> + concept __can_to_input = requires { to_input_view(std::declval<_Tp>()); }; + } + + struct _ToInput : __adaptor::_RangeAdaptorClosure<_ToInput> + { + template<viewable_range _Range> + requires __detail::__can_to_input<_Range> + constexpr auto + operator() [[nodiscard]] (_Range&& __r) const + { + if constexpr (input_range<_Range> + && !common_range<_Range> + && !forward_range<_Range>) + return views::all(__r); + else + return to_input_view(std::forward<_Range>(__r)); + } + + static constexpr bool _S_has_simple_call_op = true; + }; + + inline constexpr _ToInput to_input; + } +} // namespace ranges +#endif // __cpp_lib_ranges_to_input + _GLIBCXX_END_NAMESPACE_VERSION } // namespace std #endif // library concepts diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc new file mode 100644 index 00000000000..1e43281adb4 --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/to_input/1.cc @@ -0,0 +1,58 @@ +// { dg-do run { target c++26 } } + +#include <ranges> + +#if __cpp_lib_ranges_to_input != 202502L +# error "Feature-test macro __cpp_lib_ranges_to_input has wrong value in <ranges>" +#endif + +#include <algorithm> +#include <vector> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> + +namespace ranges = std::ranges; +namespace views = std::views; + +void +test01() +{ + std::vector<int> r{1,2,3}; + auto v = r | views::to_input; + using type = decltype(v); + static_assert( ranges::input_range<type> && !ranges::forward_range<type> ); + + VERIFY( ranges::equal(v.base(), r) ); + VERIFY( v.size() == r.size() ); + VERIFY( v.end() == r.end() ); + auto it = v.begin(); + VERIFY( it != r.end() ); + *it = 42; + ++it; + *it = 43; + it++; + ranges::iter_swap(v.begin(), it); + VERIFY( ranges::equal(r, (int[]){3,43,42}) ); + *it = ranges::iter_move(it); + VERIFY( it == r.begin() + 2 ); + VERIFY( r.end() - it == 1 ); + VERIFY( it - r.end() == -1 ); +} + +void +test02() +{ + int x[] = {1,2,3}; + __gnu_test::test_input_range<int> rx(x); + static_assert( !ranges::common_range<decltype(rx)> ); + auto v = rx | views::to_input; + using type = decltype(v); + using type = ranges::ref_view<decltype(rx)>; +} + +int +main() +{ + test01(); + test02(); +}