On Tue, 11 Feb 2025 at 05:59, 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_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
>

Reply via email to