diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index 756bdf71f8b22..49672e5ccf70a 100644 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -370,7 +370,7 @@ Status ---------------------------------------------------------- ----------------- ``__cpp_lib_ranges_as_rvalue`` ``202207L`` ---------------------------------------------------------- ----------------- - ``__cpp_lib_ranges_chunk`` *unimplemented* + ``__cpp_lib_ranges_chunk`` ``202202L`` ---------------------------------------------------------- ----------------- ``__cpp_lib_ranges_chunk_by`` ``202202L`` ---------------------------------------------------------- ----------------- diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst index 9f1e3d570f254..78796f15f03f9 100644 --- a/libcxx/docs/ReleaseNotes/22.rst +++ b/libcxx/docs/ReleaseNotes/22.rst @@ -49,6 +49,7 @@ Implemented Papers - P2835R7: Expose ``std::atomic_ref``'s object address (`Github `__) - P2944R3: Comparisons for ``reference_wrapper`` (`Github `__) - P3168R2: Give ``std::optional`` Range Support (`Github `__) +- P2442R1: Add ``std::views::chunk`` (`Github `__) Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv index b655384bad7f2..7aa2f07d9bb57 100644 --- a/libcxx/docs/Status/Cxx23Papers.csv +++ b/libcxx/docs/Status/Cxx23Papers.csv @@ -48,7 +48,7 @@ "`P2387R3 `__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 `__","" "`P2440R1 `__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Partial|","","`#105184 `__","Only ``ranges::iota`` is implemented." "`P2441R2 `__","``views::join_with``","2022-02 (Virtual)","|Complete|","21","`#105185 `__","" -"`P2442R1 `__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","","","`#105187 `__","" +"`P2442R1 `__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","|Partial|","22","`#105187 `__","Only ``views::chunk`` is implemented." "`P2443R1 `__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18","`#105188 `__","" "","","","","","","" "`P0009R18 `__","mdspan: A Non-Owning Multidimensional Array Reference","2022-07 (Virtual)","|Complete|","18","`#105189 `__","" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index cbcd764e67d93..afe8391f1a5d5 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -703,6 +703,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/__cxx03/module.modulemap b/libcxx/include/__cxx03/module.modulemap index 34a2d0f25fc45..b7eee575090ce 100644 --- a/libcxx/include/__cxx03/module.modulemap +++ b/libcxx/include/__cxx03/module.modulemap @@ -1701,6 +1701,7 @@ module cxx03_std_private_ranges_all [system] { } module cxx03_std_private_ranges_as_rvalue_view [system] { header "__ranges/as_rvalue_view.h" } module cxx03_std_private_ranges_chunk_by_view [system] { header "__ranges/chunk_by_view.h" } +module cxx03_std_private_ranges_chunk_view [system] { header "__ranges/chunk_view.h" } module cxx03_std_private_ranges_common_view [system] { header "__ranges/common_view.h" } module cxx03_std_private_ranges_concepts [system] { header "__ranges/concepts.h" diff --git a/libcxx/include/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h new file mode 100644 index 0000000000000..ae37c9784e081 --- /dev/null +++ b/libcxx/include/__ranges/chunk_view.h @@ -0,0 +1,544 @@ +// -*- 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 <__algorithm/ranges_min.h> +#include <__assert> +#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/iter_move.h> +#include <__iterator/iter_swap.h> +#include <__iterator/iterator_traits.h> +#include <__memory/addressof.h> +#include <__ranges/access.h> +#include <__ranges/all.h> +#include <__ranges/concepts.h> +#include <__ranges/enable_borrowed_range.h> +#include <__ranges/non_propagating_cache.h> +#include <__ranges/range_adaptor.h> +#include <__ranges/take_view.h> +#include <__ranges/view_interface.h> +#include <__type_traits/conditional.h> +#include <__type_traits/decay.h> +#include <__type_traits/is_nothrow_constructible.h> +#include <__type_traits/make_unsigned.h> +#include <__utility/forward.h> +#include <__utility/move.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 +inline _LIBCPP_HIDE_FROM_ABI constexpr auto __div_ceil(_Integral __num, _Integral __denom) { + _Integral __r = __num / __denom; + if (__num % __denom) + ++__r; + return __r; +} + +template + requires input_range<_View> +class _LIBCPP_ABI_LLVM18_NO_UNIQUE_ADDRESS chunk_view : public view_interface> { + _LIBCPP_NO_UNIQUE_ADDRESS _View __base_; + _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_; + _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __remainder_; + _LIBCPP_NO_UNIQUE_ADDRESS __non_propagating_cache> __current_; + + class __outer_iterator; + class __inner_iterator; + +public: + _LIBCPP_HIDE_FROM_ABI constexpr explicit chunk_view(_View __base, range_difference_t<_View> __n) + : __base_(std::move(__base)), __n_(__n), __remainder_(0) { + _LIBCPP_ASSERT_PEDANTIC(__n > 0, "Trying to construct a chunk_view with chunk size <= 0"); + } + + _LIBCPP_HIDE_FROM_ABI constexpr _View base() const& + requires std::copy_constructible<_View> + { + return __base_; + } + + _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); } + + _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator begin() { + __current_.__emplace(ranges::begin(__base_)); + __remainder_ = __n_; + return __outer_iterator(*this); + } + + _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept { return std::default_sentinel; } + + _LIBCPP_HIDE_FROM_ABI constexpr auto size() + requires sized_range<_View> + { + return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_)); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto size() const + requires sized_range + { + return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_)); + } +}; + +template + requires input_range<_View> +class chunk_view<_View>::__outer_iterator { + friend chunk_view; + + chunk_view* __parent_; + + _LIBCPP_HIDE_FROM_ABI constexpr explicit __outer_iterator(chunk_view& __parent) + : __parent_(std::addressof(__parent)) {} + +public: + class value_type; + using iterator_concept = input_iterator_tag; + using difference_type = range_difference_t<_View>; + + _LIBCPP_HIDE_FROM_ABI __outer_iterator(__outer_iterator&&) = default; + + _LIBCPP_HIDE_FROM_ABI __outer_iterator& operator=(__outer_iterator&&) = default; + + _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { + _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator."); + return value_type(*__parent_); + } + + _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator& operator++() { + ranges::advance(*__parent_->__current_, __parent_->__remainder_, ranges::end(__parent_->__base_)); + __parent_->__remainder_ = __parent_->__n_; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __outer_iterator& __i, default_sentinel_t) { + return *__i.__parent_->__current_ == ranges::end(__i.__parent_->__base_) && __i.__parent_->__remainder_ != 0; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(default_sentinel_t, const __outer_iterator& __i) + requires sized_sentinel_for, iterator_t<_View>> + { + const auto __dist = ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_; + if (__dist < __i.__parent_->__remainder_) + return __dist == 0 ? 0 : 1; + return ranges::__div_ceil(__dist - __i.__parent_->__remainder_, __i.__parent_->__n_) + 1; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __outer_iterator& __i, default_sentinel_t __s) + requires sized_sentinel_for, iterator_t<_View>> + { + return -(__s - __i); + } +}; + +template + requires input_range<_View> +class chunk_view<_View>::__outer_iterator::value_type : public view_interface { + friend __outer_iterator; + + chunk_view* __parent_; + + _LIBCPP_HIDE_FROM_ABI constexpr explicit value_type(chunk_view& __parent) : __parent_(std::addressof(__parent)) {} + +public: + _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator begin() const noexcept { return __inner_iterator(*__parent_); } + + _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept { return default_sentinel; } + + _LIBCPP_HIDE_FROM_ABI constexpr auto size() const + requires sized_sentinel_for, iterator_t<_View>> + { + return std::__to_unsigned_like( + ranges::min(__parent_->__remainder_, ranges::end(__parent_->__base_) - *__parent_->__current_)); + } +}; + +template + requires input_range<_View> +class chunk_view<_View>::__inner_iterator { + friend chunk_view; + + chunk_view* __parent_; + + _LIBCPP_HIDE_FROM_ABI constexpr explicit __inner_iterator(chunk_view& __parent) + : __parent_(std::addressof(__parent)) {} + +public: + using iterator_concept = input_iterator_tag; + using difference_type = range_difference_t<_View>; + using value_type = range_value_t<_View>; + + _LIBCPP_HIDE_FROM_ABI __inner_iterator(__inner_iterator&&) = default; + + _LIBCPP_HIDE_FROM_ABI __inner_iterator& operator=(__inner_iterator&&) = default; + + _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_View> base() const& { return *__parent_->__current_; } + + _LIBCPP_HIDE_FROM_ABI constexpr range_reference_t<_View> operator*() const { + _LIBCPP_ASSERT_PEDANTIC(*this != default_sentinel, "Trying to dereference past-the-end chunk_view iterator"); + return **__parent_->__current_; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator& operator++() { + ++*__parent_->__current_; + if (*__parent_->__current_ == ranges::end(__parent_->__base_)) + __parent_->__remainder_ = 0; + else + --__parent_->__remainder_; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __inner_iterator& __i, default_sentinel_t) { + return __i.__parent_->__remainder_ == 0; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(default_sentinel_t, const __inner_iterator& __i) + requires sized_sentinel_for, iterator_t<_View>> + { + return ranges::min(__i.__parent_->__remainder_, ranges::end(__i.__parent_->__base_) - *__i.__parent_->__current_); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __inner_iterator& __i, default_sentinel_t __s) + requires sized_sentinel_for, iterator_t<_View>> + { + return -(__s - __i); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr auto + iter_move(const __inner_iterator& __i) noexcept(noexcept(ranges::iter_move(*__i.__parent_->__current_))) { + return ranges::iter_move(*__i.__parent_->__current_); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr void + iter_swap(const __inner_iterator& __x, const __inner_iterator& __y) noexcept( + noexcept((ranges::iter_swap(*__x.__parent_->__current_, *__y.__parent_->__current_)))) + requires indirectly_swappable> + { + return ranges::iter_swap(*__x.__parent_->__current_, *__y.__parent_->__current_); + } +}; + +template + requires forward_range<_View> +class _LIBCPP_ABI_LLVM18_NO_UNIQUE_ADDRESS chunk_view<_View> : public view_interface> { + _LIBCPP_NO_UNIQUE_ADDRESS _View __base_; + _LIBCPP_NO_UNIQUE_ADDRESS range_difference_t<_View> __n_; + + template + 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_PEDANTIC(__n > 0, "Trying to construct a chunk_view with chunk size <= 0"); + } + + _LIBCPP_HIDE_FROM_ABI constexpr _View base() const& + requires copy_constructible<_View> + { + return __base_; + } + + _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); } + + _LIBCPP_HIDE_FROM_ABI constexpr auto begin() + requires(!__simple_view<_View>) + { + return __iterator(this, ranges::begin(__base_)); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const + requires forward_range + { + return __iterator(this, ranges::begin(__base_)); + } + + _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(this, ranges::end(__base_), __missing); + } else if constexpr (common_range<_View> && !bidirectional_range<_View>) + return __iterator(this, ranges::end(__base_)); + else + return default_sentinel; + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() const + requires forward_range + { + if constexpr (common_range && sized_range) { + auto __missing = (__n_ - ranges::distance(__base_) % __n_) % __n_; + return __iterator(this, ranges::end(__base_), __missing); + } else if constexpr (common_range && !bidirectional_range) + return __iterator(this, ranges::end(__base_)); + else + return default_sentinel; + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto size() + requires sized_range<_View> + { + return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_)); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto size() const + requires sized_range + { + return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_)); + } +}; + +template + requires forward_range<_View> +template +class chunk_view<_View>::__iterator { + friend chunk_view; + + using _Parent _LIBCPP_NODEBUG = __maybe_const<_Const, chunk_view>; + using _Base _LIBCPP_NODEBUG = __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_(__current), __end_(ranges::end(__parent->__base_)), __n_(__parent->__n_), __missing_(__missing) {} + + static consteval auto __get_iterator_concept() { + if constexpr (random_access_range<_Base>) + return random_access_iterator_tag{}; + else if constexpr (bidirectional_range<_Base>) + return bidirectional_iterator_tag{}; + else + return forward_iterator_tag{}; + } + +public: + using iterator_category = input_iterator_tag; + using iterator_concept = decltype(__iterator::__get_iterator_concept()); + 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 __i) + requires _Const && convertible_to, iterator_t<_Base>> && + convertible_to, sentinel_t<_Base>> + : __current_(std::move(__i.__current_)), + __end_(std::move(__i.__end_)), + __n_(__i.__n_), + __missing_(__i.__missing_) {} + + _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() const { return __current_; } + + _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { + _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to dereference past-the-end chunk_view iterator"); + return views::take(subrange(__current_, __end_), __n_); + } + + _LIBCPP_HIDE_FROM_ABI constexpr value_type operator[](difference_type __pos) const + requires random_access_range<_Base> + { + return *(*this + __pos); + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() { + _LIBCPP_ASSERT_PEDANTIC(__current_ != __end_, "Trying to advance 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) { + auto __tmp = *this; + --*this; + return __tmp; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator+=(difference_type __x) + requires random_access_range<_Base> + { + if (__x > 0) { + _LIBCPP_ASSERT_PEDANTIC(ranges::distance(__current_, __end_) > __n_ * (__x - 1), + "Trying to advance chunk_view iterator out of range"); + ranges::advance(__current_, __n_ * (__x - 1)); + __missing_ = ranges::advance(__current_, __n_, __end_); + } else if (__x < 0) { + ranges::advance(__current_, __n_ * __x + __missing_); + __missing_ = 0; + } + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator-=(difference_type __x) + requires random_access_range<_Base> + { + return *this += -__x; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) { + return __x.__current_ == __y.__current_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, default_sentinel_t) { + return __x.__current_ == __x.__end_; + } + + _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_HIDE_FROM_ABI friend constexpr bool operator>(const __iterator& __x, const __iterator& __y) + requires random_access_range<_Base> + { + return __y < __x; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(const __iterator& __x, const __iterator& __y) + requires random_access_range<_Base> + { + return !(__y < __x); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(const __iterator& __x, const __iterator& __y) + requires random_access_range<_Base> + { + return !(__x < __y); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y) + requires random_access_range<_Base> && three_way_comparable> + { + return __x.__current_ <=> __y.__current_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __pos) + requires random_access_range<_Base> + { + auto __r = __i; + __r += __pos; + return __r; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __pos, const __iterator& __i) + requires random_access_range<_Base> + { + auto __r = __i; + __r += __pos; + return __r; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __pos) + requires random_access_range<_Base> + { + auto __r = __i; + __r -= __pos; + return __r; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __i, const __iterator& __j) + requires sized_sentinel_for, iterator_t<_Base>> + { + return (__i.__current_ - __j.__current_ + __i.__missing_ - __j.__missing_) / __i.__n_; + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(default_sentinel_t, const __iterator& __i) + requires sized_sentinel_for, iterator_t<_Base>> + { + return ranges::__div_ceil(__i.__end_ - __i.__current_, __i.__n_); + } + + _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __i, default_sentinel_t __s) + requires sized_sentinel_for, iterator_t<_Base>> + { + return -(__s - __i); + } +}; + +template +chunk_view(_Range&&, range_difference_t<_Range>) -> chunk_view>; + +template +inline constexpr bool enable_borrowed_range> = forward_range<_View> && enable_borrowed_range<_View>; + +namespace views { +namespace __chunk { +struct __fn { + template + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range, range_difference_t<_Range> __n) const + noexcept(noexcept(/**/ chunk_view(std::forward<_Range>(__range), std::forward>(__n)))) + -> decltype(/*--*/ chunk_view(std::forward<_Range>(__range), std::forward>(__n))) { + return /*-------------*/ chunk_view(std::forward<_Range>(__range), std::forward>(__n)); + } + + template + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_DifferenceType __n) const + noexcept(is_nothrow_constructible_v, _DifferenceType>) { + return __pipeable(std::__bind_back(*this, std::forward<_DifferenceType>(__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/module.modulemap.in b/libcxx/include/module.modulemap.in index 955a7cc3e364a..8d4123a1e6bb2 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1855,6 +1855,10 @@ module std [system] { header "__ranges/chunk_by_view.h" export std.functional.bind_back } + module chunk_view { + header "__ranges/chunk_view.h" + export std.functional.bind_back + } module common_view { header "__ranges/common_view.h" } module concepts { header "__ranges/concepts.h" } module container_compatible_range { header "__ranges/container_compatible_range.h" } diff --git a/libcxx/include/ranges b/libcxx/include/ranges index cfaa66a0831b3..8fb65f5f4cad2 100644 --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -367,6 +367,17 @@ namespace std::ranges { class chunk_by_view; // C++23 namespace views { inline constexpr unspecified chunk_by = unspecified; } // C++23 + + // [range.chunk] + template + requires input_range + class chunk_view; // C++23 + + template + requires forward_range + class chunk_view; // C++23 + + namespace views { inline constexpr unspecified chunk = unspecified; } // C++23 } namespace std { @@ -450,6 +461,7 @@ namespace std { # if _LIBCPP_STD_VER >= 23 # include <__ranges/as_rvalue_view.h> # include <__ranges/chunk_by_view.h> +# include <__ranges/chunk_view.h> # include <__ranges/from_range.h> # include <__ranges/join_with_view.h> # include <__ranges/repeat_view.h> diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc index cc7daa3cd1aec..8f571eaec2536 100644 --- a/libcxx/modules/std/ranges.inc +++ b/libcxx/modules/std/ranges.inc @@ -315,15 +315,17 @@ export namespace std { using std::ranges::views::adjacent_transform; using std::ranges::views::pairwise_transform; } // namespace views +#endif +#if _LIBCPP_STD_VER >= 23 using std::ranges::chunk_view; - using std::ranges::chunk_view; - namespace views { using std::ranges::views::chunk; } +#endif +#if 0 using std::ranges::slide_view; namespace views { 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 0000000000000..8993c54ea4565 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp @@ -0,0 +1,73 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// std::views::chunk + +#include + +#include +#include +#include +#include +#include + +#include "test_range.h" +#include "types.h" + +constexpr bool test() { + std::array array = {1, 1, 1, 2, 2, 2, 3, 3}; + std::span span = {array.data(), 8}; + + // Test `views::chunk(view, n)` + { + auto chunked = std::views::chunk(span, 2); + static_assert(std::same_as>>); + assert(std::ranges::equal(*chunked.begin(), std::array{1, 1})); + } + + // Test `views::chunk(input_view, n)` + { + auto input = exactly_input_view(span); + auto chunked = std::views::chunk(input, 3); + assert(std::ranges::equal(*chunked.begin(), std::array{1, 1, 1})); + } + + // Test `views::chunk(n)(range)` + { + auto adaptor = std::views::chunk(4); + auto chunked = adaptor(span); + static_assert(std::same_as>>); + assert(std::ranges::equal(*chunked.begin(), std::array{1, 1, 1, 2})); + } + + // Test `view | views::chunk` + { + auto chunked = span | std::views::chunk(5); + static_assert(std::same_as>>); + static_assert(std::ranges::random_access_range); + assert(std::ranges::equal(*chunked.begin(), std::array{1, 1, 1, 2, 2})); + } + + // Test `views::chunk | adaptor` + { + auto multi_adaptor = std::views::chunk(1) | std::views::join; + auto rejoined = span | multi_adaptor; + assert(std::ranges::equal(rejoined, span)); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} \ No newline at end of file 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 0000000000000..798349a7784d8 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp @@ -0,0 +1,41 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// std::views::chunk + +#include + +#include +#include +#include + +#include "test_range.h" + +constexpr bool test() { + std::array array = {1, 1, 1, 2, 2, 2, 3, 3}; + + // Test `chunk_view.base()` + { + auto view = array | std::views::chunk(3); + auto base = view.begin().base(); + assert(base == array.begin()); + base = view.end().base(); + assert(base == array.end()); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp new file mode 100644 index 0000000000000..60dd45aa519bc --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp @@ -0,0 +1,63 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// std::views::chunk + +#include + +#include +#include +#include + +#include "test_range.h" + +constexpr bool test() { + std::list full_list = {1, 1, 1, 2, 2, 2, 3, 3}; + std::list empty_list = {}; + + // Test `chunk_view.begin()` + { + auto view = full_list | std::views::chunk(3); + auto it = view.begin(); + assert(std::ranges::equal(*it, std::list{1, 1, 1})); + assert(std::ranges::equal(*++it, std::list{2, 2, 2})); + assert(std::ranges::equal(*++it, std::list{3, 3})); // The last chunk has only 2 elements. + assert(++it == view.end()); // Reaches end + + view = full_list | std::views::chunk(5); + it = view.begin(); + assert(std::ranges::equal(*it, std::list{1, 1, 1, 2, 2})); + assert(std::ranges::equal(*++it, std::list{2, 3, 3})); + } + + // Test `empty_chunk_view.begin()` + { + auto view = empty_list | std::views::chunk(3); + assert(view.size() == 0); + assert(view.begin() == view.end()); + } + + // Test `small_view_with_big_chunk.begin()` + { + auto view = full_list | std::views::chunk(314159); + assert(view.size() == 1); + assert(std::ranges::equal(*view.begin(), full_list)); + assert(++view.begin() == view.end()); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp new file mode 100644 index 0000000000000..55fe4ca976976 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp @@ -0,0 +1,48 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// std::views::chunk + +#include + +struct view : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +struct range { + int* begin() const; + int* end() const; +}; + +struct borrowed_range { + int* begin() const; + int* end() const; +}; + +template <> +inline constexpr bool std::ranges::enable_borrowed_range = true; + +void testCTAD() { + view v; + range r; + borrowed_range br; + + static_assert(std::same_as< decltype(std::ranges::chunk_view(v, 0)), std::ranges::chunk_view >); + static_assert(std::same_as< decltype(std::ranges::chunk_view(std::move(v), 0)), std::ranges::chunk_view >); + static_assert( + std::same_as< decltype(std::ranges::chunk_view(r, 0)), std::ranges::chunk_view> >); + static_assert(std::same_as< decltype(std::ranges::chunk_view(std::move(r), 0)), + std::ranges::chunk_view> >); + static_assert(std::same_as< decltype(std::ranges::chunk_view(br, 0)), + std::ranges::chunk_view> >); + static_assert(std::same_as< decltype(std::ranges::chunk_view(std::move(br), 0)), + std::ranges::chunk_view> >); +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp new file mode 100644 index 0000000000000..056e7bb57fa5b --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp @@ -0,0 +1,64 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// std::views::chunk + +#include + +#include +#include +#include +#include +#include + +#include "test_range.h" +#include "types.h" + +constexpr bool test() { + std::list list = {1, 1, 1, 2, 2, 2, 3, 3}; + std::forward_list forward_list = {1, 1, 1, 2, 2, 2, 3, 3}; + + // Test `chunk_view.end()` + { + auto view = list | std::views::chunk(3); + auto it = view.end(); + assert(std::ranges::equal(*--it, std::list{3, 3})); // We can adjust the tailing chunk-size. + assert(std::ranges::equal(*--it, std::list{2, 2, 2})); + assert(std::ranges::equal(*--it, std::list{1, 1, 1})); + } + + // Test `not_sized_chunk_view.end()` + { + auto not_sized_list = not_sized_view(list | std::views::all); + auto view = not_sized_list | std::views::chunk(4); + static_assert(std::ranges::bidirectional_range); + static_assert(!std::ranges::sized_range); + static_assert( + std::same_as< + decltype(view.end()), + std:: + default_sentinel_t>); // We cannot handle the tailing chunk without size info, so we forbids one to derement from end(). + } + + // Test `forward_chunk_view.end()` + { + auto view = list | std::views::chunk(5); + assert(++(++view.begin()) == view.end()); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp new file mode 100644 index 0000000000000..cdfb3edee38cb --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/general.pass.cpp @@ -0,0 +1,47 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// std::views::chunk + +#include + +#include +#include +#include + +#include "test_range.h" + +constexpr bool test() { + auto str = std::string_view("Cheese the chicken chunk by chunk on truck by truck"); + // clang-format off + auto str2 = str + | std::views::chunk(4) + | std::views::join + | std::views::chunk(314159) + | std::views::take(1) + | std::views::join + | std::views::lazy_split(' ') + | std::views::chunk(2) + | std::views::transform([] (auto&& subview) + { + return subview | std::views::join_with(' '); + }) + | std::views::join_with(' '); + // clang-format on + assert(std::ranges::equal(str, str2)); + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} \ No newline at end of file diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h new file mode 100644 index 0000000000000..e66b8bf56276f --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h @@ -0,0 +1,64 @@ +//===----------------------------------------------------------------------===// +// +// 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 TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H +#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_TYPES_H + +#include +#include +#include + +template +struct exactly_input_view : View, std::ranges::view_interface> { + struct iterator : std::ranges::iterator_t { + using iterator_concept = std::input_iterator_tag; + constexpr iterator() = default; + constexpr iterator(std::ranges::iterator_t i) : std::ranges::iterator_t(i) {} + constexpr auto operator*() const { return std::ranges::iterator_t::operator*(); } + friend constexpr void operator+(auto&&...) = delete; + friend constexpr void operator-(auto&&...) = delete; + friend constexpr iterator& operator++(iterator& self) { + self.std::ranges::template iterator_t::operator++(); + return self; + } + friend constexpr void operator++(iterator& self, int) { ++self; } + friend constexpr void operator--(auto&&...) = delete; + }; + + constexpr iterator begin(this auto&& self) { return iterator(self.View::begin()); } + constexpr iterator end(this auto&& self) { return iterator(self.View::end()); } +}; + +template +struct not_sized_view : View, std::ranges::view_interface> { + struct iterator : std::ranges::iterator_t { + using iterator_concept = std::bidirectional_iterator_tag; + constexpr iterator() = default; + constexpr iterator(std::ranges::iterator_t i) : std::ranges::iterator_t(i) {} + friend constexpr void operator-(iterator, iterator) = delete; + friend constexpr iterator& operator++(iterator& self) { + self.std::ranges::template iterator_t::operator++(); + return self; + } + friend constexpr iterator operator++(iterator& self, int) { return ++self; } + friend constexpr iterator& operator--(iterator& self) { + self.std::ranges::template iterator_t::operator--(); + return self; + } + friend constexpr iterator operator--(iterator& self, int) { return --self; } + }; + + constexpr iterator begin(this auto&& self) { return iterator(self.View::begin()); } + constexpr iterator end(this auto&& self) { return iterator(self.View::end()); } + constexpr auto size() const = delete; +}; + +template +inline constexpr bool std::ranges::disable_sized_range> = true; + +#endif \ No newline at end of file diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py index 0802f865f9406..8cec6fe9b971b 100644 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -1103,7 +1103,6 @@ def add_version_header(tc): "name": "__cpp_lib_ranges_chunk", "values": {"c++23": 202202}, "headers": ["ranges"], - "unimplemented": True, }, { "name": "__cpp_lib_ranges_chunk_by", diff --git a/llvm/utils/gn/secondary/libcxx/include/BUILD.gn b/llvm/utils/gn/secondary/libcxx/include/BUILD.gn index 9755fe3a7ea50..16be8fa8de795 100644 --- a/llvm/utils/gn/secondary/libcxx/include/BUILD.gn +++ b/llvm/utils/gn/secondary/libcxx/include/BUILD.gn @@ -1352,6 +1352,7 @@ if (current_toolchain == default_toolchain) { "__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",