[libcxx-commits] [libcxx] [libc++][ranges] implement `ranges::chunk_view` for `forward_range` (PR #85741)
Xiaoyang Liu via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Mar 19 12:44:29 PDT 2024
https://github.com/xiaoyang-sde updated https://github.com/llvm/llvm-project/pull/85741
>From 7a38a42d7954be707562a7aa4a8915ea6b6cd980 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Mon, 18 Mar 2024 23:29:02 -0700
Subject: [PATCH 1/3] [libc++] [ranges] implement 'ranges::chunk_view' for
'forward_range'
---
libcxx/include/CMakeLists.txt | 1 +
libcxx/include/__ranges/chunk_view.h | 356 +++++++++++++++++++++++++++
libcxx/include/ranges | 1 +
3 files changed, 358 insertions(+)
create mode 100644 libcxx/include/__ranges/chunk_view.h
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 6ed8d21d98a15a..88a91ce67bda7a 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -625,6 +625,7 @@ set(files
__ranges/all.h
__ranges/as_rvalue_view.h
__ranges/chunk_by_view.h
+ __ranges/chunk_view.h
__ranges/common_view.h
__ranges/concepts.h
__ranges/container_compatible_range.h
diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
new file mode 100644
index 00000000000000..e3ffdd376207e1
--- /dev/null
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -0,0 +1,356 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#ifndef _LIBCPP___RANGES_CHUNK_VIEW_H
+#define _LIBCPP___RANGES_CHUNK_VIEW_H
+
+#include <__concepts/constructible.h>
+#include <__config>
+#include <__functional/bind_back.h>
+#include <__iterator/advance.h>
+#include <__iterator/concepts.h>
+#include <__iterator/default_sentinel.h>
+#include <__iterator/distance.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/subrange.h>
+#include <__ranges/take_view.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/make_unsigned.h>
+#include <__type_traits/maybe_const.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+template <class _Integer>
+constexpr _Integer __div_ceil(_Integer __num, _Integer __denom) {
+ _Integer __r = __num / __denom;
+ if (__num % __denom) {
+ ++__r;
+ }
+ return __r;
+}
+
+template <view _View>
+ requires input_range<_View>
+class chunk_view : public view_interface<chunk_view<_View>> {};
+
+template <view _View>
+ requires forward_range<_View>
+class chunk_view<_View> : public view_interface<chunk_view<_View>> {
+private:
+ _LIBCPP_NO_UNIQUE_ADDRESS _View __base_;
+ range_difference_t<_View> __n_;
+
+ template <bool>
+ class __iterator;
+
+public:
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n)
+ : __base_(std::move(__base)), __n_(__n) {
+ _LIBCPP_ASSERT_UNCATEGORIZED(__n > 0, "__n must be greater than 0");
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+ requires copy_constructible<_View>
+ {
+ return __base_;
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
+ requires(!__simple_view<_View>)
+ {
+ return __iterator<false>(this, ranges::begin(__base_));
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+ requires forward_range<const _View>
+ {
+ return __iterator<true>(this, ranges::begin(__base_));
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto end()
+ requires(!__simple_view<_View>)
+ {
+ if constexpr (common_range<_View> && sized_range<_View>) {
+ auto __missing = (__n_ - ranges::distance(__base_) % __n_) % __n_;
+ return __iterator<false>(this, ranges::end(__base_), __missing);
+ } else if constexpr (common_range<_View> && !bidirectional_range<_View>) {
+ return __iterator<false>(this, ranges::end(__base_));
+ } else {
+ return default_sentinel;
+ }
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+ requires forward_range<_View>
+ {
+ if constexpr (common_range<const _View> && sized_range<const _View>) {
+ auto __missing = (__n_ - ranges::distance(__base_) % __n_) % __n_;
+ return __iterator<true>(this, ranges::end(__base_), __missing);
+ } else if constexpr (common_range<const _View> && !bidirectional_range<const _View>) {
+ return __iterator<true>(this, ranges::end(__base_));
+ } else {
+ return default_sentinel;
+ }
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+ requires sized_range<_View>
+ {
+ return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+ requires sized_range<const _View>
+ {
+ return std::__to_unsigned_like(__div_ceil(ranges::distance(__base_), __n_));
+ }
+};
+
+template <class _View>
+chunk_view(_View&&, range_difference_t<_View>) -> chunk_view<views::all_t<_View>>;
+
+template <class _View>
+inline constexpr bool enable_borrowed_range<chunk_view<_View>> = enable_borrowed_range<_View> && forward_range<_View>;
+
+template <view _View>
+ requires forward_range<_View>
+template <bool _Const>
+class chunk_view<_View>::__iterator {
+private:
+ friend chunk_view;
+
+ using _Parent = __maybe_const<_Const, chunk_view>;
+ using _Base = __maybe_const<_Const, _View>;
+
+ iterator_t<_Base> __current_ = iterator_t<_Base>();
+ sentinel_t<_Base> __end_ = sentinel_t<_Base>();
+ range_difference_t<_Base> __n_ = 0;
+ range_difference_t<_Base> __missing_ = 0;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator(
+ _Parent* __parent, iterator_t<_Base> __current, range_difference_t<_Base> __missing = 0)
+ : __current_(std::move(__current)),
+ __end_(ranges::end(__parent->__base_), __n_(__parent->__n_)),
+ __missing_(__missing) {}
+
+public:
+ using iterator_category = input_iterator_tag;
+ using iterator_concept =
+ conditional_t<random_access_range<_Base>,
+ random_access_iterator_tag,
+ conditional_t<bidirectional_range<_Base>, bidirectional_iterator_tag, forward_iterator_tag>>;
+ using value_type = decltype(views::take(subrange(__current_, __end_), __n_));
+ using difference_type = range_difference_t<_Base>;
+
+ _LIBCPP_HIDE_FROM_ABI __iterator() = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
+ requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>> &&
+ convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
+ : __current_(std::move(__i.__current_)),
+ __end_(std::move(__i.__end_)),
+ __n_(__i.__n_),
+ __missing_(__i.__missing_) {}
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() const { return __current_; }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const {
+ _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Dereferencing past-the-end chunk_view iterator");
+ return views::take(subrange(__current_, __end_), __n_);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
+ _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Incrementing past-the-end chunk_view iterator");
+ __missing_ = ranges::advance(__current_, __n_, __end_);
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int) {
+ auto __tmp = *this;
+ ++*this;
+ return __tmp;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--()
+ requires bidirectional_range<_Base>
+ {
+ ranges::advance(__current_, __missing_ - __n_);
+ __missing_ = 0;
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
+ requires bidirectional_range<_Base>
+ {
+ auto __tmp = *this;
+ --*this;
+ return __tmp;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator+=(const difference_type __offset)
+ requires random_access_range<_Base>
+ {
+ if (__offset > 0) {
+ ranges::advance(__current_, __n_ * (__offset - 1));
+ __missing_ = ranges::advance(__current_, __n_, __end_);
+ } else if (__offset < 0) {
+ ranges::advance(__current_, __n_ * __offset + __missing_);
+ __missing_ = 0;
+ }
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(const difference_type __offset)
+ requires random_access_range<_Base>
+ {
+ return *this += -__offset;
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr value_type operator[](const difference_type __offset) const
+ requires random_access_range<_Base>
+ {
+ return *(*this + __offset);
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+ operator==(const __iterator& __x, const __iterator& __y) {
+ return __x.__current_ == __y.__current_;
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+ operator==(const __iterator& __x, default_sentinel_t) {
+ return __x.__current_ == __x.__end_;
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+ operator==(const __iterator& __x, const __iterator& __y)
+ requires random_access_range<_Base>
+ {
+ return __x.__current_ < __y.__current_;
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+ operator>(const __iterator& __x, const __iterator& __y)
+ requires random_access_range<_Base>
+ {
+ return __y < __x;
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+ operator<=(const __iterator& __x, const __iterator& __y)
+ requires random_access_range<_Base>
+ {
+ return !(__y < __x);
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+ operator>=(const __iterator& __x, const __iterator& __y)
+ requires random_access_range<_Base>
+ {
+ return !(__x < __y);
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI friend constexpr auto
+ operator<=>(const __iterator& __x, const __iterator& __y)
+ requires random_access_range<_Base> && three_way_comparable<iterator_t<_Base>>
+ {
+ return __x.__current_ <=> __y.__current_;
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+ operator+(const __iterator& __x, const difference_type __y)
+ requires random_access_range<_Base>
+ {
+ return __iterator{__x} += __y;
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+ operator+(const difference_type __x, const __iterator& __y)
+ requires random_access_range<_Base>
+ {
+ return __y + __x;
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator
+ operator-(const __iterator& __x, difference_type __y)
+ requires random_access_range<_Base>
+ {
+ return __iterator{__x} -= __y;
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+ operator-(const __iterator& __x, const __iterator& __y)
+ requires sized_sentinel_for<iterator_t<_Base>, iterator_t<_Base>>
+ {
+ return (__x.__current_ - __y.__current_ + __x.__missing_ - __y.__missing_) / __x.n_;
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+ operator-(const default_sentinel_t, const __iterator& __x)
+ requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+ {
+ return __div_ceil(__x.__end_ - __x.__current_, __x.__n_);
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+ operator-(const __iterator& __x, const default_sentinel_t __y)
+ requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
+ {
+ return -(__y - __x);
+ }
+};
+
+namespace views {
+namespace __chunk {
+struct __fn {
+ template <class _Range, convertible_to<range_difference_t<_Range>> _Np>
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto
+ operator()(_Range&& __range,
+ _Np&& __n) const noexcept(noexcept(chunk_view(std::forward<_Range>(__range), std::forward<_Np>(__n))))
+ -> decltype(chunk_view(std::forward<_Range>(__range), std::forward<_Np>(__n))) {
+ return chunk_view(std::forward<_Range>(__range), std::forward<_Np>(__n));
+ }
+
+ template <class _Np>
+ requires constructible_from<decay_t<_Np>, _Np>
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto
+ operator()(_Np&& __n) const noexcept(is_nothrow_constructible_v<decay_t<_Np>, _Np>) {
+ return __range_adaptor_closure_t(std::__bind_back(*this, std::forward<_Np>(__n)));
+ }
+};
+} // namespace __chunk
+
+inline namespace __cpo {
+inline constexpr auto chunk = __chunk::__fn{};
+} // namespace __cpo
+} // namespace views
+
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___RANGES_CHUNK_VIEW_H
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 167d2137eaf454..1a63cef09dfb6b 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -380,6 +380,7 @@ namespace std {
#include <__ranges/all.h>
#include <__ranges/as_rvalue_view.h>
#include <__ranges/chunk_by_view.h>
+#include <__ranges/chunk_view.h>
#include <__ranges/common_view.h>
#include <__ranges/concepts.h>
#include <__ranges/counted.h>
>From 060cafd65ff6f9bce32ae3f5a26892ecb816ed88 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Tue, 19 Mar 2024 10:52:06 -0700
Subject: [PATCH 2/3] [libc++][ranges] add missing headers to 'chunk_view.h'
---
libcxx/include/__ranges/chunk_view.h | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index e3ffdd376207e1..817a41025bf203 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -10,15 +10,18 @@
#define _LIBCPP___RANGES_CHUNK_VIEW_H
#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
#include <__config>
#include <__functional/bind_back.h>
#include <__iterator/advance.h>
#include <__iterator/concepts.h>
#include <__iterator/default_sentinel.h>
#include <__iterator/distance.h>
+#include <__iterator/iterator_traits.h>
#include <__ranges/access.h>
#include <__ranges/all.h>
#include <__ranges/concepts.h>
+#include <__ranges/enable_borrowed_range.h>
#include <__ranges/subrange.h>
#include <__ranges/take_view.h>
#include <__ranges/view_interface.h>
@@ -324,17 +327,16 @@ namespace views {
namespace __chunk {
struct __fn {
template <class _Range, convertible_to<range_difference_t<_Range>> _Np>
- _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto
- operator()(_Range&& __range,
- _Np&& __n) const noexcept(noexcept(chunk_view(std::forward<_Range>(__range), std::forward<_Np>(__n))))
- -> decltype(chunk_view(std::forward<_Range>(__range), std::forward<_Np>(__n))) {
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range, _Np&& __n) const
+ noexcept(noexcept(chunk_view(std::forward<_Range>(__range), std::forward<_Np>(__n))))
+ -> decltype(chunk_view(std::forward<_Range>(__range), std::forward<_Np>(__n))) {
return chunk_view(std::forward<_Range>(__range), std::forward<_Np>(__n));
}
template <class _Np>
requires constructible_from<decay_t<_Np>, _Np>
- _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto
- operator()(_Np&& __n) const noexcept(is_nothrow_constructible_v<decay_t<_Np>, _Np>) {
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Np&& __n) const
+ noexcept(is_nothrow_constructible_v<decay_t<_Np>, _Np>) {
return __range_adaptor_closure_t(std::__bind_back(*this, std::forward<_Np>(__n)));
}
};
>From 5fed8c6215d2b31fd5666d92dd0f200e5a1cc4e5 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Tue, 19 Mar 2024 12:44:14 -0700
Subject: [PATCH 3/3] [libc++][ranges] implement 'ranges::chunk_view' for
'forward_range'
---
libcxx/docs/Status/Cxx23Papers.csv | 2 +-
libcxx/docs/Status/RangesViews.csv | 2 +-
libcxx/include/__ranges/chunk_view.h | 3 +-
.../range.chunk/adaptor.nodiscard.verify.cpp | 24 +++
.../range.chunk/adaptor.pass.cpp | 192 ++++++++++++++++++
.../range.adaptors/range.chunk/base.pass.cpp | 98 +++++++++
.../range.adaptors/range.chunk/ctad.pass.cpp | 62 ++++++
7 files changed, 380 insertions(+), 3 deletions(-)
create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.chunk/adaptor.nodiscard.verify.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.pass.cpp
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 80547c5c1f3f57..cef23388994118 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -48,7 +48,7 @@
"`P2387R3 <https://wg21.link/P2387R3>`__","LWG","Pipe support for user-defined range adaptors","February 2022","","","|ranges|"
"`P2440R1 <https://wg21.link/P2440R1>`__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","","","|ranges|"
"`P2441R2 <https://wg21.link/P2441R2>`__","LWG","``views::join_with``","February 2022","|In Progress|","","|ranges|"
-"`P2442R1 <https://wg21.link/P2442R1>`__","LWG","Windowing range adaptors: ``views::chunk`` and ``views::slide``","February 2022","","","|ranges|"
+"`P2442R1 <https://wg21.link/P2442R1>`__","LWG","Windowing range adaptors: ``views::chunk`` and ``views::slide``","February 2022",""|In progress|,"","|ranges|"
"`P2443R1 <https://wg21.link/P2443R1>`__","LWG","``views::chunk_by``","February 2022","|Complete|","18.0","|ranges|"
"","","","","","",""
"`P0009R18 <https://wg21.link/P0009R18>`__","LWG","mdspan: A Non-Owning Multidimensional Array Reference","July 2022","|Complete|","18.0"
diff --git a/libcxx/docs/Status/RangesViews.csv b/libcxx/docs/Status/RangesViews.csv
index f141656eb131a2..17ea6290560103 100644
--- a/libcxx/docs/Status/RangesViews.csv
+++ b/libcxx/docs/Status/RangesViews.csv
@@ -30,7 +30,7 @@ C++23,`adjacent <https://wg21.link/P2321R2>`_,Hui Xie,No patch yet,Not started
C++23,`adjacent_transform <https://wg21.link/P2321R2>`_,Hui Xie,No patch yet,Not started
C++23,`join_with <https://wg21.link/P2441R2>`_,Jakub Mazurkiewicz,`65536 <https://github.com/llvm/llvm-project/pull/65536>`_,In progress
C++23,`slide <https://wg21.link/P2442R1>`_,Will Hawkins,`67146 <https://github.com/llvm/llvm-project/pull/67146>`_,In Progress
-C++23,`chunk <https://wg21.link/P2442R1>`_,Unassigned,No patch yet,Not started
+C++23,`chunk <https://wg21.link/P2442R1>`_,Xiaoyang Liu,`85741 <https://github.com/llvm/llvm-project/pull/85741>`_,In Progress
C++23,`chunk_by <https://wg21.link/P2443R1>`_,Jakub Mazurkiewicz,`D144767 <https://llvm.org/D144767>`_,✅
C++23,`as_const <https://wg21.link/P2278R4>`_,Unassigned,No patch yet,Not started
C++23,`as_rvalue <https://wg21.link/P2446R2>`_,Nikolas Klauser,`D137637 <https://llvm.org/D137637>`_,✅
diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h
index 817a41025bf203..5ae27d6a4be5e8 100644
--- a/libcxx/include/__ranges/chunk_view.h
+++ b/libcxx/include/__ranges/chunk_view.h
@@ -153,7 +153,8 @@ class chunk_view<_View>::__iterator {
_LIBCPP_HIDE_FROM_ABI constexpr __iterator(
_Parent* __parent, iterator_t<_Base> __current, range_difference_t<_Base> __missing = 0)
: __current_(std::move(__current)),
- __end_(ranges::end(__parent->__base_), __n_(__parent->__n_)),
+ __end_(ranges::end(__parent->__base_)),
+ __n_(__parent->__n_),
__missing_(__missing) {}
public:
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/adaptor.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/adaptor.nodiscard.verify.cpp
new file mode 100644
index 00000000000000..fd61fc09960cfc
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/adaptor.nodiscard.verify.cpp
@@ -0,0 +1,24 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <ranges>
+
+// Test the libc++ extension that std::views::chunk is marked as [[nodiscard]].
+
+#include <ranges>
+
+void test() {
+ int range[] = {1, 2, 3, 0, 1, 2};
+
+ std::views::chunk(2); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::views::chunk(range, 2); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ range | std::views::chunk(2); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::views::all | std::views::chunk(2); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
new file mode 100644
index 00000000000000..7254e9a4ca2809
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp
@@ -0,0 +1,192 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <ranges>
+
+// std::views::chunk
+
+#include <algorithm>
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <type_traits>
+#include <iostream>
+
+#include "test_iterators.h"
+#include "test_range.h"
+
+struct Range : std::ranges::view_base {
+ using Iterator = forward_iterator<int*>;
+ using Sentinel = sentinel_wrapper<Iterator>;
+ constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) {}
+ constexpr Iterator begin() const { return Iterator(begin_); }
+ constexpr Sentinel end() const { return Sentinel(Iterator(end_)); }
+
+private:
+ int* begin_;
+ int* end_;
+};
+
+template <typename View>
+constexpr void compare_views(View v, std::initializer_list<std::initializer_list<int>> list) {
+ auto b1 = v.begin();
+ auto e1 = v.end();
+ auto b2 = list.begin();
+ auto e2 = list.end();
+ for (; b1 != e1 && b2 != e2; ++b1, ++b2) {
+ const bool eq = std::ranges::equal(*b1, *b2, [](const auto a, const auto b) {
+ assert(a == b);
+ return true;
+ });
+ assert(eq);
+ }
+ assert(b1 == e1);
+ assert(b2 == e2);
+}
+
+template <class T>
+constexpr const T&& as_const_rvalue(T&& t) {
+ return static_cast<T const&&>(t);
+}
+
+constexpr bool test() {
+ constexpr int N = 10;
+ int buf[N] = {1, 1, 4, 5, 1, 4, 1, 9, 1, 9};
+
+ // Test range adaptor object
+ {
+ using RangeAdaptorObject = decltype(std::views::chunk);
+ static_assert(std::is_const_v<RangeAdaptorObject>);
+
+ // The type of a customization point object, ignoring cv-qualifiers, shall model semiregular
+ static_assert(std::semiregular<std::remove_const_t<RangeAdaptorObject>>);
+ }
+
+ // Test `views::chunk(n)(range)`
+ {
+ using Result = std::ranges::chunk_view<Range>;
+ const Range range(buf, buf + N);
+
+ {
+ // `views::chunk(n)` - &&
+ std::same_as<Result> decltype(auto) result = std::views::chunk(3)(range);
+ compare_views(result, {{1, 1, 4}, {5, 1, 4}, {1, 9, 1}, {9}});
+ }
+ {
+ // `views::chunk(n)` - const&&
+ std::same_as<Result> decltype(auto) result = as_const_rvalue(std::views::chunk(3))(range);
+ compare_views(result, {{1, 1, 4}, {5, 1, 4}, {1, 9, 1}, {9}});
+ }
+ {
+ // `views::chunk(n)` - &
+ auto partial = std::views::chunk(3);
+ std::same_as<Result> decltype(auto) result = partial(range);
+ compare_views(result, {{1, 1, 4}, {5, 1, 4}, {1, 9, 1}, {9}});
+ }
+ {
+ // `views::chunk(n)` - const&
+ const auto partial = std::views::chunk(3);
+ std::same_as<Result> decltype(auto) result = partial(range);
+ compare_views(result, {{1, 1, 4}, {5, 1, 4}, {1, 9, 1}, {9}});
+ }
+ }
+
+ // Test `range | views::chunk(n)`
+ {
+ using Result = std::ranges::chunk_view<Range>;
+ const Range range(buf, buf + N);
+
+ {
+ // `views::chunk(n)` - &&
+ std::same_as<Result> decltype(auto) result = range | std::views::chunk(3);
+ compare_views(result, {{1, 1, 4}, {5, 1, 4}, {1, 9, 1}, {9}});
+ }
+ {
+ // `views::chunk(n)` - const&&
+ std::same_as<Result> decltype(auto) result = range | as_const_rvalue(std::views::chunk(3));
+ compare_views(result, {{1, 1, 4}, {5, 1, 4}, {1, 9, 1}, {9}});
+ }
+ {
+ // `views::chunk(n)` - &
+ auto partial = std::views::chunk(3);
+ std::same_as<Result> decltype(auto) result = range | partial;
+ compare_views(result, {{1, 1, 4}, {5, 1, 4}, {1, 9, 1}, {9}});
+ }
+ {
+ // `views::chunk(n)` - const&
+ const auto partial = std::views::chunk(3);
+ std::same_as<Result> decltype(auto) result = range | partial;
+ compare_views(result, {{1, 1, 4}, {5, 1, 4}, {1, 9, 1}, {9}});
+ }
+ }
+
+ // Test `views::chunk(range, n)` range adaptor object
+ {
+ using Result = std::ranges::chunk_view<Range>;
+ const Range range(buf, buf + N);
+
+ {
+ // `views::chunk` - &&
+ auto range_adaptor = std::views::chunk;
+ std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, 3);
+ compare_views(result, {{1, 1, 4}, {5, 1, 4}, {1, 9, 1}, {9}});
+ }
+ {
+ // `views::chunk` - const&&
+ auto const range_adaptor = std::views::chunk;
+ std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, 3);
+ compare_views(result, {{1, 1, 4}, {5, 1, 4}, {1, 9, 1}, {9}});
+ }
+ {
+ // `views::chunk` - &
+ auto range_adaptor = std::views::chunk;
+ std::same_as<Result> decltype(auto) result = range_adaptor(range, 3);
+ compare_views(result, {{1, 1, 4}, {5, 1, 4}, {1, 9, 1}, {9}});
+ }
+ {
+ // `views::chunk` - const&
+ auto const range_adaptor = std::views::chunk;
+ std::same_as<Result> decltype(auto) result = range_adaptor(range, 3);
+ compare_views(result, {{1, 1, 4}, {5, 1, 4}, {1, 9, 1}, {9}});
+ }
+ }
+
+ // Test that it's possible to call `std::views::chunk` with any single argument as long as the resulting closure is
+ // never invoked. There is no good use case for it, but it's valid.
+ {
+ struct X {};
+ [[maybe_unused]] auto partial = std::views::chunk(X{});
+ }
+
+ // Test SFINAE friendliness
+ {
+ struct NotAView {};
+
+ static_assert(!CanBePiped<Range, decltype(std::views::chunk)>);
+ static_assert(CanBePiped<Range, decltype(std::views::chunk(N))>);
+ static_assert(!CanBePiped<NotAView, decltype(std::views::chunk(N))>);
+ static_assert(!CanBePiped<std::initializer_list<int>, decltype(std::views::chunk(N))>);
+
+ static_assert(!std::is_invocable_v<decltype(std::views::chunk)>);
+ static_assert(std::is_invocable_v<decltype(std::views::chunk), Range, std::ranges::range_difference_t<Range>>);
+ static_assert(!std::is_invocable_v<decltype(std::views::chunk), NotAView, std::ranges::range_difference_t<Range>>);
+ }
+
+ { static_assert(std::is_same_v<decltype(std::ranges::views::chunk), decltype(std::views::chunk)>); }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
new file mode 100644
index 00000000000000..404f019207df55
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp
@@ -0,0 +1,98 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <ranges>
+
+// constexpr View base() const& requires copy_constructible<View>;
+// constexpr View base() &&;
+
+#include <ranges>
+
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+struct Range : std::ranges::view_base {
+ constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) {}
+ constexpr Range(Range const& other) : was_copy_initialized(true), begin_(other.begin_), end_(other.end_) {}
+ constexpr Range(Range&& other) : was_move_initialized(true), begin_(other.begin_), end_(other.end_) {}
+ Range& operator=(Range const&) = default;
+ Range& operator=(Range&&) = default;
+ constexpr int* begin() const { return begin_; }
+ constexpr int* end() const { return end_; }
+
+ bool was_copy_initialized = false;
+ bool was_move_initialized = false;
+
+private:
+ int* begin_;
+ int* end_;
+};
+
+static_assert(std::ranges::view<Range>);
+static_assert(std::ranges::forward_range<Range>);
+
+struct NonCopyableRange : std::ranges::view_base {
+ explicit NonCopyableRange(int*, int*);
+ NonCopyableRange(NonCopyableRange const&) = delete;
+ NonCopyableRange(NonCopyableRange&&) = default;
+ NonCopyableRange& operator=(NonCopyableRange const&) = default;
+ NonCopyableRange& operator=(NonCopyableRange&&) = default;
+ int* begin() const;
+ int* end() const;
+};
+
+static_assert(!std::copy_constructible<NonCopyableRange>);
+
+template <typename T>
+concept CanCallBaseOn = requires(T&& t) { std::forward<T>(t).base(); };
+
+constexpr bool test() {
+ constexpr int N = 4;
+ int buf[N] = {1, 2, 3, 4};
+
+ // Check the const& overload
+ {
+ Range range(buf, buf + N);
+ const std::ranges::chunk_view<Range> view(range, N);
+ std::same_as<Range> decltype(auto) result = view.base();
+ assert(result.was_copy_initialized);
+ assert(result.begin() == buf);
+ assert(result.end() == buf + N);
+ }
+
+ // Check the && overload
+ {
+ Range range(buf, buf + N);
+ std::ranges::chunk_view<Range> view(range, N);
+ std::same_as<Range> decltype(auto) result = std::move(view).base();
+ assert(result.was_move_initialized);
+ assert(result.begin() == buf);
+ assert(result.end() == buf + N);
+ }
+
+ // Ensure the const& overloads are not considered when the base is not copy-constructible
+ {
+ static_assert(!CanCallBaseOn<const std::ranges::chunk_view<NonCopyableRange>&>);
+ static_assert(!CanCallBaseOn<std::ranges::chunk_view<NonCopyableRange>&>);
+ static_assert(!CanCallBaseOn<const std::ranges::chunk_view<NonCopyableRange>&&>);
+ static_assert(CanCallBaseOn<std::ranges::chunk_view<NonCopyableRange>&&>);
+ static_assert(CanCallBaseOn<std::ranges::chunk_view<NonCopyableRange>>);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.pass.cpp
new file mode 100644
index 00000000000000..e96717462df5e1
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.pass.cpp
@@ -0,0 +1,62 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <ranges>
+
+// template <class _View>
+// chunk_view(_View&&, range_difference_t<_View>) -> chunk_view<views::all_t<_View>>;
+
+#include <ranges>
+
+#include <cassert>
+#include <type_traits>
+
+#include "test_iterators.h"
+
+struct View : std::ranges::view_base {
+ View() = default;
+ forward_iterator<int*> begin() const;
+ sentinel_wrapper<forward_iterator<int*>> end() const;
+};
+static_assert(std::ranges::view<View>);
+
+struct Range {
+ Range() = default;
+ forward_iterator<int*> begin() const;
+ sentinel_wrapper<forward_iterator<int*>> end() const;
+};
+static_assert(std::ranges::range<Range>);
+static_assert(!std::ranges::view<Range>);
+
+constexpr bool test() {
+ {
+ View v;
+ std::ranges::chunk_view view(v, 42);
+ static_assert(std::is_same_v<decltype(view), std::ranges::chunk_view<View>>);
+ }
+ {
+ Range r;
+ std::ranges::chunk_view view(r, 42);
+ static_assert(std::is_same_v<decltype(view), std::ranges::chunk_view<std::ranges::ref_view<Range>>>);
+ }
+ {
+ std::ranges::chunk_view view(Range{}, 42);
+ static_assert(std::is_same_v<decltype(view), std::ranges::chunk_view<std::ranges::owning_view<Range>>>);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
More information about the libcxx-commits
mailing list