On Tue, 11 Mar 2025, Tomasz Kaminski wrote: > > > On Mon, Mar 10, 2025 at 11:28 PM Patrick Palka <ppa...@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 > > >