https://gcc.gnu.org/g:410a4c126407c0ce048ad848d09cf4d39f578756

commit r15-7840-g410a4c126407c0ce048ad848d09cf4d39f578756
Author: Patrick Palka <ppa...@redhat.com>
Date:   Wed Mar 5 16:46:15 2025 -0500

    libstdc++: Implement P3138R5 views::cache_latest
    
    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.
    
    Reviewed-by: Jonathan Wakely <jwak...@redhat.com>

Diff:
---
 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(+)

diff --git a/libstdc++-v3/include/bits/version.def 
b/libstdc++-v3/include/bits/version.def
index 0cdc2e82fc55..eb2c6d8a3ee7 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1846,6 +1846,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 ec52cba517fa..05dadd6fd19c 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -2045,6 +2045,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) && (__cpp_pack_indexing)
 #  define __glibcxx_ranges_concat 202403L
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index def7527431cd..6790fcf7af19 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>
@@ -10201,6 +10204,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 000000000000..5904831c4e03
--- /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();
+}

Reply via email to