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 f1912668e4013..513e44b27372a 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: P2442R1: Windowing range adaptors: ``views::chunk`` and ``views::slide`` (`Github `__) (Implemented ``views::slide`` only) 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 19732016ee411..3fea9a0465862 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/__ranges/chunk_view.h b/libcxx/include/__ranges/chunk_view.h new file mode 100644 index 0000000000000..f19d2c9ede8a7 --- /dev/null +++ b/libcxx/include/__ranges/chunk_view.h @@ -0,0 +1,562 @@ +// -*- 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 +[[nodiscard]] _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 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"); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const& + requires std::copy_constructible<_View> + { + return __base_; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __outer_iterator begin() { + __current_.__emplace(ranges::begin(__base_)); + __remainder_ = __n_; + return __outer_iterator(*this); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept { + return std::default_sentinel; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() + requires sized_range<_View> + { + return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_)); + } + + [[nodiscard]] _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; + + [[nodiscard]] _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; } + + [[nodiscard]] _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; + } + + [[nodiscard]] _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; + } + + [[nodiscard]] _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: + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __inner_iterator begin() const noexcept { + return __inner_iterator(*__parent_); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr default_sentinel_t end() const noexcept { return default_sentinel; } + + [[nodiscard]] _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) noexcept + : __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; + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_View> base() const& { return *__parent_->__current_; } + + [[nodiscard]] _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; } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool + operator==(const __inner_iterator& __i, default_sentinel_t) { + return __i.__parent_->__remainder_ == 0; + } + + [[nodiscard]] _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_); + } + + [[nodiscard]] _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); + } + + [[nodiscard]] _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 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"); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const& + requires copy_constructible<_View> + { + return __base_; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() + requires(!__simple_view<_View>) + { + return __iterator(this, ranges::begin(__base_)); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const + requires forward_range + { + return __iterator(this, ranges::begin(__base_)); + } + + [[nodiscard]] _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; + } + + [[nodiscard]] _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; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() + requires sized_range<_View> + { + return std::__to_unsigned_like(ranges::__div_ceil(ranges::distance(__base_), __n_)); + } + + [[nodiscard]] _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) {} + + [[nodiscard]] 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_) {} + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() const { return __current_; } + + [[nodiscard]] _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_); + } + + [[nodiscard]] _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; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) { + return __x.__current_ == __y.__current_; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator& __x, default_sentinel_t) { + return __x.__current_ == __x.__end_; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<(const __iterator& __x, const __iterator& __y) + requires random_access_range<_Base> + { + return __x.__current_ < __y.__current_; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(const __iterator& __x, const __iterator& __y) + requires random_access_range<_Base> + { + return __y < __x; + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(const __iterator& __x, const __iterator& __y) + requires random_access_range<_Base> + { + return !(__y < __x); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(const __iterator& __x, const __iterator& __y) + requires random_access_range<_Base> + { + return !(__x < __y); + } + + [[nodiscard]] _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_; + } + + [[nodiscard]] _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; + } + + [[nodiscard]] _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; + } + + [[nodiscard]] _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; + } + + [[nodiscard]] _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_; + } + + [[nodiscard]] _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_); + } + + [[nodiscard]] _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 static constexpr auto + operator()(_Range&& __range, range_difference_t<_Range> __n) 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 static constexpr auto + operator()(_DifferenceType __n) noexcept(is_nothrow_constructible_v, _DifferenceType>) { + return __pipeable(std::__bind_back(__fn{}, 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 bbc2617835bc0..b81ce3640bffd 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1859,6 +1859,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/include/version b/libcxx/include/version index 05532ea731ff3..daeb88aa2a79d 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -522,7 +522,7 @@ __cpp_lib_void_t 201411L # define __cpp_lib_ranges 202406L // # define __cpp_lib_ranges_as_const 202207L # define __cpp_lib_ranges_as_rvalue 202207L -// # define __cpp_lib_ranges_chunk 202202L +# define __cpp_lib_ranges_chunk 202202L # define __cpp_lib_ranges_chunk_by 202202L # define __cpp_lib_ranges_contains 202207L # define __cpp_lib_ranges_find_last 202207L 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/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp new file mode 100644 index 0000000000000..14c618c505e70 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/no_unique_address.compile.pass.cpp @@ -0,0 +1,53 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// This test ensures that we use `[[no_unique_address]]` in `chunk_view`. + +#include +#include +#include +#include + +#include "test_iterators.h" +#include "test_range.h" + +struct input_view { + cpp20_input_iterator begin() const; + sentinel_wrapper> end() const; +}; +template <> +inline constexpr bool std::ranges::enable_view = true; +static_assert(std::ranges::input_range && !std::ranges::forward_range); + +struct forward_view { + int* begin() const; + int* end() const; +}; +template <> +inline constexpr bool std::ranges::enable_view = true; +static_assert(std::ranges::forward_range); + +using CV1 = std::ranges::chunk_view; +// Expected CV1 (with View == input) layout: +// [[no_unique_address]] _View __base_ // size: 0 +// [[no_unique_address]] range_difference_t<_View> __n_ // size: sizeof(ptrdiff_t) +// [[no_unique_address]] range_difference_t<_View> __remainder_ // size: sizeof(ptrdiff_t) +// [[no_unique_address]] __non_propagating_cache> __current_ // size: sizeof(__non_propagating_cache>), align: std::ptrdiff_t +static_assert(alignof(std::ranges::__non_propagating_cache>) == alignof(std::ptrdiff_t)); +static_assert(sizeof(CV1) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t) * 2 + + sizeof(std::ranges::__non_propagating_cache>)); + +using CV2 = std::ranges::chunk_view; +// Expected CV2 (with View >= forward) layout: +// [[no_unique_address]] _View __base_ // size: 0 +// [[no_unique_address]] range_difference_t<_View> // size: sizeof(ptrdiff_t) +static_assert(sizeof(CV2) == /*sizeof(__base_) == 0 + */ sizeof(std::ptrdiff_t)); diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp new file mode 100644 index 0000000000000..5e557715f4b03 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk/nodiscard.verify.cpp @@ -0,0 +1,86 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// Test the libc++ extension that std::ranges::chunk_view::iterator::operator* is marked as [[nodiscard]]. + +#include +#include + +void test() { + char range[6] = {'x', 'x', 'y', 'y', 'z', 'z'}; + auto view = range | std::views::chunk(2); + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::views::chunk(3); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::views::chunk(range, 3); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + range | std::views::chunk(3); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::views::reverse | std::views::chunk(3); + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + view.base(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::as_const(view).base(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::move(std::as_const(view)).base(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::move(view).base(); + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + view.begin(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::as_const(view).begin(); + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + view.end(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::as_const(view).end(); + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::views::chunk(3); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::views::chunk(range, 3); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + range | std::views::chunk(3); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::views::reverse | std::views::chunk(3); + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + *view.begin(); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + *std::as_const(view).begin(); + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + (view.begin() == view.end()); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + (std::as_const(view).begin() == view.end()); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + (view.begin() == std::as_const(view).end()); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + (std::as_const(view).begin() == std::as_const(view).end()); + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + (view.begin() == view.end()); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + (std::as_const(view).begin() == view.end()); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + (view.begin() == std::as_const(view).end()); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + (std::as_const(view).begin() == std::as_const(view).end()); + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::ranges::iter_move(view.begin()); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::ranges::iter_move(std::as_const(view).begin()); +} diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp index 5116864879485..41e1603f62350 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/ranges.version.compile.pass.cpp @@ -270,17 +270,11 @@ # error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++23" # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_chunk -# error "__cpp_lib_ranges_chunk should be defined in c++23" -# endif -# if __cpp_lib_ranges_chunk != 202202L -# error "__cpp_lib_ranges_chunk should have the value 202202L in c++23" -# endif -# else -# ifdef __cpp_lib_ranges_chunk -# error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_chunk +# error "__cpp_lib_ranges_chunk should be defined in c++23" +# endif +# if __cpp_lib_ranges_chunk != 202202L +# error "__cpp_lib_ranges_chunk should have the value 202202L in c++23" # endif # ifndef __cpp_lib_ranges_chunk_by @@ -387,17 +381,11 @@ # error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++26" # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_chunk -# error "__cpp_lib_ranges_chunk should be defined in c++26" -# endif -# if __cpp_lib_ranges_chunk != 202202L -# error "__cpp_lib_ranges_chunk should have the value 202202L in c++26" -# endif -# else -# ifdef __cpp_lib_ranges_chunk -# error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_chunk +# error "__cpp_lib_ranges_chunk should be defined in c++26" +# endif +# if __cpp_lib_ranges_chunk != 202202L +# error "__cpp_lib_ranges_chunk should have the value 202202L in c++26" # endif # ifndef __cpp_lib_ranges_chunk_by diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp index 996ec29dce697..a825934e49b3a 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp @@ -5700,17 +5700,11 @@ # error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++23" # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_chunk -# error "__cpp_lib_ranges_chunk should be defined in c++23" -# endif -# if __cpp_lib_ranges_chunk != 202202L -# error "__cpp_lib_ranges_chunk should have the value 202202L in c++23" -# endif -# else -# ifdef __cpp_lib_ranges_chunk -# error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_chunk +# error "__cpp_lib_ranges_chunk should be defined in c++23" +# endif +# if __cpp_lib_ranges_chunk != 202202L +# error "__cpp_lib_ranges_chunk should have the value 202202L in c++23" # endif # ifndef __cpp_lib_ranges_chunk_by @@ -7619,17 +7613,11 @@ # error "__cpp_lib_ranges_as_rvalue should have the value 202207L in c++26" # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_chunk -# error "__cpp_lib_ranges_chunk should be defined in c++26" -# endif -# if __cpp_lib_ranges_chunk != 202202L -# error "__cpp_lib_ranges_chunk should have the value 202202L in c++26" -# endif -# else -# ifdef __cpp_lib_ranges_chunk -# error "__cpp_lib_ranges_chunk should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_chunk +# error "__cpp_lib_ranges_chunk should be defined in c++26" +# endif +# if __cpp_lib_ranges_chunk != 202202L +# error "__cpp_lib_ranges_chunk should have the value 202202L in c++26" # endif # ifndef __cpp_lib_ranges_chunk_by 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..c61967ec9da00 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/adaptor.pass.cpp @@ -0,0 +1,78 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// std::views::chunk + +#include +#include +#include +#include +#include +#include + +#include "test_range.h" +#include "types.h" + +constexpr bool test() { + std::array array = {1, 2, 3, 4, 5, 6, 7, 8}; + std::ranges::ref_view> view = array | std::views::all; + + // Test `views::chunk(view, n)` + { + std::same_as>>> decltype(auto) chunked = + std::views::chunk(view, 2); + assert(std::ranges::equal(*chunked.begin(), std::array{1, 2})); + std::same_as>>> decltype(auto) const_chunked = + std::views::chunk(std::as_const(view), 2); + assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2})); + } + + // Test `views::chunk(n)(range)` + { + static_assert(noexcept(std::views::chunk(2))); + /*__pipable*/ auto adaptor = std::views::chunk(3); + std::same_as>>> decltype(auto) chunked = + adaptor(view); + assert(std::ranges::equal(*chunked.begin(), std::array{1, 2, 3})); + std::same_as>>> decltype(auto) const_chunked = + adaptor(std::as_const(view)); + assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2, 3})); + } + + // Test `view | views::chunk` + { + std::same_as>>> decltype(auto) chunked = + view | std::views::chunk(4); + assert(std::ranges::equal(*chunked.begin(), std::array{1, 2, 3, 4})); + std::same_as>>> decltype(auto) const_chunked = + std::as_const(view) | std::views::chunk(4); + assert(std::ranges::equal(*const_chunked.begin(), std::array{1, 2, 3, 4})); + } + + // Test `views::chunk | adaptor` + { + /*__pipable*/ auto adaptors = std::views::chunk(5) | std::views::join; + std::ranges::input_range auto rejoined = view | adaptors; + assert(std::ranges::equal(rejoined, view)); + std::ranges::input_range auto const_rejoined = std::as_const(view) | adaptors; + assert(std::ranges::equal(const_rejoined, view)); + } + + 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 0000000000000..e3a315a7a39ce --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/base.pass.cpp @@ -0,0 +1,51 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// constexpr V base() const& requires copyy_constructible; +// constexpr V base() &&; + +#include +#include +#include +#include +#include + +#include "test_range.h" + +constexpr bool test() { + std::array array = {1, 2, 3, 4, 5, 6, 7, 8}; + std::ranges::chunk_view>> chunked = array | std::views::chunk(3); + std::ranges::chunk_view>> const_chunked = + std::as_const(array) | std::views::chunk(4); + + // Test `chunk_view.base()` + { + std::same_as::iterator> decltype(auto) begin = chunked.begin().base(); + std::same_as::iterator> decltype(auto) end = chunked.end().base(); + assert(begin == array.begin()); + assert(end == array.end()); + + std::same_as::const_iterator> decltype(auto) const_begin = const_chunked.begin().base(); + std::same_as::const_iterator> decltype(auto) const_end = const_chunked.end().base(); + assert(const_begin == std::as_const(array).begin()); + assert(const_end == std::as_const(array).end()); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} 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..3cccd7d1bebc6 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/begin.pass.cpp @@ -0,0 +1,67 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// V models only input_range: +// constexpr __outer_iterator begin(); + +// V models forward_range: +// constexpr auto begin() requires (!__simple_view); +// constexpr auto begin() const requires forward_range; + +#include +#include +#include +#include +#include + +#include "test_range.h" +#include "types.h" + +constexpr bool test() { + std::vector vector = {1, 2, 3, 4, 5, 6, 7, 8}; + std::ranges::chunk_view>> chunked = vector | std::views::chunk(3); + std::ranges::chunk_view>> const_chunked = + std::as_const(vector) | std::views::chunk(3); + std::ranges::chunk_view> input_chunked = input_span(vector.data(), 8) | std::views::chunk(3); + + // Test `chunk_view.begin()` when V models only input_range + { + /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin(); + assert(std::ranges::equal(*it, std::vector{1, 2, 3})); + assert(std::ranges::equal(*++it, std::vector{4, 5, 6})); + assert(std::ranges::equal(*++it, std::vector{7, 8})); + assert(++it == input_chunked.end()); + } + + // Test `chunk_view.begin()` when V models forward_range + { + /*chunk_view::__iterator*/ std::forward_iterator auto it = chunked.begin(); + assert(std::ranges::equal(*it, std::vector{1, 2, 3})); + assert(std::ranges::equal(*++it, std::vector{4, 5, 6})); + assert(std::ranges::equal(*++it, std::vector{7, 8})); + assert(++it == chunked.end()); + /*chunk_view::__iterator*/ std::forward_iterator auto const_it = const_chunked.begin(); + assert(std::ranges::equal(*const_it, std::vector{1, 2, 3})); + assert(std::ranges::equal(*++const_it, std::vector{4, 5, 6})); + assert(std::ranges::equal(*++const_it, std::vector{7, 8})); + assert(++const_it == const_chunked.end()); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} 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..9bf2baba58c78 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/ctad.compile.pass.cpp @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// template +// chunk_view(R&&, range_difference_t) -> chunk_view>; + +#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 test_ctad() { + view v; + range r; + borrowed_range br; + + // clang-format off + static_assert(std::same_as>); + static_assert(std::same_as>); + static_assert(std::same_as>>); + static_assert(std::same_as>>); + static_assert(std::same_as>>); + static_assert(std::same_as>>); + // clang-format on +} 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..73eb0d967f6a2 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/end.pass.cpp @@ -0,0 +1,65 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// V models only input_range: +// constexpr default_sentinel_t end(); + +// V moduels forward_range: +// constexpr auto end() requires (!__simple_view); +// constexpr auto end() const requires forward_range; + +#include +#include +#include +#include +#include +#include + +#include "test_range.h" +#include "types.h" + +constexpr bool test() { + std::vector vector = {1, 2, 3, 4, 5, 6, 7, 8}; + std::ranges::chunk_view>> chunked = vector | std::views::chunk(3); + std::ranges::chunk_view>> const_chunked = + std::as_const(vector) | std::views::chunk(3); + std::ranges::chunk_view> input_chunked = input_span(vector.data(), 8) | std::views::chunk(3); + + // Test `chunk_view.end()` when V models only input_range + { + static_assert(noexcept(input_chunked.end())); + [[maybe_unused]] std::same_as auto it = input_chunked.end(); + } + + // Test `chunk_view.end()` when V models forward_range + { + /*chunk_view::__iterator*/ std::forward_iterator auto it = chunked.end(); + assert(std::ranges::equal(*--it, std::vector{7, 8})); + assert(std::ranges::equal(*--it, std::vector{4, 5, 6})); + assert(std::ranges::equal(*--it, std::vector{1, 2, 3})); + assert(it == chunked.begin()); + /*chunk_view::__iterator*/ std::forward_iterator auto const_it = const_chunked.end(); + assert(std::ranges::equal(*--const_it, std::vector{7, 8})); + assert(std::ranges::equal(*--const_it, std::vector{4, 5, 6})); + assert(std::ranges::equal(*--const_it, std::vector{1, 2, 3})); + assert(const_it == const_chunked.begin()); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} 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..89891bc093080 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/general.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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// General tests for chunk_view. This file does not test anything specifically. + +#include +#include +#include +#include + +#include "test_range.h" + +constexpr bool test() { + std::string_view str = "Cheese with chicken chunk by chunk on truck with my trick"; + // 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; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp new file mode 100644 index 0000000000000..d86726d958e6e --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/compare.pass.cpp @@ -0,0 +1,109 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// V models only input_range +// friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t); +// friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i) +// requires sized_sentinel_for, iterator_t>; +// friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t t) +// requires sized_sentinel_for, iterator_t>; + +// V models forward_range +// friend constexpr bool operator==(const iterator& x, const iterator& y) +// friend constexpr bool operator<(const iterator& x, const iterator& y) +// requires random_access_range; +// friend constexpr bool operator>(const iterator& x, const iterator& y) +// requires random_access_range; +// friend constexpr bool operator<=(const iterator& x, const iterator& y) +// requires random_access_range; +// friend constexpr bool operator>=(const iterator& x, const iterator& y) +// requires random_access_range; +// friend constexpr auto operator<=>(const iterator& x, const iterator& y) +// requires random_access_range && +// three_way_comparable>; + +#include +#include +#include +#include +#include +#include + +#include "test_range.h" +#include "../types.h" + +constexpr bool test() { + std::vector vector = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + std::ranges::chunk_view>> chunked = vector | std::views::chunk(3); + std::ranges::chunk_view> input_chunked = input_span(vector) | std::views::chunk(3); + + // Test `friend constexpr bool opreator==(const outer_iterator& x, default_sentinel_t)` + { + /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin(); + std::ranges::advance(it, 4); + assert(it == std::default_sentinel); + } + + // Test `friend constexpr difference_type operator-(default_sentinel_t t, const outer_iterator& i)` + { + assert(input_chunked.end() - input_chunked.begin() == 4); + } + + // Test `friend constexpr difference_type operator-(const outer_iterator& i, default_sentinel_t)` + { + assert(input_chunked.begin() - input_chunked.end() == -4); + } + + // Test `friend constexpr bool operator==(const iterator& x, const iterator& y)` + { + assert(chunked.begin() == chunked.begin()); + assert(chunked.end() == chunked.end()); + } + + // Test `friend constexpr bool operator<(const iterator& x, const iterator& y)` + { + assert(chunked.begin() < chunked.end()); + } + + // Test `friend constexpr bool operator>(const iterator& x, const iterator& y)` + { + assert(chunked.end() > chunked.begin()); + } + + // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)` + { + assert(chunked.begin() <= chunked.begin()); + assert(chunked.begin() <= chunked.end()); + } + + // Test `friend constexpr bool operator>=(const iterator& x, const iterator& y)` + { + assert(chunked.end() >= chunked.end()); + assert(chunked.end() >= chunked.begin()); + } + + // Test `friend constexpr auto operator<=>(const iterator& x, const iterator& y)` + { + assert((chunked.begin() <=> chunked.begin()) == std::strong_ordering::equal); + assert((chunked.begin() <=> chunked.end()) == std::strong_ordering::less); + assert((chunked.end() <=> chunked.begin()) == std::strong_ordering::greater); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp new file mode 100644 index 0000000000000..397b32e73080a --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/decrement.pass.cpp @@ -0,0 +1,60 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// constexpr iterator& operator--() +// requires bidirectional_range; +// constexpr iterator operator--(int) +// requires bidirectional_range; +// constexpr iterator& operator-=(difference_type) +// requires random_access_range; + +#include +#include +#include +#include +#include + +#include "test_range.h" + +constexpr bool test() { + std::vector vector = {1, 2, 3, 4, 5, 6, 7, 8}; + std::ranges::chunk_view>> chunked = vector | std::views::chunk(2); + + // Test `constexpr iterator& operator--();` + { + /*chunk_view::__outer_iterator*/ std::bidirectional_iterator auto it = chunked.end(); + assert(std::ranges::equal(*--it, std::vector{7, 8})); + } + + // Test `constexpr iterator operator--(int)` + { + /*chunk_view::__outer_iterator*/ std::bidirectional_iterator auto it = chunked.end(); + it--; + assert(std::ranges::equal(*it, std::vector{7, 8})); + } + + // Test `constexpr iterator& operator-=(difference_type)` + { + /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.end(); + it -= 3; + assert(std::ranges::equal(*it, std::vector{3, 4})); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp new file mode 100644 index 0000000000000..b8772d0fac3d2 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/deref.pass.cpp @@ -0,0 +1,77 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// V models only input_range: +// constexpr value_type outer_iterator::operator*() const; +// constexpr inner_iterator outer_iterator::value_type::begin() const noexcept; +// constexpr default_sentinel_t outer_iterator::value_type::end() const noexcept; +// constexpr range_reference_v inner_iterator::operator*() const; + +// V models forward_range: +// constexpr value_type iterator::operator*() const; + +#include +#include +#include +#include +#include +#include + +#include "test_range.h" +#include "../types.h" + +constexpr bool test() { + std::vector vector = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + std::ranges::chunk_view>> chunked = vector | std::views::chunk(3); + std::ranges::chunk_view> input_chunked = input_span(vector) | std::views::chunk(3); + + // Test `constexpr value_type outer_iterator::operator*() const` + { + static_assert(std::ranges::input_range); + } + + // Test `constexpr inner_iterator outer_iterator::value_type::begin() const noexcept` + { + /*chunk_view::__outer_iterator::value_type*/ std::ranges::input_range auto inner = *input_chunked.begin(); + assert(*inner.begin() == *vector.begin()); + static_assert(noexcept(inner.begin())); + } + + // Test `constexpr default_sentinel_t outer_iterator::value_type::end() const noexcept` + { + /*chunk_view::__outer_iterator::value_type*/ std::ranges::input_range auto inner = *input_chunked.begin(); + [[maybe_unused]] std::same_as auto it = inner.end(); + static_assert(noexcept((inner.end()))); + } + + // Test `constexpr value_type iterator::operator*() const` + { + /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin(); + std::same_as decltype(auto) v = *it; + assert(v == 1); + } + + // Test `constexpr range_reference_v inner_iterator::operator*() const` + { + std::same_as decltype(auto) v = *(*chunked.begin()).begin(); + assert(v == 1); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp new file mode 100644 index 0000000000000..6048aba027ce9 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/range.chunk.iter/increment.pass.cpp @@ -0,0 +1,95 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: std-at-least-c++23 + +// + +// V models only input_range +// constexpr outer_iterator& operator++(); +// constexpr void operator++(int); +// constexpr inner_iterator& operator++(); +// constexpr void operator++(int); + +// V models forward_range +// constexpr iterator& operator++(); +// constexpr iterator operator++(int); +// constexpr iterator& operator+=(difference_type) +// requires random_access_range; + +#include +#include +#include +#include +#include + +#include "test_range.h" +#include "../types.h" + +constexpr bool test() { + std::vector vector = {1, 2, 3, 4, 5, 6, 7, 8}; + std::ranges::chunk_view>> chunked = vector | std::views::chunk(2); + std::ranges::chunk_view> input_chunked = input_span(vector) | std::views::chunk(2); + + // Test `constexpr outer_iterator& operator++();` + { + /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin(); + assert(std::ranges::equal(*++it, std::vector{3, 4})); + } + + // Test `constexpr void operator++(int);` + { + /*chunk_view::__outer_iterator*/ std::input_iterator auto it = input_chunked.begin(); + static_assert(std::same_as); + it++; + assert(std::ranges::equal(*it, std::vector{3, 4})); + } + + // Test `constexpr inner_iterator& operator++();` + { + /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin(); + assert(*++it == 2); + } + + // Test `constexpr inner_iterator& operator++();` + { + /*chunk_view::__inner_iterator*/ std::input_iterator auto it = (*input_chunked.begin()).begin(); + static_assert(std::same_as); + it++; + assert(*it == 2); + } + + // Test `constexpr iterator& operator++();` + { + /*chunk_view::__iterator*/ std::forward_iterator auto it = chunked.begin(); + assert(std::ranges::equal(*++it, std::vector{3, 4})); + } + + // Test `constexpr iterator operator++(int)` + { + /*chunk_view::__iterator*/ std::forward_iterator auto it = chunked.begin(); + it++; + assert(std::ranges::equal(*it, std::vector{3, 4})); + } + + // Test `constexpr iterator& operator+=(difference_type)` + { + /*chunk_view::__iterator*/ std::random_access_iterator auto it = chunked.begin(); + it += 3; + assert(std::ranges::equal(*it, std::vector{7, 8})); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} 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..09d16c9a48af0 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk/types.h @@ -0,0 +1,50 @@ +//===----------------------------------------------------------------------===// +// +// 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 +#include + +// input_span + +template +struct input_span : std::span { + struct iterator : std::span::iterator { + using iterator_concept = std::input_iterator_tag; + constexpr iterator() = default; + constexpr iterator(std::span::iterator i) : std::span::iterator(i) {} + constexpr auto operator*() const { return std::span::iterator::operator*(); } + friend constexpr auto operator+(iterator, std::span::difference_type) = delete; + friend constexpr auto operator+(std::span::difference_type, iterator) = delete; + friend constexpr auto operator-(iterator, std::span::difference_type) = delete; + friend constexpr auto operator-(std::span::difference_type, iterator) = delete; + friend constexpr iterator& operator++(iterator& self) { + ++static_cast::iterator&>(self); + return self; + } + friend constexpr void operator++(iterator& self, int) { ++self; } + friend constexpr iterator& operator--(iterator&) = delete; + friend constexpr void operator--(iterator&, int) = delete; + }; + + using std::span::span; + constexpr iterator begin() { return iterator(std::span::begin()); } + constexpr iterator end() { return iterator(std::span::end()); } +}; + +template +inline constexpr bool std::ranges::enable_view> = true; + +static_assert(std::ranges::input_range> && !std::ranges::forward_range> && + std::ranges::view>); + +#endif 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",