Message ID | 20250211055734.2852490-1-ppalka@redhat.com |
---|---|
State | New |
Headers | show |
Series | libstdc++: Implement P3138R5 views::cache_latest | expand |
On Tue, 11 Feb 2025 at 05:59, 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_cache_latest): Define. > * include/bits/version.h: Regenerate. > * include/std/ranges (cache_latest_view): Define for C++26. > (cache_latest_view::_Iterator): Likewise. > (cache_latest_view::_Sentinel): Likewise. > (views::__detail::__can_cache_latest): Likewise. > (views::_CacheLatest, views::cache_latest): Likewise. > * testsuite/std/ranges/adaptors/cache_latest/1.cc: New test. The test is missing from the patch. > --- > libstdc++-v3/include/bits/version.def | 8 ++ > libstdc++-v3/include/bits/version.h | 10 ++ > libstdc++-v3/include/std/ranges | 189 ++++++++++++++++++++++++++ > 3 files changed, 207 insertions(+) > > diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def > index 002e560dc0d..6fb5db2e1fc 100644 > --- a/libstdc++-v3/include/bits/version.def > +++ b/libstdc++-v3/include/bits/version.def > @@ -1837,6 +1837,14 @@ ftms = { > }; > }; > > +ftms = { > + name = ranges_cache_latest; > + values = { > + v = 202411; > + cxxmin = 26; > + }; > +}; > + > ftms = { > name = ranges_concat; > values = { > diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h > index 70de189b1e0..db61a396c45 100644 > --- a/libstdc++-v3/include/bits/version.h > +++ b/libstdc++-v3/include/bits/version.h > @@ -2035,6 +2035,16 @@ > #endif /* !defined(__cpp_lib_is_virtual_base_of) && defined(__glibcxx_want_is_virtual_base_of) */ > #undef __glibcxx_want_is_virtual_base_of > > +#if !defined(__cpp_lib_ranges_cache_latest) > +# if (__cplusplus > 202302L) > +# define __glibcxx_ranges_cache_latest 202411L > +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges_cache_latest) > +# define __cpp_lib_ranges_cache_latest 202411L > +# endif > +# endif > +#endif /* !defined(__cpp_lib_ranges_cache_latest) && defined(__glibcxx_want_ranges_cache_latest) */ > +#undef __glibcxx_want_ranges_cache_latest > + > #if !defined(__cpp_lib_ranges_concat) > # if (__cplusplus > 202302L) > # define __glibcxx_ranges_concat 202403L > diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges > index 5c795a90fbc..db9a00be264 100644 > --- a/libstdc++-v3/include/std/ranges > +++ b/libstdc++-v3/include/std/ranges > @@ -58,6 +58,7 @@ > #define __glibcxx_want_ranges_as_const > #define __glibcxx_want_ranges_as_rvalue > #define __glibcxx_want_ranges_cartesian_product > +#define __glibcxx_want_ranges_cache_latest > #define __glibcxx_want_ranges_concat > #define __glibcxx_want_ranges_chunk > #define __glibcxx_want_ranges_chunk_by > @@ -1534,6 +1535,8 @@ namespace views::__adaptor > this->_M_payload._M_apply(_Optional_func{__f}, __i); > return this->_M_get(); > } > + > + using _Optional_base<_Tp>::_M_reset; > }; > > template<range _Range> > @@ -10203,6 +10206,192 @@ namespace ranges > } // namespace ranges > #endif // __cpp_lib_ranges_concat > > +#if __cpp_lib_ranges_cache_latest // C++ >= 26 > +namespace ranges > +{ > + template<input_range _Vp> > + requires view<_Vp> > + class cache_latest_view : public view_interface<cache_latest_view<_Vp>> > + { > + _Vp _M_base = _Vp(); > + > + using __cache_t = conditional_t<is_reference_v<range_reference_t<_Vp>>, > + add_pointer_t<range_reference_t<_Vp>>, > + range_reference_t<_Vp>>; __conditional_t is cheaper to instantiate than conditional_t, so when it doesn't affect the mangled name of a public symbol we should prefer __conditional_t. > + __detail::__non_propagating_cache<__cache_t> _M_cache; > + > + class _Iterator; > + class _Sentinel; > + > + public: > + cache_latest_view() requires default_initializable<_Vp> = default; > + > + constexpr explicit > + cache_latest_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() > + { return _Iterator(*this); } > + > + constexpr auto > + end() > + { return _Sentinel(*this); } > + > + 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> > + cache_latest_view(_Range&&) -> cache_latest_view<views::all_t<_Range>>; > + > + template<input_range _Vp> > + requires view<_Vp> > + class cache_latest_view<_Vp>::_Iterator > + { > + cache_latest_view* _M_parent; > + iterator_t<_Vp> _M_current; > + > + constexpr explicit > + _Iterator(cache_latest_view& __parent) > + : _M_parent(std::__addressof(__parent)), > + _M_current(ranges::begin(__parent._M_base)) > + { } > + > + friend class cache_latest_view; > + > + public: > + using difference_type = range_difference_t<_Vp>; > + using value_type = range_value_t<_Vp>; > + using iterator_concept = input_iterator_tag; > + > + _Iterator(_Iterator&&) = default; > + > + _Iterator& > + operator=(_Iterator&&) = default; > + > + constexpr iterator_t<_Vp> > + base() && > + { return std::move(_M_current); } > + > + constexpr const iterator_t<_Vp>& > + base() const & noexcept > + { return _M_current; } > + > + constexpr range_reference_t<_Vp>& > + operator*() const > + { > + if constexpr (is_reference_v<range_reference_t<_Vp>>) > + { > + if (!_M_parent->_M_cache) > + _M_parent->_M_cache = std::__addressof(__detail::__as_lvalue(*_M_current)); > + return **_M_parent->_M_cache; > + } > + else > + { > + if (!_M_parent->_M_cache) > + _M_parent->_M_cache._M_emplace_deref(_M_current); > + return *_M_parent->_M_cache; > + } > + } > + > + constexpr _Iterator& > + operator++() > + { > + _M_parent->_M_cache._M_reset(); > + ++_M_current; > + return *this; > + } > + > + constexpr void > + operator++(int) > + { ++*this; } > + > + friend constexpr range_rvalue_reference_t<_Vp> > + 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<_Vp>> > + { ranges::iter_swap(__x._M_current, __y._M_current); } > + }; > + > + template<input_range _Vp> > + requires view<_Vp> > + class cache_latest_view<_Vp>::_Sentinel > + { > + sentinel_t<_Vp> _M_end = sentinel_t<_Vp>(); > + > + constexpr explicit > + _Sentinel(cache_latest_view& parent) > + : _M_end(ranges::end(parent._M_base)) > + { } > + > + friend class cache_latest_view; > + > + public: > + _Sentinel() = default; > + > + constexpr sentinel_t<_Vp> > + base() const > + { return _M_end; } > + > + friend constexpr bool > + operator==(const _Iterator& __x, const _Sentinel& __y) > + { return __x._M_current == __y._M_end; } > + > + friend constexpr range_difference_t<_Vp> > + operator-(const _Iterator& __x, const _Sentinel& __y) > + requires sized_sentinel_for<sentinel_t<_Vp>, iterator_t<_Vp>> > + { return __x._M_current - __y._M_end; } > + > + friend constexpr range_difference_t<_Vp> > + operator-(const _Sentinel& __x, const _Iterator& __y) > + requires sized_sentinel_for<sentinel_t<_Vp>, iterator_t<_Vp>> > + { return __x._M_end - __y._M_current; } > + }; > + > + namespace views > + { > + namespace __detail > + { > + template<typename _Tp> > + concept __can_cache_latest = requires { cache_latest_view(std::declval<_Tp>()); }; > + } > + > + struct _CacheLatest : __adaptor::_RangeAdaptorClosure<_CacheLatest> > + { > + template<viewable_range _Range> > + requires __detail::__can_cache_latest<_Range> > + constexpr auto > + operator() [[nodiscard]] (_Range&& __r) const > + { return cache_latest_view(std::forward<_Range>(__r)); } > + > + static constexpr bool _S_has_simple_call_op = true; > + }; > + > + inline constexpr _CacheLatest cache_latest; > + } > +} // namespace ranges > +#endif // __cpp_lib_ranges_cache_latest > + > _GLIBCXX_END_NAMESPACE_VERSION > } // namespace std > #endif // library concepts > -- > 2.48.1.291.g388218fac7.dirty >
On Thu, 13 Feb 2025, Jonathan Wakely wrote: > On Tue, 11 Feb 2025 at 05:59, 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_cache_latest): Define. > > * include/bits/version.h: Regenerate. > > * include/std/ranges (cache_latest_view): Define for C++26. > > (cache_latest_view::_Iterator): Likewise. > > (cache_latest_view::_Sentinel): Likewise. > > (views::__detail::__can_cache_latest): Likewise. > > (views::_CacheLatest, views::cache_latest): Likewise. > > * testsuite/std/ranges/adaptors/cache_latest/1.cc: New test. > > The test is missing from the patch. Whoops, below is the complete patch. > > > --- > > libstdc++-v3/include/bits/version.def | 8 ++ > > libstdc++-v3/include/bits/version.h | 10 ++ > > libstdc++-v3/include/std/ranges | 189 ++++++++++++++++++++++++++ > > 3 files changed, 207 insertions(+) > > > > diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def > > index 002e560dc0d..6fb5db2e1fc 100644 > > --- a/libstdc++-v3/include/bits/version.def > > +++ b/libstdc++-v3/include/bits/version.def > > @@ -1837,6 +1837,14 @@ ftms = { > > }; > > }; > > > > +ftms = { > > + name = ranges_cache_latest; > > + values = { > > + v = 202411; > > + cxxmin = 26; > > + }; > > +}; > > + > > ftms = { > > name = ranges_concat; > > values = { > > diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h > > index 70de189b1e0..db61a396c45 100644 > > --- a/libstdc++-v3/include/bits/version.h > > +++ b/libstdc++-v3/include/bits/version.h > > @@ -2035,6 +2035,16 @@ > > #endif /* !defined(__cpp_lib_is_virtual_base_of) && defined(__glibcxx_want_is_virtual_base_of) */ > > #undef __glibcxx_want_is_virtual_base_of > > > > +#if !defined(__cpp_lib_ranges_cache_latest) > > +# if (__cplusplus > 202302L) > > +# define __glibcxx_ranges_cache_latest 202411L > > +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges_cache_latest) > > +# define __cpp_lib_ranges_cache_latest 202411L > > +# endif > > +# endif > > +#endif /* !defined(__cpp_lib_ranges_cache_latest) && defined(__glibcxx_want_ranges_cache_latest) */ > > +#undef __glibcxx_want_ranges_cache_latest > > + > > #if !defined(__cpp_lib_ranges_concat) > > # if (__cplusplus > 202302L) > > # define __glibcxx_ranges_concat 202403L > > diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges > > index 5c795a90fbc..db9a00be264 100644 > > --- a/libstdc++-v3/include/std/ranges > > +++ b/libstdc++-v3/include/std/ranges > > @@ -58,6 +58,7 @@ > > #define __glibcxx_want_ranges_as_const > > #define __glibcxx_want_ranges_as_rvalue > > #define __glibcxx_want_ranges_cartesian_product > > +#define __glibcxx_want_ranges_cache_latest > > #define __glibcxx_want_ranges_concat > > #define __glibcxx_want_ranges_chunk > > #define __glibcxx_want_ranges_chunk_by > > @@ -1534,6 +1535,8 @@ namespace views::__adaptor > > this->_M_payload._M_apply(_Optional_func{__f}, __i); > > return this->_M_get(); > > } > > + > > + using _Optional_base<_Tp>::_M_reset; I also forgot to mention this change in the ChangeLog. > > }; > > > > template<range _Range> > > @@ -10203,6 +10206,192 @@ namespace ranges > > } // namespace ranges > > #endif // __cpp_lib_ranges_concat > > > > +#if __cpp_lib_ranges_cache_latest // C++ >= 26 > > +namespace ranges > > +{ > > + template<input_range _Vp> > > + requires view<_Vp> > > + class cache_latest_view : public view_interface<cache_latest_view<_Vp>> > > + { > > + _Vp _M_base = _Vp(); > > + > > + using __cache_t = conditional_t<is_reference_v<range_reference_t<_Vp>>, > > + add_pointer_t<range_reference_t<_Vp>>, > > + range_reference_t<_Vp>>; > > __conditional_t is cheaper to instantiate than conditional_t, so when > it doesn't affect the mangled name of a public symbol we should prefer > __conditional_t. Ack, fixed below. -- >8 -- libstdc++-v3/ChangeLog: * include/bits/version.def (ranges_cache_latest): Define. * include/bits/version.h: Regenerate. * include/std/ranges (__detail::__non_propagating_cache::_M_reset): Export from base class _Optional_base. (cache_latest_view): Define for C++26. (cache_latest_view::_Iterator): Likewise. (cache_latest_view::_Sentinel): Likewise. (views::__detail::__can_cache_latest): Likewise. (views::_CacheLatest, views::cache_latest): Likewise. * testsuite/std/ranges/adaptors/cache_latest/1.cc: New test. --- libstdc++-v3/include/bits/version.def | 8 + libstdc++-v3/include/bits/version.h | 10 + libstdc++-v3/include/std/ranges | 189 ++++++++++++++++++ .../std/ranges/adaptors/cache_latest/1.cc | 72 +++++++ 4 files changed, 279 insertions(+) create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/cache_latest/1.cc diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 002e560dc0d..6fb5db2e1fc 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1837,6 +1837,14 @@ ftms = { }; }; +ftms = { + name = ranges_cache_latest; + values = { + v = 202411; + cxxmin = 26; + }; +}; + ftms = { name = ranges_concat; values = { diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 70de189b1e0..db61a396c45 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -2035,6 +2035,16 @@ #endif /* !defined(__cpp_lib_is_virtual_base_of) && defined(__glibcxx_want_is_virtual_base_of) */ #undef __glibcxx_want_is_virtual_base_of +#if !defined(__cpp_lib_ranges_cache_latest) +# if (__cplusplus > 202302L) +# define __glibcxx_ranges_cache_latest 202411L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges_cache_latest) +# define __cpp_lib_ranges_cache_latest 202411L +# endif +# endif +#endif /* !defined(__cpp_lib_ranges_cache_latest) && defined(__glibcxx_want_ranges_cache_latest) */ +#undef __glibcxx_want_ranges_cache_latest + #if !defined(__cpp_lib_ranges_concat) # if (__cplusplus > 202302L) # define __glibcxx_ranges_concat 202403L diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index 5c795a90fbc..07ec90248a6 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -57,6 +57,7 @@ #define __glibcxx_want_ranges #define __glibcxx_want_ranges_as_const #define __glibcxx_want_ranges_as_rvalue +#define __glibcxx_want_ranges_cache_latest #define __glibcxx_want_ranges_cartesian_product #define __glibcxx_want_ranges_concat #define __glibcxx_want_ranges_chunk @@ -1534,6 +1535,8 @@ namespace views::__adaptor this->_M_payload._M_apply(_Optional_func{__f}, __i); return this->_M_get(); } + + using _Optional_base<_Tp>::_M_reset; }; template<range _Range> @@ -10203,6 +10206,192 @@ namespace ranges } // namespace ranges #endif // __cpp_lib_ranges_concat +#if __cpp_lib_ranges_cache_latest // C++ >= 26 +namespace ranges +{ + template<input_range _Vp> + requires view<_Vp> + class cache_latest_view : public view_interface<cache_latest_view<_Vp>> + { + _Vp _M_base = _Vp(); + + using __cache_t = __conditional_t<is_reference_v<range_reference_t<_Vp>>, + add_pointer_t<range_reference_t<_Vp>>, + range_reference_t<_Vp>>; + __detail::__non_propagating_cache<__cache_t> _M_cache; + + class _Iterator; + class _Sentinel; + + public: + cache_latest_view() requires default_initializable<_Vp> = default; + + constexpr explicit + cache_latest_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() + { return _Iterator(*this); } + + constexpr auto + end() + { return _Sentinel(*this); } + + 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> + cache_latest_view(_Range&&) -> cache_latest_view<views::all_t<_Range>>; + + template<input_range _Vp> + requires view<_Vp> + class cache_latest_view<_Vp>::_Iterator + { + cache_latest_view* _M_parent; + iterator_t<_Vp> _M_current; + + constexpr explicit + _Iterator(cache_latest_view& __parent) + : _M_parent(std::__addressof(__parent)), + _M_current(ranges::begin(__parent._M_base)) + { } + + friend class cache_latest_view; + + public: + using difference_type = range_difference_t<_Vp>; + using value_type = range_value_t<_Vp>; + using iterator_concept = input_iterator_tag; + + _Iterator(_Iterator&&) = default; + + _Iterator& + operator=(_Iterator&&) = default; + + constexpr iterator_t<_Vp> + base() && + { return std::move(_M_current); } + + constexpr const iterator_t<_Vp>& + base() const & noexcept + { return _M_current; } + + constexpr range_reference_t<_Vp>& + operator*() const + { + if constexpr (is_reference_v<range_reference_t<_Vp>>) + { + if (!_M_parent->_M_cache) + _M_parent->_M_cache = std::__addressof(__detail::__as_lvalue(*_M_current)); + return **_M_parent->_M_cache; + } + else + { + if (!_M_parent->_M_cache) + _M_parent->_M_cache._M_emplace_deref(_M_current); + return *_M_parent->_M_cache; + } + } + + constexpr _Iterator& + operator++() + { + _M_parent->_M_cache._M_reset(); + ++_M_current; + return *this; + } + + constexpr void + operator++(int) + { ++*this; } + + friend constexpr range_rvalue_reference_t<_Vp> + 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<_Vp>> + { ranges::iter_swap(__x._M_current, __y._M_current); } + }; + + template<input_range _Vp> + requires view<_Vp> + class cache_latest_view<_Vp>::_Sentinel + { + sentinel_t<_Vp> _M_end = sentinel_t<_Vp>(); + + constexpr explicit + _Sentinel(cache_latest_view& __parent) + : _M_end(ranges::end(__parent._M_base)) + { } + + friend class cache_latest_view; + + public: + _Sentinel() = default; + + constexpr sentinel_t<_Vp> + base() const + { return _M_end; } + + friend constexpr bool + operator==(const _Iterator& __x, const _Sentinel& __y) + { return __x._M_current == __y._M_end; } + + friend constexpr range_difference_t<_Vp> + operator-(const _Iterator& __x, const _Sentinel& __y) + requires sized_sentinel_for<sentinel_t<_Vp>, iterator_t<_Vp>> + { return __x._M_current - __y._M_end; } + + friend constexpr range_difference_t<_Vp> + operator-(const _Sentinel& __x, const _Iterator& __y) + requires sized_sentinel_for<sentinel_t<_Vp>, iterator_t<_Vp>> + { return __x._M_end - __y._M_current; } + }; + + namespace views + { + namespace __detail + { + template<typename _Tp> + concept __can_cache_latest = requires { cache_latest_view(std::declval<_Tp>()); }; + } + + struct _CacheLatest : __adaptor::_RangeAdaptorClosure<_CacheLatest> + { + template<viewable_range _Range> + requires __detail::__can_cache_latest<_Range> + constexpr auto + operator() [[nodiscard]] (_Range&& __r) const + { return cache_latest_view(std::forward<_Range>(__r)); } + + static constexpr bool _S_has_simple_call_op = true; + }; + + inline constexpr _CacheLatest cache_latest; + } +} // namespace ranges +#endif // __cpp_lib_ranges_cache_latest + _GLIBCXX_END_NAMESPACE_VERSION } // namespace std #endif // library concepts diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/cache_latest/1.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/cache_latest/1.cc new file mode 100644 index 00000000000..5904831c4e0 --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/cache_latest/1.cc @@ -0,0 +1,72 @@ +// { dg-do run { target c++26 } } + +#include <ranges> + +#if __cpp_lib_ranges_cache_latest != 202411L +# error "Feature-test macro __cpp_lib_ranges_cache_latest has wrong value in <ranges>" +#endif + +#include <algorithm> +#include <testsuite_hooks.h> + +namespace ranges = std::ranges; +namespace views = std::views; + +constexpr bool +test01() +{ + int xs[] = {1,2,3,4,5}; + auto v = xs | views::cache_latest; + VERIFY( ranges::equal(v, xs) ); + VERIFY( ranges::size(v) == 5 ); + + auto it = v.begin(); + auto st = v.end(); + VERIFY( st - it == 5 ); + VERIFY( it - st == -5 ); + it++; + VERIFY( st - it == 4 ); + VERIFY( it - st == -4 ); + + auto jt = v.begin(); + ranges::iter_swap(it, jt); + VERIFY( ranges::equal(xs, (int[]){2,1,3,4,5}) ); + int n = ranges::iter_move(it); + VERIFY( n == 1 ); + ranges::iter_swap(it, jt); + + auto w = views::iota(1, 6) | views::cache_latest; + VERIFY( ranges::equal(w, xs) ); + + return true; +} + +constexpr bool +test02() +{ + // Motivating example from P3138R5 + int xs[] = {1, 2, 3, 4, 5}; + int transform_count = 0; + auto v = xs | views::transform([&](int i){ ++transform_count; return i * i; }) + | views::filter([](int i){ return i % 2 == 0; }); + VERIFY( ranges::equal(v, (int[]){4, 16}) ); + VERIFY( transform_count == 7 ); + + transform_count = 0; + auto w = xs | views::transform([&](int i){ ++transform_count; return i * i; }) + | views::cache_latest + | views::filter([](int i){ return i % 2 == 0; }); + VERIFY( ranges::equal(w, (int[]){4, 16}) ); + VERIFY( transform_count == 5 ); + + return true; +} + +int +main() +{ + static_assert(test01()); + static_assert(test02()); + test01(); + test02(); +}
On Fri, 14 Feb 2025 at 05:12, Patrick Palka wrote: > > On Thu, 13 Feb 2025, Jonathan Wakely wrote: > > > On Tue, 11 Feb 2025 at 05:59, 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_cache_latest): Define. > > > * include/bits/version.h: Regenerate. > > > * include/std/ranges (cache_latest_view): Define for C++26. > > > (cache_latest_view::_Iterator): Likewise. > > > (cache_latest_view::_Sentinel): Likewise. > > > (views::__detail::__can_cache_latest): Likewise. > > > (views::_CacheLatest, views::cache_latest): Likewise. > > > * testsuite/std/ranges/adaptors/cache_latest/1.cc: New test. > > > > The test is missing from the patch. > > Whoops, below is the complete patch. OK for trunk, thanks. > > > > > > --- > > > libstdc++-v3/include/bits/version.def | 8 ++ > > > libstdc++-v3/include/bits/version.h | 10 ++ > > > libstdc++-v3/include/std/ranges | 189 ++++++++++++++++++++++++++ > > > 3 files changed, 207 insertions(+) > > > > > > diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def > > > index 002e560dc0d..6fb5db2e1fc 100644 > > > --- a/libstdc++-v3/include/bits/version.def > > > +++ b/libstdc++-v3/include/bits/version.def > > > @@ -1837,6 +1837,14 @@ ftms = { > > > }; > > > }; > > > > > > +ftms = { > > > + name = ranges_cache_latest; > > > + values = { > > > + v = 202411; > > > + cxxmin = 26; > > > + }; > > > +}; > > > + > > > ftms = { > > > name = ranges_concat; > > > values = { > > > diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h > > > index 70de189b1e0..db61a396c45 100644 > > > --- a/libstdc++-v3/include/bits/version.h > > > +++ b/libstdc++-v3/include/bits/version.h > > > @@ -2035,6 +2035,16 @@ > > > #endif /* !defined(__cpp_lib_is_virtual_base_of) && defined(__glibcxx_want_is_virtual_base_of) */ > > > #undef __glibcxx_want_is_virtual_base_of > > > > > > +#if !defined(__cpp_lib_ranges_cache_latest) > > > +# if (__cplusplus > 202302L) > > > +# define __glibcxx_ranges_cache_latest 202411L > > > +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges_cache_latest) > > > +# define __cpp_lib_ranges_cache_latest 202411L > > > +# endif > > > +# endif > > > +#endif /* !defined(__cpp_lib_ranges_cache_latest) && defined(__glibcxx_want_ranges_cache_latest) */ > > > +#undef __glibcxx_want_ranges_cache_latest > > > + > > > #if !defined(__cpp_lib_ranges_concat) > > > # if (__cplusplus > 202302L) > > > # define __glibcxx_ranges_concat 202403L > > > diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges > > > index 5c795a90fbc..db9a00be264 100644 > > > --- a/libstdc++-v3/include/std/ranges > > > +++ b/libstdc++-v3/include/std/ranges > > > @@ -58,6 +58,7 @@ > > > #define __glibcxx_want_ranges_as_const > > > #define __glibcxx_want_ranges_as_rvalue > > > #define __glibcxx_want_ranges_cartesian_product > > > +#define __glibcxx_want_ranges_cache_latest > > > #define __glibcxx_want_ranges_concat > > > #define __glibcxx_want_ranges_chunk > > > #define __glibcxx_want_ranges_chunk_by > > > @@ -1534,6 +1535,8 @@ namespace views::__adaptor > > > this->_M_payload._M_apply(_Optional_func{__f}, __i); > > > return this->_M_get(); > > > } > > > + > > > + using _Optional_base<_Tp>::_M_reset; > > I also forgot to mention this change in the ChangeLog. > > > > }; > > > > > > template<range _Range> > > > @@ -10203,6 +10206,192 @@ namespace ranges > > > } // namespace ranges > > > #endif // __cpp_lib_ranges_concat > > > > > > +#if __cpp_lib_ranges_cache_latest // C++ >= 26 > > > +namespace ranges > > > +{ > > > + template<input_range _Vp> > > > + requires view<_Vp> > > > + class cache_latest_view : public view_interface<cache_latest_view<_Vp>> > > > + { > > > + _Vp _M_base = _Vp(); > > > + > > > + using __cache_t = conditional_t<is_reference_v<range_reference_t<_Vp>>, > > > + add_pointer_t<range_reference_t<_Vp>>, > > > + range_reference_t<_Vp>>; > > > > __conditional_t is cheaper to instantiate than conditional_t, so when > > it doesn't affect the mangled name of a public symbol we should prefer > > __conditional_t. > > Ack, fixed below. > > -- >8 -- > > libstdc++-v3/ChangeLog: > > * include/bits/version.def (ranges_cache_latest): Define. > * include/bits/version.h: Regenerate. > * include/std/ranges (__detail::__non_propagating_cache::_M_reset): > Export from base class _Optional_base. > (cache_latest_view): Define for C++26. > (cache_latest_view::_Iterator): Likewise. > (cache_latest_view::_Sentinel): Likewise. > (views::__detail::__can_cache_latest): Likewise. > (views::_CacheLatest, views::cache_latest): Likewise. > * testsuite/std/ranges/adaptors/cache_latest/1.cc: New test. > --- > libstdc++-v3/include/bits/version.def | 8 + > libstdc++-v3/include/bits/version.h | 10 + > libstdc++-v3/include/std/ranges | 189 ++++++++++++++++++ > .../std/ranges/adaptors/cache_latest/1.cc | 72 +++++++ > 4 files changed, 279 insertions(+) > create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/cache_latest/1.cc > > diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def > index 002e560dc0d..6fb5db2e1fc 100644 > --- a/libstdc++-v3/include/bits/version.def > +++ b/libstdc++-v3/include/bits/version.def > @@ -1837,6 +1837,14 @@ ftms = { > }; > }; > > +ftms = { > + name = ranges_cache_latest; > + values = { > + v = 202411; > + cxxmin = 26; > + }; > +}; > + > ftms = { > name = ranges_concat; > values = { > diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h > index 70de189b1e0..db61a396c45 100644 > --- a/libstdc++-v3/include/bits/version.h > +++ b/libstdc++-v3/include/bits/version.h > @@ -2035,6 +2035,16 @@ > #endif /* !defined(__cpp_lib_is_virtual_base_of) && defined(__glibcxx_want_is_virtual_base_of) */ > #undef __glibcxx_want_is_virtual_base_of > > +#if !defined(__cpp_lib_ranges_cache_latest) > +# if (__cplusplus > 202302L) > +# define __glibcxx_ranges_cache_latest 202411L > +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges_cache_latest) > +# define __cpp_lib_ranges_cache_latest 202411L > +# endif > +# endif > +#endif /* !defined(__cpp_lib_ranges_cache_latest) && defined(__glibcxx_want_ranges_cache_latest) */ > +#undef __glibcxx_want_ranges_cache_latest > + > #if !defined(__cpp_lib_ranges_concat) > # if (__cplusplus > 202302L) > # define __glibcxx_ranges_concat 202403L > diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges > index 5c795a90fbc..07ec90248a6 100644 > --- a/libstdc++-v3/include/std/ranges > +++ b/libstdc++-v3/include/std/ranges > @@ -57,6 +57,7 @@ > #define __glibcxx_want_ranges > #define __glibcxx_want_ranges_as_const > #define __glibcxx_want_ranges_as_rvalue > +#define __glibcxx_want_ranges_cache_latest > #define __glibcxx_want_ranges_cartesian_product > #define __glibcxx_want_ranges_concat > #define __glibcxx_want_ranges_chunk > @@ -1534,6 +1535,8 @@ namespace views::__adaptor > this->_M_payload._M_apply(_Optional_func{__f}, __i); > return this->_M_get(); > } > + > + using _Optional_base<_Tp>::_M_reset; > }; > > template<range _Range> > @@ -10203,6 +10206,192 @@ namespace ranges > } // namespace ranges > #endif // __cpp_lib_ranges_concat > > +#if __cpp_lib_ranges_cache_latest // C++ >= 26 > +namespace ranges > +{ > + template<input_range _Vp> > + requires view<_Vp> > + class cache_latest_view : public view_interface<cache_latest_view<_Vp>> > + { > + _Vp _M_base = _Vp(); > + > + using __cache_t = __conditional_t<is_reference_v<range_reference_t<_Vp>>, > + add_pointer_t<range_reference_t<_Vp>>, > + range_reference_t<_Vp>>; > + __detail::__non_propagating_cache<__cache_t> _M_cache; > + > + class _Iterator; > + class _Sentinel; > + > + public: > + cache_latest_view() requires default_initializable<_Vp> = default; > + > + constexpr explicit > + cache_latest_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() > + { return _Iterator(*this); } > + > + constexpr auto > + end() > + { return _Sentinel(*this); } > + > + 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> > + cache_latest_view(_Range&&) -> cache_latest_view<views::all_t<_Range>>; > + > + template<input_range _Vp> > + requires view<_Vp> > + class cache_latest_view<_Vp>::_Iterator > + { > + cache_latest_view* _M_parent; > + iterator_t<_Vp> _M_current; > + > + constexpr explicit > + _Iterator(cache_latest_view& __parent) > + : _M_parent(std::__addressof(__parent)), > + _M_current(ranges::begin(__parent._M_base)) > + { } > + > + friend class cache_latest_view; > + > + public: > + using difference_type = range_difference_t<_Vp>; > + using value_type = range_value_t<_Vp>; > + using iterator_concept = input_iterator_tag; > + > + _Iterator(_Iterator&&) = default; > + > + _Iterator& > + operator=(_Iterator&&) = default; > + > + constexpr iterator_t<_Vp> > + base() && > + { return std::move(_M_current); } > + > + constexpr const iterator_t<_Vp>& > + base() const & noexcept > + { return _M_current; } > + > + constexpr range_reference_t<_Vp>& > + operator*() const > + { > + if constexpr (is_reference_v<range_reference_t<_Vp>>) > + { > + if (!_M_parent->_M_cache) > + _M_parent->_M_cache = std::__addressof(__detail::__as_lvalue(*_M_current)); > + return **_M_parent->_M_cache; > + } > + else > + { > + if (!_M_parent->_M_cache) > + _M_parent->_M_cache._M_emplace_deref(_M_current); > + return *_M_parent->_M_cache; > + } > + } > + > + constexpr _Iterator& > + operator++() > + { > + _M_parent->_M_cache._M_reset(); > + ++_M_current; > + return *this; > + } > + > + constexpr void > + operator++(int) > + { ++*this; } > + > + friend constexpr range_rvalue_reference_t<_Vp> > + 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<_Vp>> > + { ranges::iter_swap(__x._M_current, __y._M_current); } > + }; > + > + template<input_range _Vp> > + requires view<_Vp> > + class cache_latest_view<_Vp>::_Sentinel > + { > + sentinel_t<_Vp> _M_end = sentinel_t<_Vp>(); > + > + constexpr explicit > + _Sentinel(cache_latest_view& __parent) > + : _M_end(ranges::end(__parent._M_base)) > + { } > + > + friend class cache_latest_view; > + > + public: > + _Sentinel() = default; > + > + constexpr sentinel_t<_Vp> > + base() const > + { return _M_end; } > + > + friend constexpr bool > + operator==(const _Iterator& __x, const _Sentinel& __y) > + { return __x._M_current == __y._M_end; } > + > + friend constexpr range_difference_t<_Vp> > + operator-(const _Iterator& __x, const _Sentinel& __y) > + requires sized_sentinel_for<sentinel_t<_Vp>, iterator_t<_Vp>> > + { return __x._M_current - __y._M_end; } > + > + friend constexpr range_difference_t<_Vp> > + operator-(const _Sentinel& __x, const _Iterator& __y) > + requires sized_sentinel_for<sentinel_t<_Vp>, iterator_t<_Vp>> > + { return __x._M_end - __y._M_current; } > + }; > + > + namespace views > + { > + namespace __detail > + { > + template<typename _Tp> > + concept __can_cache_latest = requires { cache_latest_view(std::declval<_Tp>()); }; > + } > + > + struct _CacheLatest : __adaptor::_RangeAdaptorClosure<_CacheLatest> > + { > + template<viewable_range _Range> > + requires __detail::__can_cache_latest<_Range> > + constexpr auto > + operator() [[nodiscard]] (_Range&& __r) const > + { return cache_latest_view(std::forward<_Range>(__r)); } > + > + static constexpr bool _S_has_simple_call_op = true; > + }; > + > + inline constexpr _CacheLatest cache_latest; > + } > +} // namespace ranges > +#endif // __cpp_lib_ranges_cache_latest > + > _GLIBCXX_END_NAMESPACE_VERSION > } // namespace std > #endif // library concepts > diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/cache_latest/1.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/cache_latest/1.cc > new file mode 100644 > index 00000000000..5904831c4e0 > --- /dev/null > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/cache_latest/1.cc > @@ -0,0 +1,72 @@ > +// { dg-do run { target c++26 } } > + > +#include <ranges> > + > +#if __cpp_lib_ranges_cache_latest != 202411L > +# error "Feature-test macro __cpp_lib_ranges_cache_latest has wrong value in <ranges>" > +#endif > + > +#include <algorithm> > +#include <testsuite_hooks.h> > + > +namespace ranges = std::ranges; > +namespace views = std::views; > + > +constexpr bool > +test01() > +{ > + int xs[] = {1,2,3,4,5}; > + auto v = xs | views::cache_latest; > + VERIFY( ranges::equal(v, xs) ); > + VERIFY( ranges::size(v) == 5 ); > + > + auto it = v.begin(); > + auto st = v.end(); > + VERIFY( st - it == 5 ); > + VERIFY( it - st == -5 ); > + it++; > + VERIFY( st - it == 4 ); > + VERIFY( it - st == -4 ); > + > + auto jt = v.begin(); > + ranges::iter_swap(it, jt); > + VERIFY( ranges::equal(xs, (int[]){2,1,3,4,5}) ); > + int n = ranges::iter_move(it); > + VERIFY( n == 1 ); > + ranges::iter_swap(it, jt); > + > + auto w = views::iota(1, 6) | views::cache_latest; > + VERIFY( ranges::equal(w, xs) ); > + > + return true; > +} > + > +constexpr bool > +test02() > +{ > + // Motivating example from P3138R5 > + int xs[] = {1, 2, 3, 4, 5}; > + int transform_count = 0; > + auto v = xs | views::transform([&](int i){ ++transform_count; return i * i; }) > + | views::filter([](int i){ return i % 2 == 0; }); > + VERIFY( ranges::equal(v, (int[]){4, 16}) ); > + VERIFY( transform_count == 7 ); > + > + transform_count = 0; > + auto w = xs | views::transform([&](int i){ ++transform_count; return i * i; }) > + | views::cache_latest > + | views::filter([](int i){ return i % 2 == 0; }); > + VERIFY( ranges::equal(w, (int[]){4, 16}) ); > + VERIFY( transform_count == 5 ); > + > + return true; > +} > + > +int > +main() > +{ > + static_assert(test01()); > + static_assert(test02()); > + test01(); > + test02(); > +} > -- > 2.48.1.329.ge2067b49ec.dirty > > > > > > > + __detail::__non_propagating_cache<__cache_t> _M_cache; > > > + > > > + class _Iterator; > > > + class _Sentinel; > > > + > > > + public: > > > + cache_latest_view() requires default_initializable<_Vp> = default; > > > + > > > + constexpr explicit > > > + cache_latest_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() > > > + { return _Iterator(*this); } > > > + > > > + constexpr auto > > > + end() > > > + { return _Sentinel(*this); } > > > + > > > + 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> > > > + cache_latest_view(_Range&&) -> cache_latest_view<views::all_t<_Range>>; > > > + > > > + template<input_range _Vp> > > > + requires view<_Vp> > > > + class cache_latest_view<_Vp>::_Iterator > > > + { > > > + cache_latest_view* _M_parent; > > > + iterator_t<_Vp> _M_current; > > > + > > > + constexpr explicit > > > + _Iterator(cache_latest_view& __parent) > > > + : _M_parent(std::__addressof(__parent)), > > > + _M_current(ranges::begin(__parent._M_base)) > > > + { } > > > + > > > + friend class cache_latest_view; > > > + > > > + public: > > > + using difference_type = range_difference_t<_Vp>; > > > + using value_type = range_value_t<_Vp>; > > > + using iterator_concept = input_iterator_tag; > > > + > > > + _Iterator(_Iterator&&) = default; > > > + > > > + _Iterator& > > > + operator=(_Iterator&&) = default; > > > + > > > + constexpr iterator_t<_Vp> > > > + base() && > > > + { return std::move(_M_current); } > > > + > > > + constexpr const iterator_t<_Vp>& > > > + base() const & noexcept > > > + { return _M_current; } > > > + > > > + constexpr range_reference_t<_Vp>& > > > + operator*() const > > > + { > > > + if constexpr (is_reference_v<range_reference_t<_Vp>>) > > > + { > > > + if (!_M_parent->_M_cache) > > > + _M_parent->_M_cache = std::__addressof(__detail::__as_lvalue(*_M_current)); > > > + return **_M_parent->_M_cache; > > > + } > > > + else > > > + { > > > + if (!_M_parent->_M_cache) > > > + _M_parent->_M_cache._M_emplace_deref(_M_current); > > > + return *_M_parent->_M_cache; > > > + } > > > + } > > > + > > > + constexpr _Iterator& > > > + operator++() > > > + { > > > + _M_parent->_M_cache._M_reset(); > > > + ++_M_current; > > > + return *this; > > > + } > > > + > > > + constexpr void > > > + operator++(int) > > > + { ++*this; } > > > + > > > + friend constexpr range_rvalue_reference_t<_Vp> > > > + 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<_Vp>> > > > + { ranges::iter_swap(__x._M_current, __y._M_current); } > > > + }; > > > + > > > + template<input_range _Vp> > > > + requires view<_Vp> > > > + class cache_latest_view<_Vp>::_Sentinel > > > + { > > > + sentinel_t<_Vp> _M_end = sentinel_t<_Vp>(); > > > + > > > + constexpr explicit > > > + _Sentinel(cache_latest_view& parent) > > > + : _M_end(ranges::end(parent._M_base)) > > > + { } > > > + > > > + friend class cache_latest_view; > > > + > > > + public: > > > + _Sentinel() = default; > > > + > > > + constexpr sentinel_t<_Vp> > > > + base() const > > > + { return _M_end; } > > > + > > > + friend constexpr bool > > > + operator==(const _Iterator& __x, const _Sentinel& __y) > > > + { return __x._M_current == __y._M_end; } > > > + > > > + friend constexpr range_difference_t<_Vp> > > > + operator-(const _Iterator& __x, const _Sentinel& __y) > > > + requires sized_sentinel_for<sentinel_t<_Vp>, iterator_t<_Vp>> > > > + { return __x._M_current - __y._M_end; } > > > + > > > + friend constexpr range_difference_t<_Vp> > > > + operator-(const _Sentinel& __x, const _Iterator& __y) > > > + requires sized_sentinel_for<sentinel_t<_Vp>, iterator_t<_Vp>> > > > + { return __x._M_end - __y._M_current; } > > > + }; > > > + > > > + namespace views > > > + { > > > + namespace __detail > > > + { > > > + template<typename _Tp> > > > + concept __can_cache_latest = requires { cache_latest_view(std::declval<_Tp>()); }; > > > + } > > > + > > > + struct _CacheLatest : __adaptor::_RangeAdaptorClosure<_CacheLatest> > > > + { > > > + template<viewable_range _Range> > > > + requires __detail::__can_cache_latest<_Range> > > > + constexpr auto > > > + operator() [[nodiscard]] (_Range&& __r) const > > > + { return cache_latest_view(std::forward<_Range>(__r)); } > > > + > > > + static constexpr bool _S_has_simple_call_op = true; > > > + }; > > > + > > > + inline constexpr _CacheLatest cache_latest; > > > + } > > > +} // namespace ranges > > > +#endif // __cpp_lib_ranges_cache_latest > > > + > > > _GLIBCXX_END_NAMESPACE_VERSION > > > } // namespace std > > > #endif // library concepts > > > -- > > > 2.48.1.291.g388218fac7.dirty > > > > > > > >
diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 002e560dc0d..6fb5db2e1fc 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1837,6 +1837,14 @@ ftms = { }; }; +ftms = { + name = ranges_cache_latest; + values = { + v = 202411; + cxxmin = 26; + }; +}; + ftms = { name = ranges_concat; values = { diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 70de189b1e0..db61a396c45 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -2035,6 +2035,16 @@ #endif /* !defined(__cpp_lib_is_virtual_base_of) && defined(__glibcxx_want_is_virtual_base_of) */ #undef __glibcxx_want_is_virtual_base_of +#if !defined(__cpp_lib_ranges_cache_latest) +# if (__cplusplus > 202302L) +# define __glibcxx_ranges_cache_latest 202411L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges_cache_latest) +# define __cpp_lib_ranges_cache_latest 202411L +# endif +# endif +#endif /* !defined(__cpp_lib_ranges_cache_latest) && defined(__glibcxx_want_ranges_cache_latest) */ +#undef __glibcxx_want_ranges_cache_latest + #if !defined(__cpp_lib_ranges_concat) # if (__cplusplus > 202302L) # define __glibcxx_ranges_concat 202403L diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index 5c795a90fbc..db9a00be264 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -58,6 +58,7 @@ #define __glibcxx_want_ranges_as_const #define __glibcxx_want_ranges_as_rvalue #define __glibcxx_want_ranges_cartesian_product +#define __glibcxx_want_ranges_cache_latest #define __glibcxx_want_ranges_concat #define __glibcxx_want_ranges_chunk #define __glibcxx_want_ranges_chunk_by @@ -1534,6 +1535,8 @@ namespace views::__adaptor this->_M_payload._M_apply(_Optional_func{__f}, __i); return this->_M_get(); } + + using _Optional_base<_Tp>::_M_reset; }; template<range _Range> @@ -10203,6 +10206,192 @@ namespace ranges } // namespace ranges #endif // __cpp_lib_ranges_concat +#if __cpp_lib_ranges_cache_latest // C++ >= 26 +namespace ranges +{ + template<input_range _Vp> + requires view<_Vp> + class cache_latest_view : public view_interface<cache_latest_view<_Vp>> + { + _Vp _M_base = _Vp(); + + using __cache_t = conditional_t<is_reference_v<range_reference_t<_Vp>>, + add_pointer_t<range_reference_t<_Vp>>, + range_reference_t<_Vp>>; + __detail::__non_propagating_cache<__cache_t> _M_cache; + + class _Iterator; + class _Sentinel; + + public: + cache_latest_view() requires default_initializable<_Vp> = default; + + constexpr explicit + cache_latest_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() + { return _Iterator(*this); } + + constexpr auto + end() + { return _Sentinel(*this); } + + 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> + cache_latest_view(_Range&&) -> cache_latest_view<views::all_t<_Range>>; + + template<input_range _Vp> + requires view<_Vp> + class cache_latest_view<_Vp>::_Iterator + { + cache_latest_view* _M_parent; + iterator_t<_Vp> _M_current; + + constexpr explicit + _Iterator(cache_latest_view& __parent) + : _M_parent(std::__addressof(__parent)), + _M_current(ranges::begin(__parent._M_base)) + { } + + friend class cache_latest_view; + + public: + using difference_type = range_difference_t<_Vp>; + using value_type = range_value_t<_Vp>; + using iterator_concept = input_iterator_tag; + + _Iterator(_Iterator&&) = default; + + _Iterator& + operator=(_Iterator&&) = default; + + constexpr iterator_t<_Vp> + base() && + { return std::move(_M_current); } + + constexpr const iterator_t<_Vp>& + base() const & noexcept + { return _M_current; } + + constexpr range_reference_t<_Vp>& + operator*() const + { + if constexpr (is_reference_v<range_reference_t<_Vp>>) + { + if (!_M_parent->_M_cache) + _M_parent->_M_cache = std::__addressof(__detail::__as_lvalue(*_M_current)); + return **_M_parent->_M_cache; + } + else + { + if (!_M_parent->_M_cache) + _M_parent->_M_cache._M_emplace_deref(_M_current); + return *_M_parent->_M_cache; + } + } + + constexpr _Iterator& + operator++() + { + _M_parent->_M_cache._M_reset(); + ++_M_current; + return *this; + } + + constexpr void + operator++(int) + { ++*this; } + + friend constexpr range_rvalue_reference_t<_Vp> + 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<_Vp>> + { ranges::iter_swap(__x._M_current, __y._M_current); } + }; + + template<input_range _Vp> + requires view<_Vp> + class cache_latest_view<_Vp>::_Sentinel + { + sentinel_t<_Vp> _M_end = sentinel_t<_Vp>(); + + constexpr explicit + _Sentinel(cache_latest_view& parent) + : _M_end(ranges::end(parent._M_base)) + { } + + friend class cache_latest_view; + + public: + _Sentinel() = default; + + constexpr sentinel_t<_Vp> + base() const + { return _M_end; } + + friend constexpr bool + operator==(const _Iterator& __x, const _Sentinel& __y) + { return __x._M_current == __y._M_end; } + + friend constexpr range_difference_t<_Vp> + operator-(const _Iterator& __x, const _Sentinel& __y) + requires sized_sentinel_for<sentinel_t<_Vp>, iterator_t<_Vp>> + { return __x._M_current - __y._M_end; } + + friend constexpr range_difference_t<_Vp> + operator-(const _Sentinel& __x, const _Iterator& __y) + requires sized_sentinel_for<sentinel_t<_Vp>, iterator_t<_Vp>> + { return __x._M_end - __y._M_current; } + }; + + namespace views + { + namespace __detail + { + template<typename _Tp> + concept __can_cache_latest = requires { cache_latest_view(std::declval<_Tp>()); }; + } + + struct _CacheLatest : __adaptor::_RangeAdaptorClosure<_CacheLatest> + { + template<viewable_range _Range> + requires __detail::__can_cache_latest<_Range> + constexpr auto + operator() [[nodiscard]] (_Range&& __r) const + { return cache_latest_view(std::forward<_Range>(__r)); } + + static constexpr bool _S_has_simple_call_op = true; + }; + + inline constexpr _CacheLatest cache_latest; + } +} // namespace ranges +#endif // __cpp_lib_ranges_cache_latest + _GLIBCXX_END_NAMESPACE_VERSION } // namespace std #endif // library concepts