diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -346,7 +346,7 @@ --------------------------------------------------- ----------------- ``__cpp_lib_ranges_chunk`` *unimplemented* --------------------------------------------------- ----------------- - ``__cpp_lib_ranges_chunk_by`` *unimplemented* + ``__cpp_lib_ranges_chunk_by`` ``202202L`` --------------------------------------------------- ----------------- ``__cpp_lib_ranges_iota`` *unimplemented* --------------------------------------------------- ----------------- diff --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst --- a/libcxx/docs/ReleaseNotes/18.rst +++ b/libcxx/docs/ReleaseNotes/18.rst @@ -41,7 +41,7 @@ - P2497R0 - Testing for success or failure of ```` functions - P2697R1 - Interfacing ``bitset`` with ``string_view`` - +- P2443R1 - ``views::chunk_by`` Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv --- a/libcxx/docs/Status/Cxx23Papers.csv +++ b/libcxx/docs/Status/Cxx23Papers.csv @@ -49,7 +49,7 @@ "`P2440R1 `__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","","","|ranges|" "`P2441R2 `__","LWG","``views::join_with``","February 2022","","","|ranges|" "`P2442R1 `__","LWG","Windowing range adaptors: ``views::chunk`` and ``views::slide``","February 2022","","","|ranges|" -"`P2443R1 `__","LWG","``views::chunk_by``","February 2022","","","|ranges|" +"`P2443R1 `__","LWG","``views::chunk_by``","February 2022","|Complete|","18.0","|ranges|" "","","","","","","" "`P0009R18 `__","LWG","mdspan: A Non-Owning Multidimensional Array Reference","July 2022","|In progress| [#note-P0009R18]_|","" "`P0429R9 `__","LWG","A Standard ``flat_map``","July 2022","","" diff --git a/libcxx/docs/Status/RangesViews.csv b/libcxx/docs/Status/RangesViews.csv --- a/libcxx/docs/Status/RangesViews.csv +++ b/libcxx/docs/Status/RangesViews.csv @@ -31,7 +31,7 @@ C++23,`join_with `_,Unassigned,No patch yet,Not started C++23,`slide `_,Unassigned,No patch yet,Not started C++23,`chunk `_,Unassigned,No patch yet,Not started -C++23,`chunk_by `_,Unassigned,No patch yet,Not started +C++23,`chunk_by `_,Jakub Mazurkiewicz,`D144767 `,✅ C++23,`as_const `_,Unassigned,No patch yet,Not started C++23,`as_rvalue `_,Nikolas Klauser,`D137637 `_,✅ C++23,`stride `_,Hristo Hristov and Will Hawkins,`D156924 `_,In Progress diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -602,6 +602,7 @@ __ranges/access.h __ranges/all.h __ranges/as_rvalue_view.h + __ranges/chunk_by_view.h __ranges/common_view.h __ranges/concepts.h __ranges/container_compatible_range.h diff --git a/libcxx/include/__algorithm/ranges_adjacent_find.h b/libcxx/include/__algorithm/ranges_adjacent_find.h --- a/libcxx/include/__algorithm/ranges_adjacent_find.h +++ b/libcxx/include/__algorithm/ranges_adjacent_find.h @@ -24,6 +24,9 @@ # pragma GCC system_header #endif +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + #if _LIBCPP_STD_VER >= 20 _LIBCPP_BEGIN_NAMESPACE_STD @@ -75,4 +78,6 @@ #endif // _LIBCPP_STD_VER >= 20 +_LIBCPP_POP_MACROS + #endif // _LIBCPP___ALGORITHM_RANGES_ADJACENT_FIND_H diff --git a/libcxx/include/__ranges/chunk_by_view.h b/libcxx/include/__ranges/chunk_by_view.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__ranges/chunk_by_view.h @@ -0,0 +1,230 @@ +// -*- 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_BY_VIEW_H +#define _LIBCPP___RANGES_CHUNK_BY_VIEW_H + +#include <__algorithm/ranges_adjacent_find.h> +#include <__assert> +#include <__concepts/constructible.h> +#include <__config> +#include <__functional/bind_back.h> +#include <__functional/invoke.h> +#include <__functional/not_fn.h> +#include <__functional/reference_wrapper.h> +#include <__iterator/concepts.h> +#include <__iterator/default_sentinel.h> +#include <__iterator/iterator_traits.h> +#include <__iterator/next.h> +#include <__iterator/prev.h> +#include <__memory/addressof.h> +#include <__ranges/access.h> +#include <__ranges/all.h> +#include <__ranges/concepts.h> +#include <__ranges/movable_box.h> +#include <__ranges/non_propagating_cache.h> +#include <__ranges/range_adaptor.h> +#include <__ranges/reverse_view.h> +#include <__ranges/subrange.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/is_object.h> +#include <__utility/forward.h> +#include <__utility/in_place.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 , iterator_t<_View>> _Pred> + requires view<_View> && is_object_v<_Pred> +class chunk_by_view : public view_interface> { + _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View(); + _LIBCPP_NO_UNIQUE_ADDRESS __movable_box<_Pred> __pred_; + + // We cache the result of begin() to allow providing an amortized O(1). + using _Cache = __non_propagating_cache>; + _Cache __cached_begin_; + + class __iterator; + + _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> __find_next(iterator_t<_View> __current) { + _LIBCPP_ASSERT_UNCATEGORIZED( + __pred_.__has_value(), "Trying to call __find_next() on a chunk_by_view that does not have a valid predicate."); + + return ranges::next(ranges::adjacent_find(__current, ranges::end(__base_), std::not_fn(std::ref(*__pred_))), + 1, + ranges::end(__base_)); + } + + _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_View> __find_prev(iterator_t<_View> __current) + requires bidirectional_range<_View> + { + _LIBCPP_ASSERT_UNCATEGORIZED( + __current != ranges::begin(__base_), "Trying to call __find_prev() on a begin iterator."); + _LIBCPP_ASSERT_UNCATEGORIZED( + __pred_.__has_value(), "Trying to call __find_prev() on a chunk_by_view that does not have a valid predicate."); + + auto __first = ranges::begin(__base_); + reverse_view __reversed{subrange{__first, __current}}; + auto __reversed_pred = [this](_Tp&& __x, _Up&& __y) { + return !std::invoke(*__pred_, std::forward<_Up>(__y), std::forward<_Tp>(__x)); + }; + return ranges::prev(ranges::adjacent_find(__reversed, __reversed_pred).base(), 1, std::move(__first)); + } + +public: + _LIBCPP_HIDE_FROM_ABI chunk_by_view() + requires default_initializable<_View> && default_initializable<_Pred> + = default; + + _LIBCPP_HIDE_FROM_ABI constexpr explicit chunk_by_view(_View __base, _Pred __pred) + : __base_(std::move(__base)), __pred_(in_place, std::move(__pred)) {} + + _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 const _Pred& pred() const { return *__pred_; } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator begin() { + _LIBCPP_ASSERT_UNCATEGORIZED( + __pred_.__has_value(), "Trying to call begin() on a chunk_by_view that does not have a valid predicate."); + + auto __first = ranges::begin(__base_); + if (!__cached_begin_.__has_value()) { + __cached_begin_.__emplace(__find_next(__first)); + } + return {*this, std::move(__first), *__cached_begin_}; + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() { + if constexpr (common_range<_View>) { + return __iterator{*this, ranges::end(__base_), ranges::end(__base_)}; + } else { + return default_sentinel; + } + } +}; + +template +chunk_by_view(_Range&&, _Pred) -> chunk_by_view, _Pred>; + +template , iterator_t<_View>> _Pred> + requires view<_View> && is_object_v<_Pred> +class chunk_by_view<_View, _Pred>::__iterator { + friend chunk_by_view; + + chunk_by_view* __parent_ = nullptr; + _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __current_ = iterator_t<_View>(); + _LIBCPP_NO_UNIQUE_ADDRESS iterator_t<_View> __next_ = iterator_t<_View>(); + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator( + chunk_by_view& __parent, iterator_t<_View> __current, iterator_t<_View> __next) + : __parent_(std::addressof(__parent)), __current_(__current), __next_(__next) {} + +public: + using value_type = subrange>; + using difference_type = range_difference_t<_View>; + using iterator_category = input_iterator_tag; + using iterator_concept = conditional_t, bidirectional_iterator_tag, forward_iterator_tag>; + + _LIBCPP_HIDE_FROM_ABI __iterator() = default; + + _LIBCPP_HIDE_FROM_ABI constexpr value_type operator*() const { + _LIBCPP_ASSERT_UNCATEGORIZED(__current_ != __next_, "Trying to dereference past-the-end chunk_by_view iterator."); + return {__current_, __next_}; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() { + _LIBCPP_ASSERT_UNCATEGORIZED(__current_ != __next_, "Trying to increment past end chunk_by_view iterator."); + __current_ = __next_; + __next_ = __parent_->__find_next(__current_); + 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<_View> + { + __next_ = __current_; + __current_ = __parent_->__find_prev(__next_); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int) + requires bidirectional_range<_View> + { + auto __tmp = *this; + --*this; + return __tmp; + } + + _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.__next_; + } +}; + +namespace views { +namespace __chunk_by { +struct __fn { + template + _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range, _Pred&& __pred) const + noexcept(noexcept(/**/ chunk_by_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred)))) + -> decltype(/*--*/ chunk_by_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred))) { + return /*-------------*/ chunk_by_view(std::forward<_Range>(__range), std::forward<_Pred>(__pred)); + } + + template + requires constructible_from, _Pred> + _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Pred&& __pred) const + noexcept(is_nothrow_constructible_v, _Pred>) { + return __range_adaptor_closure_t(std::__bind_back(*this, std::forward<_Pred>(__pred))); + } +}; +} // namespace __chunk_by + +inline namespace __cpo { +inline constexpr auto chunk_by = __chunk_by::__fn{}; +} // namespace __cpo +} // namespace views +} // namespace ranges + +#endif // _LIBCPP_STD_VER >= 23 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___RANGES_CHUNK_BY_VIEW_H diff --git a/libcxx/include/__ranges/movable_box.h b/libcxx/include/__ranges/movable_box.h --- a/libcxx/include/__ranges/movable_box.h +++ b/libcxx/include/__ranges/movable_box.h @@ -26,6 +26,9 @@ # pragma GCC system_header #endif +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + _LIBCPP_BEGIN_NAMESPACE_STD #if _LIBCPP_STD_VER >= 20 @@ -203,4 +206,6 @@ _LIBCPP_END_NAMESPACE_STD +_LIBCPP_POP_MACROS + #endif // _LIBCPP___RANGES_MOVABLE_BOX_H diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1659,6 +1659,7 @@ export std_private_ranges_owning_view } module std_private_ranges_as_rvalue_view [system] { header "__ranges/as_rvalue_view.h" } +module std_private_ranges_chunk_by_view [system] { header "__ranages/chunk_by_view.h" } module std_private_ranges_common_view [system] { header "__ranges/common_view.h" } module std_private_ranges_concepts [system] { header "__ranges/concepts.h" diff --git a/libcxx/include/ranges b/libcxx/include/ranges --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -320,17 +320,24 @@ class zip_view; // C++23 template - inline constexpr bool enable_borrowed_range> = // C++23 + inline constexpr bool enable_borrowed_range> = // C++23 (enable_borrowed_range && ...); - namespace views { inline constexpr unspecified zip = unspecified; } // C++23 + namespace views { inline constexpr unspecified zip = unspecified; } // C++23 // [range.as.rvalue] template requires input_range - class as_rvalue_view; // since C++23 + class as_rvalue_view; // C++23 - namespace views { inline constexpr unspecified as_rvalue ) unspecified; } // since C++23 + namespace views { inline constexpr unspecified as_rvalue ) unspecified; } // C++23 + + [range.chunk.by] + template, iterator_t> Pred> + requires view && is_object_v + class chunk_by_view; // C++23 + + namespace views { inline constexpr unspecified chunk_by = unspecified; } // C++23 } namespace std { @@ -373,6 +380,7 @@ #include <__ranges/access.h> #include <__ranges/all.h> #include <__ranges/as_rvalue_view.h> +#include <__ranges/chunk_by_view.h> #include <__ranges/common_view.h> #include <__ranges/concepts.h> #include <__ranges/counted.h> diff --git a/libcxx/include/version b/libcxx/include/version --- a/libcxx/include/version +++ b/libcxx/include/version @@ -435,7 +435,7 @@ // # define __cpp_lib_print 202207L # define __cpp_lib_ranges_as_rvalue 202207L // # define __cpp_lib_ranges_chunk 202202L -// # define __cpp_lib_ranges_chunk_by 202202L +# define __cpp_lib_ranges_chunk_by 202202L // # define __cpp_lib_ranges_iota 202202L // # define __cpp_lib_ranges_join_with 202202L # define __cpp_lib_ranges_repeat 202207L diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc --- a/libcxx/modules/std/ranges.inc +++ b/libcxx/modules/std/ranges.inc @@ -309,6 +309,7 @@ namespace views { using std::ranges::views::slide; } +#endif // [range.chunk.by], chunk by view using std::ranges::chunk_by_view; @@ -317,6 +318,7 @@ using std::ranges::views::chunk_by; } +#if 0 // [range.stride], stride view using std::ranges::stride_view; diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/adaptor.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/adaptor.nodiscard.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/adaptor.nodiscard.verify.cpp @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// Test the libc++ extension that std::views::chunk_by is marked as [[nodiscard]]. + +#include +#include + +void test() { + int range[] = {1, 2, 3, 0, 1, 2}; + std::ranges::less_equal pred; + + std::views::chunk_by(pred); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::views::chunk_by(range, pred); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + range | std::views::chunk_by(pred); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + std::views::all | std::views::chunk_by(pred); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/assert.begin.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/assert.begin.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/assert.begin.pass.cpp @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// 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: has-unix-headers +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: no-exceptions +// UNSUPPORTED: !libcpp-hardening-mode=debug +// XFAIL: availability-verbose_abort-missing + +// + +// Call begin() on chunk_by_view with empty predicate + +#include + +#include "check_assertion.h" +#include "types.h" + +int main(int, char**) { + int input[] = {1, 2, 3}; + auto view1 = std::views::chunk_by(input, ThrowOnCopyPred{}); + auto view2 = std::views::chunk_by(input, ThrowOnCopyPred{}); + try { + view1 = view2; + } catch (...) { + } + TEST_LIBCPP_ASSERT_FAILURE( + view1.begin(), "Trying to call begin() on a chunk_by_view that does not have a valid predicate."); + return 0; +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/assert.find-next.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/assert.find-next.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/assert.find-next.pass.cpp @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// 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: has-unix-headers +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: no-exceptions +// UNSUPPORTED: !libcpp-hardening-mode=debug +// XFAIL: availability-verbose_abort-missing + +// + +// Call find-next() on chunk_by_view with empty predicate + +#include + +#include "check_assertion.h" +#include "types.h" + +int main(int, char**) { + int input[] = {1, 2, 3}; + // This is the easiest way to get '__find_next' to fail. If we used default constructed view here, + // then begin() would fail instead of __find_next. + auto view1 = std::views::chunk_by(input, ThrowOnCopyPred{}); + auto view2 = std::views::chunk_by(input, ThrowOnCopyPred{}); + auto it = view1.begin(); + try { + view1 = view2; + } catch (...) { + } + TEST_LIBCPP_ASSERT_FAILURE( + ++it, "Trying to call __find_next() on a chunk_by_view that does not have a valid predicate."); + return 0; +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/assert.find-prev.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/assert.find-prev.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/assert.find-prev.pass.cpp @@ -0,0 +1,49 @@ +//===----------------------------------------------------------------------===// +// +// 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: has-unix-headers +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: no-exceptions +// UNSUPPORTED: !libcpp-hardening-mode=debug +// XFAIL: availability-verbose_abort-missing + +// + +// Call find-prev() on chunk_by_view with begin iterator +// Call find-prev() on chunk_by_view with empty predicate + +#include +#include + +#include "check_assertion.h" +#include "types.h" + +int main(int, char**) { + int input[] = {1, 1, 2, 2}; + + { // Call find-prev() on chunk_by_view with begin iterator + auto view = std::views::chunk_by(input, std::equal_to{}); + auto it = view.begin(); + TEST_LIBCPP_ASSERT_FAILURE(--it, "Trying to call __find_prev() on a begin iterator."); + } + + { // Call find-prev() on chunk_by_view with empty predicate + auto view1 = std::views::chunk_by(input, ThrowOnCopyPred{}); + auto view2 = std::views::chunk_by(input, ThrowOnCopyPred{}); + auto it = view1.begin(); + ++it; + try { + view1 = view2; + } catch (...) { + } + TEST_LIBCPP_ASSERT_FAILURE( + --it, "Trying to call __find_prev() on a chunk_by_view that does not have a valid predicate."); + } + + return 0; +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/assert.deref.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/assert.deref.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/assert.deref.pass.cpp @@ -0,0 +1,30 @@ +//===----------------------------------------------------------------------===// +// +// 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: has-unix-headers +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: !libcpp-hardening-mode=debug +// XFAIL: availability-verbose_abort-missing + +// + +// Dereference past end chunk_by_view iterator + +#include +#include + +#include "check_assertion.h" + +int main(int, char**) { + int input[] = {1, 2, 3}; + auto view = std::views::chunk_by(input, std::less{}); + auto it = view.begin(); + ++it; + TEST_LIBCPP_ASSERT_FAILURE(*it, "Trying to dereference past-the-end chunk_by_view iterator."); + return 0; +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/assert.increment.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/assert.increment.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/assert.increment.pass.cpp @@ -0,0 +1,30 @@ +//===----------------------------------------------------------------------===// +// +// 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: has-unix-headers +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: !libcpp-hardening-mode=debug +// XFAIL: availability-verbose_abort-missing + +// + +// Increment past end chunk_by_view iterator + +#include +#include + +#include "check_assertion.h" + +int main(int, char**) { + int input[] = {1, 2, 3}; + auto view = std::views::chunk_by(input, std::less{}); + auto it = view.begin(); + ++it; + TEST_LIBCPP_ASSERT_FAILURE(++it, "Trying to increment past end chunk_by_view iterator."); + return 0; +} diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/types.h b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/types.h new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.chunk.by/types.h @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// 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_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_BY_TYPES_H +#define TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_BY_TYPES_H + +struct ThrowOnCopyPred { + ThrowOnCopyPred() = default; + ThrowOnCopyPred(const ThrowOnCopyPred&) { throw 0; } + ThrowOnCopyPred& operator=(const ThrowOnCopyPred&) = delete; + + ThrowOnCopyPred(ThrowOnCopyPred&&) = default; + ThrowOnCopyPred& operator=(ThrowOnCopyPred&&) = default; + + bool operator()(int x, int y) const { return x != y; } +}; + +#endif // TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_BY_TYPES_H diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv --- a/libcxx/test/libcxx/transitive_includes/cxx03.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv @@ -687,11 +687,11 @@ ranges iosfwd ranges iterator ranges limits +ranges new ranges optional ranges span ranges tuple ranges type_traits -ranges variant ranges version ratio climits ratio cstdint diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv --- a/libcxx/test/libcxx/transitive_includes/cxx11.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv @@ -692,11 +692,11 @@ ranges iosfwd ranges iterator ranges limits +ranges new ranges optional ranges span ranges tuple ranges type_traits -ranges variant ranges version ratio climits ratio cstdint diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv --- a/libcxx/test/libcxx/transitive_includes/cxx14.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv @@ -694,11 +694,11 @@ ranges iosfwd ranges iterator ranges limits +ranges new ranges optional ranges span ranges tuple ranges type_traits -ranges variant ranges version ratio climits ratio cstdint diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv --- a/libcxx/test/libcxx/transitive_includes/cxx17.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv @@ -694,11 +694,11 @@ ranges iosfwd ranges iterator ranges limits +ranges new ranges optional ranges span ranges tuple ranges type_traits -ranges variant ranges version ratio climits ratio cstdint diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv --- a/libcxx/test/libcxx/transitive_includes/cxx20.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv @@ -700,11 +700,11 @@ ranges iosfwd ranges iterator ranges limits +ranges new ranges optional ranges span ranges tuple ranges type_traits -ranges variant ranges version ratio climits ratio cstdint diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv --- a/libcxx/test/libcxx/transitive_includes/cxx23.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv @@ -499,6 +499,7 @@ ranges iosfwd ranges iterator ranges limits +ranges new ranges optional ranges span ranges tuple diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv --- a/libcxx/test/libcxx/transitive_includes/cxx26.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv @@ -499,6 +499,7 @@ ranges iosfwd ranges iterator ranges limits +ranges new ranges optional ranges span ranges tuple 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 --- 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 @@ -214,17 +214,11 @@ # endif # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_chunk_by -# error "__cpp_lib_ranges_chunk_by should be defined in c++23" -# endif -# if __cpp_lib_ranges_chunk_by != 202202L -# error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++23" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_ranges_chunk_by -# error "__cpp_lib_ranges_chunk_by should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_chunk_by +# error "__cpp_lib_ranges_chunk_by should be defined in c++23" +# endif +# if __cpp_lib_ranges_chunk_by != 202202L +# error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++23" # endif # if !defined(_LIBCPP_VERSION) @@ -309,17 +303,11 @@ # endif # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_chunk_by -# error "__cpp_lib_ranges_chunk_by should be defined in c++26" -# endif -# if __cpp_lib_ranges_chunk_by != 202202L -# error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++26" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_ranges_chunk_by -# error "__cpp_lib_ranges_chunk_by should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_chunk_by +# error "__cpp_lib_ranges_chunk_by should be defined in c++26" +# endif +# if __cpp_lib_ranges_chunk_by != 202202L +# error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++26" # endif # if !defined(_LIBCPP_VERSION) 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 --- 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 @@ -4971,17 +4971,11 @@ # endif # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_chunk_by -# error "__cpp_lib_ranges_chunk_by should be defined in c++23" -# endif -# if __cpp_lib_ranges_chunk_by != 202202L -# error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++23" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_ranges_chunk_by -# error "__cpp_lib_ranges_chunk_by should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_chunk_by +# error "__cpp_lib_ranges_chunk_by should be defined in c++23" +# endif +# if __cpp_lib_ranges_chunk_by != 202202L +# error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++23" # endif # if !defined(_LIBCPP_VERSION) @@ -6518,17 +6512,11 @@ # endif # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_ranges_chunk_by -# error "__cpp_lib_ranges_chunk_by should be defined in c++26" -# endif -# if __cpp_lib_ranges_chunk_by != 202202L -# error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++26" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_ranges_chunk_by -# error "__cpp_lib_ranges_chunk_by should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_ranges_chunk_by +# error "__cpp_lib_ranges_chunk_by should be defined in c++26" +# endif +# if __cpp_lib_ranges_chunk_by != 202202L +# error "__cpp_lib_ranges_chunk_by should have the value 202202L in c++26" # endif # if !defined(_LIBCPP_VERSION) diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/adaptor.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/adaptor.pass.cpp @@ -0,0 +1,237 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// std::views::chunk_by + +#include + +#include +#include +#include +#include +#include +#include + +#include "test_iterators.h" + +template +concept CanBePiped = requires(View&& view, T&& t) { + { std::forward(view) | std::forward(t) }; +}; + +struct Pred { + constexpr bool operator()(int x, int y) const { return x != -y; } +}; + +struct NonCopyablePredicate : Pred { + NonCopyablePredicate(NonCopyablePredicate const&) = delete; +}; + +struct Range : std::ranges::view_base { + using Iterator = forward_iterator; + using Sentinel = sentinel_wrapper; + constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) {} + constexpr Iterator begin() const { return Iterator(begin_); } + constexpr Sentinel end() const { return Sentinel(Iterator(end_)); } + +private: + int* begin_; + int* end_; +}; + +template +constexpr void compareViews(View v, std::initializer_list> list) { + auto b1 = v.begin(); + auto e1 = v.end(); + auto b2 = list.begin(); + auto e2 = list.end(); + for (; b1 != e1 && b2 != e2; ++b1, ++b2) { + bool eq = std::ranges::equal(*b1, *b2, [](int x, int y) { + assert(x == y); + return true; + }); + assert(eq); + } + assert(b1 == e1); + assert(b2 == e2); +} + +constexpr int absoluteValue(int x) { return x < 0 ? -x : x; } + +template +constexpr const T&& asConstRvalue(T&& t) { + return static_cast(t); +} + +constexpr bool test() { + int buff[] = {-4, -3, -2, -1, 1, 2, 3, 4}; + + // Test range adaptor object + { + using RangeAdaptorObject = decltype(std::views::chunk_by); + static_assert(std::is_const_v); + + // The type of a customization point object, ignoring cv-qualifiers, shall model semiregular + static_assert(std::semiregular>); + } + + // Test `views::chunk_by(pred)(v)` + { + using Result = std::ranges::chunk_by_view; + Range const range(buff, buff + 8); + Pred pred; + + { + // 'views::chunk_by(pred)' - && + std::same_as decltype(auto) result = std::views::chunk_by(pred)(range); + compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}}); + } + { + // 'views::chunk_by(pred)' - const&& + std::same_as decltype(auto) result = asConstRvalue(std::views::chunk_by(pred))(range); + compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}}); + } + { + // 'views::chunk_by(pred)' - & + auto partial = std::views::chunk_by(pred); + std::same_as decltype(auto) result = partial(range); + compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}}); + } + { + // 'views::chunk_by(pred)' - const& + auto const partial = std::views::chunk_by(pred); + std::same_as decltype(auto) result = partial(range); + compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}}); + } + } + + // Test `v | views::chunk_by(pred)` + { + using Result = std::ranges::chunk_by_view; + Range const range(buff, buff + 8); + Pred pred; + + { + // 'views::chunk_by(pred)' - && + std::same_as decltype(auto) result = range | std::views::chunk_by(pred); + compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}}); + } + { + // 'views::chunk_by(pred)' - const&& + std::same_as decltype(auto) result = range | asConstRvalue(std::views::chunk_by(pred)); + compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}}); + } + { + // 'views::chunk_by(pred)' - & + auto partial = std::views::chunk_by(pred); + std::same_as decltype(auto) result = range | partial; + compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}}); + } + { + // 'views::chunk_by(pred)' - const& + auto const partial = std::views::chunk_by(pred); + std::same_as decltype(auto) result = range | partial; + compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}}); + } + } + + // Test `views::chunk_by(v, pred)` range adaptor object + { + using Result = std::ranges::chunk_by_view; + Range const range(buff, buff + 8); + Pred pred; + + { + // 'views::chunk_by' - && + auto range_adaptor = std::views::chunk_by; + std::same_as decltype(auto) result = std::move(range_adaptor)(range, pred); + compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}}); + } + { + // 'views::chunk_by' - const&& + auto const range_adaptor = std::views::chunk_by; + std::same_as decltype(auto) result = std::move(range_adaptor)(range, pred); + compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}}); + } + { + // 'views::chunk_by' - & + auto range_adaptor = std::views::chunk_by; + std::same_as decltype(auto) result = range_adaptor(range, pred); + compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}}); + } + { + // 'views::chunk_by' - const& + auto const range_adaptor = std::views::chunk_by; + std::same_as decltype(auto) result = range_adaptor(range, pred); + compareViews(result, {{-4, -3, -2, -1}, {1, 2, 3, 4}}); + } + } + + // Test that one can call std::views::chunk_by with arbitrary stuff, as long as we + // don't try to actually complete the call by passing it a range. + // + // That makes no sense and we can't do anything with the result, but it's valid. + { + int array[3] = {1, 2, 3}; + [[maybe_unused]] auto partial = std::views::chunk_by(std::move(array)); + } + + // Test `adaptor | views::chunk_by(pred)` + { + Range const range(buff, buff + 8); + + { + auto pred1 = [](int i) { return absoluteValue(i) < 3; }; + Pred pred2; + using Result = std::ranges::chunk_by_view, Pred>; + std::same_as decltype(auto) result = range | std::views::filter(pred1) | std::views::chunk_by(pred2); + compareViews(result, {{-2, -1}, {1, 2}}); + } + { + auto pred1 = [](int i) { return absoluteValue(i) < 3; }; + Pred pred2; + using Result = std::ranges::chunk_by_view, Pred>; + auto const partial = std::views::filter(pred1) | std::views::chunk_by(pred2); + std::same_as decltype(auto) result = range | partial; + compareViews(result, {{-2, -1}, {1, 2}}); + } + } + + // Test SFINAE friendliness + { + struct NotAView {}; + struct NotInvocable {}; + + static_assert(!CanBePiped); + static_assert(CanBePiped); + static_assert(!CanBePiped); + static_assert(!CanBePiped, decltype(std::views::chunk_by(Pred{}))>); + static_assert(!CanBePiped); + + static_assert(!std::is_invocable_v); + static_assert(!std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(!std::is_invocable_v); + static_assert(!std::is_invocable_v); + } + + { static_assert(std::is_same_v); } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/base.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/base.pass.cpp @@ -0,0 +1,99 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// constexpr View base() const& requires copy_constructible; +// constexpr View base() &&; + +#include + +#include +#include +#include + +struct Range : std::ranges::view_base { + constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) {} + constexpr Range(Range const& other) : begin_(other.begin_), end_(other.end_), wasCopyInitialized(true) {} + constexpr Range(Range&& other) : begin_(other.begin_), end_(other.end_), wasMoveInitialized(true) {} + Range& operator=(Range const&) = default; + Range& operator=(Range&&) = default; + constexpr int* begin() const { return begin_; } + constexpr int* end() const { return end_; } + + int* begin_; + int* end_; + bool wasCopyInitialized = false; + bool wasMoveInitialized = false; +}; + +static_assert(std::ranges::view); +static_assert(std::ranges::forward_range); + +struct Pred { + bool operator()(int, int) const; +}; + +struct NonCopyableRange : std::ranges::view_base { + explicit NonCopyableRange(int*, int*); + NonCopyableRange(NonCopyableRange const&) = delete; + NonCopyableRange(NonCopyableRange&&) = default; + NonCopyableRange& operator=(NonCopyableRange const&) = default; + NonCopyableRange& operator=(NonCopyableRange&&) = default; + int* begin() const; + int* end() const; +}; + +static_assert(!std::copy_constructible); + +template +concept CanCallBaseOn = requires(T t) { std::forward(t).base(); }; + +constexpr bool test() { + int buff[] = {1, 2, 3, 4}; + + // Check the const& overload + { + Range range(buff, buff + 4); + std::ranges::chunk_by_view const view(range, Pred{}); + std::same_as decltype(auto) result = view.base(); + assert(result.wasCopyInitialized); + assert(result.begin() == buff); + assert(result.end() == buff + 4); + } + + // Check the && overload + { + Range range(buff, buff + 4); + std::ranges::chunk_by_view view(range, Pred{}); + std::same_as decltype(auto) result = std::move(view).base(); + assert(result.wasMoveInitialized); + assert(result.begin() == buff); + assert(result.end() == buff + 4); + } + + // Ensure the const& overload is not considered when the base is not copy-constructible + { + static_assert(!CanCallBaseOn const&>); + static_assert(!CanCallBaseOn&>); + static_assert(!CanCallBaseOn const&&>); + static_assert(CanCallBaseOn&&>); + static_assert(CanCallBaseOn>); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/begin.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/begin.pass.cpp @@ -0,0 +1,156 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// constexpr iterator begin(); + +#include + +#include +#include + +#include "test_iterators.h" +#include "types.h" + +struct Range : std::ranges::view_base { + using Iterator = forward_iterator; + using Sentinel = sentinel_wrapper; + constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) {} + constexpr Iterator begin() const { return Iterator(begin_); } + constexpr Sentinel end() const { return Sentinel(Iterator(end_)); } + +private: + int* begin_; + int* end_; +}; + +struct TrackingPred : TrackInitialization { + using TrackInitialization::TrackInitialization; + constexpr bool operator()(int x, int y) { return x != -y; } +}; + +template +concept HasBegin = requires(T t) { t.begin(); }; + +static_assert(HasBegin>); +static_assert(!HasBegin>); + +constexpr bool test() { + int buff[] = {-4, -3, -2, -1, 1, 2, 3, 4}; + + // Check the return type of `begin()` + { + Range range(buff, buff + 1); + auto pred = [](int, int) { return true; }; + std::ranges::chunk_by_view view(range, pred); + using ChunkByIterator = std::ranges::iterator_t; + ASSERT_SAME_TYPE(ChunkByIterator, decltype(view.begin())); + } + + // begin() over an empty range + { + Range range(buff, buff); + auto pred = [](int, int) { return true; }; + std::ranges::chunk_by_view view(range, pred); + auto it = view.begin(); + assert(it == view.begin()); + assert(it == view.end()); + } + + // begin() over a 1-element range + { + Range range(buff, buff + 1); + auto pred = [](int x, int y) { return x == y; }; + std::ranges::chunk_by_view view(range, pred); + auto it = view.begin(); + assert(base((*it).begin()) == buff); + assert(base((*it).end()) == buff + 1); + } + + // begin() over a 2-element range + { + Range range(buff, buff + 2); + auto pred = [](int x, int y) { return x == y; }; + std::ranges::chunk_by_view view(range, pred); + auto it = view.begin(); + assert(base((*it).begin()) == buff); + assert(base((*it).end()) == buff + 1); + assert(base((*++it).begin()) == buff + 1); + assert(base((*it).end()) == buff + 2); + } + + // begin() over a longer range + { + Range range(buff, buff + 8); + auto pred = [](int x, int y) { return x != -y; }; + std::ranges::chunk_by_view view(range, pred); + auto it = view.begin(); + assert(base((*it).end()) == buff + 4); + } + + // Make sure we do not make a copy of the predicate when we call begin() + // (we should be passing it to ranges::adjacent_find using std::ref) + { + bool moved = false, copied = false; + Range range(buff, buff + 2); + std::ranges::chunk_by_view view(range, TrackingPred(&moved, &copied)); + std::exchange(moved, false); + [[maybe_unused]] auto it = view.begin(); + assert(!moved); + assert(!copied); + } + + // Test with a non-const predicate + { + Range range(buff, buff + 8); + auto pred = [](int x, int y) mutable { return x != -y; }; + std::ranges::chunk_by_view view(range, pred); + auto it = view.begin(); + assert(base((*it).end()) == buff + 4); + } + + // Test with a predicate that takes by non-const reference + { + Range range(buff, buff + 8); + auto pred = [](int& x, int& y) { return x != -y; }; + std::ranges::chunk_by_view view(range, pred); + auto it = view.begin(); + assert(base((*it).end()) == buff + 4); + } + + // Test caching + { + // Make sure that we cache the result of begin() on subsequent calls + Range range(buff, buff + 8); + int called = 0; + auto pred = [&](int x, int y) { + ++called; + return x != -y; + }; + + std::ranges::chunk_by_view view(range, pred); + assert(called == 0); + for (int k = 0; k != 3; ++k) { + auto it = view.begin(); + assert(base((*it).end()) == buff + 4); + assert(called == 4); // 4, because the cached iterator is 'buff + 4' (end of the first chunk) + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/constraints.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/constraints.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/constraints.compile.pass.cpp @@ -0,0 +1,121 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// Check constraints on the type itself. +// +// template , iterator_t> Pred> +// requires view && is_object_v +// class chunk_by_view; + +#include + +#include +#include +#include +#include + +#include "almost_satisfies_types.h" +#include "test_iterators.h" + +template +concept CanFormChunkByView = requires { typename std::ranges::chunk_by_view; }; + +// chunk_by_view is not valid when the view is not a forward_range +namespace test_when_view_is_not_a_forward_range { + +struct View : std::ranges::view_base { + ForwardIteratorNotDerivedFrom begin() const; + ForwardIteratorNotDerivedFrom end() const; +}; +struct Pred { + bool operator()(int, int) const; +}; + +static_assert(!std::ranges::forward_range); +static_assert(std::indirect_binary_predicate); +static_assert(std::ranges::view); +static_assert(std::is_object_v); +static_assert(!CanFormChunkByView); + +} // namespace test_when_view_is_not_a_forward_range + +// chunk_by_view is not valid when the predicate is not indirect_binary_predicate +namespace test_when_the_predicate_is_not_indirect_binary_predicate { + +struct View : std::ranges::view_base { + int* begin() const; + int* end() const; +}; +struct Pred {}; + +static_assert(std::ranges::forward_range); +static_assert(!std::indirect_binary_predicate); +static_assert(std::ranges::view); +static_assert(std::is_object_v); +static_assert(!CanFormChunkByView); + +} // namespace test_when_the_predicate_is_not_indirect_binary_predicate + +// chunk_by_view is not valid when the view is not a view +namespace test_when_the_view_param_is_not_a_view { + +struct View { + int* begin() const; + int* end() const; +}; +struct Pred { + bool operator()(int, int) const; +}; + +static_assert(std::ranges::input_range); +static_assert(std::indirect_binary_predicate); +static_assert(!std::ranges::view); +static_assert(std::is_object_v); +static_assert(!CanFormChunkByView); + +} // namespace test_when_the_view_param_is_not_a_view + +// chunk_by_view is not valid when the predicate is not an object type +namespace test_when_the_predicate_is_not_an_object_type { + +struct View : std::ranges::view_base { + int* begin() const; + int* end() const; +}; +using Pred = bool (&)(int, int); + +static_assert(std::ranges::input_range); +static_assert(std::indirect_binary_predicate); +static_assert(std::ranges::view); +static_assert(!std::is_object_v); +static_assert(!CanFormChunkByView); + +} // namespace test_when_the_predicate_is_not_an_object_type + +// chunk_by_view is valid when all the constraints are satisfied (test the test) +namespace test_when_all_the_constraints_are_satisfied { + +struct View : std::ranges::view_base { + int* begin() const; + int* end() const; +}; +struct Pred { + bool operator()(int, int) const; +}; + +static_assert(std::ranges::input_range); +static_assert(std::indirect_binary_predicate); +static_assert(std::ranges::view); +static_assert(std::is_object_v); +static_assert(CanFormChunkByView); + +} // namespace test_when_all_the_constraints_are_satisfied diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/ctad.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/ctad.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/ctad.pass.cpp @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// template +// chunk_by_view(Range&&, Pred) -> chunk_by_view, Pred>; + +#include + +#include +#include + +#include "test_iterators.h" + +struct View : std::ranges::view_base { + View() = default; + forward_iterator begin() const; + sentinel_wrapper> end() const; +}; +static_assert(std::ranges::view); + +// A range that is not a view +struct Range { + Range() = default; + forward_iterator begin() const; + sentinel_wrapper> end() const; +}; +static_assert(std::ranges::range); +static_assert(!std::ranges::view); + +struct Pred { + constexpr bool operator()(int x, int y) const { return x <= y; } +}; + +constexpr bool test() { + { + View v; + Pred pred; + std::ranges::chunk_by_view view(v, pred); + static_assert(std::is_same_v>); + } + { + Range r; + Pred pred; + std::ranges::chunk_by_view view(r, pred); + static_assert(std::is_same_v, Pred>>); + } + { + Pred pred; + std::ranges::chunk_by_view view(Range{}, pred); + static_assert(std::is_same_v, Pred>>); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/ctor.default.pass.cpp @@ -0,0 +1,132 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// chunk_by_view() requires std::default_initializable && +// std::default_initializable = default; + +#include + +#include +#include + +constexpr int buff[] = {-2, 1, -1, 2}; + +struct DefaultConstructibleView : std::ranges::view_base { + DefaultConstructibleView() = default; + constexpr int const* begin() const { return buff; } + constexpr int const* end() const { return buff + 4; } +}; + +struct DefaultConstructiblePredicate { + DefaultConstructiblePredicate() = default; + constexpr bool operator()(int x, int y) const { return x != -y; } +}; + +struct NoDefaultView : std::ranges::view_base { + NoDefaultView() = delete; + int* begin() const; + int* end() const; +}; + +struct NoDefaultPredicate { + NoDefaultPredicate() = delete; + constexpr bool operator()(int, int) const; +}; + +struct NoexceptView : std::ranges::view_base { + NoexceptView() noexcept; + int const* begin() const; + int const* end() const; +}; + +struct NoexceptPredicate { + NoexceptPredicate() noexcept; + bool operator()(int, int) const; +}; + +struct MayThrowView : std::ranges::view_base { + MayThrowView() noexcept(false); + int const* begin() const; + int const* end() const; +}; + +struct MayThrowPredicate { + MayThrowPredicate() noexcept(false); + bool operator()(int, int) const; +}; + +constexpr void compareRanges(std::ranges::subrange v, std::initializer_list list) { + assert(v.size() == list.size()); + for (size_t i = 0; i < v.size(); ++i) { + assert(v[i] == list.begin()[i]); + } +} + +constexpr bool test() { + // Check default constructor with default initialization + { + using View = std::ranges::chunk_by_view; + View view; + auto it = view.begin(), end = view.end(); + compareRanges(*it++, {-2, 1}); + compareRanges(*it++, {-1, 2}); + assert(it == end); + } + + // Check default construction with copy-list-initialization + { + using View = std::ranges::chunk_by_view; + View view = {}; + auto it = view.begin(), end = view.end(); + compareRanges(*it++, {-2, 1}); + compareRanges(*it++, {-1, 2}); + assert(it == end); + } + + // Check cases where the default constructor isn't provided + { + static_assert( + !std::is_default_constructible_v>); + static_assert( + !std::is_default_constructible_v>); + static_assert(!std::is_default_constructible_v>); + } + + // Check noexcept-ness + { + { + using View = std::ranges::chunk_by_view; + static_assert(!noexcept(View())); + } + { + using View = std::ranges::chunk_by_view; + static_assert(!noexcept(View())); + } + { + using View = std::ranges::chunk_by_view; + static_assert(!noexcept(View())); + } + { + using View = std::ranges::chunk_by_view; + static_assert(noexcept(View())); + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/ctor.view_pred.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/ctor.view_pred.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/ctor.view_pred.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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// constexpr chunk_by_view(View, Pred); + +#include + +#include +#include +#include +#include + +#include "types.h" + +struct Range : std::ranges::view_base { + constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) {} + constexpr int* begin() const { return begin_; } + constexpr int* end() const { return end_; } + +private: + int* begin_; + int* end_; +}; + +static_assert(std::ranges::view); +static_assert(std::ranges::forward_range); + +struct Pred { + constexpr bool operator()(int x, int y) const { return x <= y; } +}; + +struct TrackingPred : TrackInitialization { + using TrackInitialization::TrackInitialization; + constexpr bool operator()(int&, int&) const; +}; + +struct TrackingRange : TrackInitialization, std::ranges::view_base { + using TrackInitialization::TrackInitialization; + int* begin() const; + int* end() const; +}; + +template +void implicitConstructionTest(T); + +template +concept ImplicitConstructibleFrom = + requires(Args&&... args) { implicitConstructionTest({std::forward(args)...}); }; + +constexpr bool test() { + int buff[] = {1, 2, 3, 0, 1, 2, -1, -1, 0}; + + // Test explicit syntax + { + Range range(buff, buff + 9); + Pred pred; + std::ranges::chunk_by_view view(range, pred); + auto it = view.begin(), end = view.end(); + assert(std::ranges::equal(*it++, std::array{1, 2, 3})); + assert(std::ranges::equal(*it++, std::array{0, 1, 2})); + assert(std::ranges::equal(*it++, std::array{-1, -1, 0})); + assert(it == end); + } + + // Test implicit syntax + { + using ChunkByView = std::ranges::chunk_by_view; + static_assert(!ImplicitConstructibleFrom); + static_assert(!ImplicitConstructibleFrom); + } + + // Make sure we move the view + { + bool moved = false, copied = false; + TrackingRange range(&moved, &copied); + Pred pred; + [[maybe_unused]] std::ranges::chunk_by_view view(std::move(range), pred); + assert(moved); + assert(!copied); + } + + // Make sure we move the predicate + { + bool moved = false, copied = false; + Range range(buff, buff + 9); + TrackingPred pred(&moved, &copied); + [[maybe_unused]] std::ranges::chunk_by_view view(range, std::move(pred)); + assert(moved); + assert(!copied); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/end.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/end.pass.cpp @@ -0,0 +1,121 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// constexpr auto end(); + +#include + +#include +#include +#include + +#include "test_iterators.h" + +struct NonCommonRange : std::ranges::view_base { + using Iterator = forward_iterator; + using Sentinel = sentinel_wrapper; + constexpr explicit NonCommonRange(int* b, int* e) : begin_(b), end_(e) {} + constexpr Iterator begin() const { return Iterator(begin_); } + constexpr Sentinel end() const { return Sentinel(Iterator(end_)); } + +private: + int* begin_; + int* end_; +}; + +static_assert(std::ranges::forward_range); +static_assert(!std::ranges::common_range); + +struct CommonRange : std::ranges::view_base { + using Iterator = bidirectional_iterator; + constexpr explicit CommonRange(int* b, int* e) : begin_(b), end_(e) {} + constexpr Iterator begin() const { return Iterator(begin_); } + constexpr Iterator end() const { return Iterator(end_); } + +private: + int* begin_; + int* end_; +}; + +static_assert(std::ranges::bidirectional_range); +static_assert(std::ranges::common_range); + +constexpr bool test() { + int buff[] = {1, 0, 3, 1, 2, 3, 4, 5}; + + // Check the return type of `end()` + { + CommonRange range(buff, buff + 1); + auto pred = [](int, int) { return true; }; + std::ranges::chunk_by_view view(range, pred); + using ChunkByView = decltype(view); + static_assert(std::ranges::common_range); + ASSERT_SAME_TYPE(std::ranges::sentinel_t, decltype(view.end())); + } + + // end() on an empty range + { + CommonRange range(buff, buff); + auto pred = [](int x, int y) { return x <= y; }; + std::ranges::chunk_by_view view(range, pred); + auto end = view.end(); + assert(end == std::default_sentinel); + } + + // end() on a 1-element range + { + CommonRange range(buff, buff + 1); + auto pred = [](int& x, int& y) { return x <= y; }; + std::ranges::chunk_by_view view(range, pred); + auto end = view.end(); + assert(base((*--end).begin()) == buff); + assert(base((*end).end()) == buff + 1); + } + + // end() on a 2-element range + { + CommonRange range(buff, buff + 2); + auto pred = [](int const& x, int const& y) { return x <= y; }; + std::ranges::chunk_by_view view(range, pred); + auto end = view.end(); + assert(base((*--end).begin()) == buff + 1); + assert(base((*--end).end()) == buff + 1); + } + + // end() on a 8-element range + { + CommonRange range(buff, buff + 8); + auto pred = [](const int x, const int y) { return x < y; }; + std::ranges::chunk_by_view view(range, pred); + auto end = view.end(); + assert(base((*--end).end()) == buff + 8); + assert(base((*--end).end()) == buff + 3); + } + + // end() on a non-common range + { + NonCommonRange range(buff, buff + 1); + std::ranges::chunk_by_view view(range, std::ranges::less_equal{}); + auto end = view.end(); + ASSERT_SAME_TYPE(std::default_sentinel_t, std::ranges::sentinel_t); + ASSERT_SAME_TYPE(std::default_sentinel_t, decltype(end)); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/pred.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/pred.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/pred.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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// constexpr Pred const& pred() const; + +#include + +#include +#include + +struct Range : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +struct Pred { + bool operator()(int, int) const; + int value; +}; + +constexpr bool test() { + { + Pred pred{42}; + std::ranges::chunk_by_view const view(Range{}, pred); + std::same_as decltype(auto) result = view.pred(); + assert(result.value == 42); + + // Make sure we're really holding a reference to something inside the view + assert(&result == &view.pred()); + } + + // Same, but calling on a non-const view + { + Pred pred{42}; + std::ranges::chunk_by_view view(Range{}, pred); + std::same_as decltype(auto) result = view.pred(); + assert(result.value == 42); + + // Make sure we're really holding a reference to something inside the view + assert(&result == &view.pred()); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/compare.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/compare.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/compare.pass.cpp @@ -0,0 +1,113 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// friend constexpr bool operator==(const iterator& x, const iterator& y); +// friend constexpr bool operator==(const iterator& x, default_sentinel_t); + +#include + +#include +#include +#include +#include +#include + +#include "../types.h" +#include "test_iterators.h" +#include "test_macros.h" + +template > +constexpr void test() { + using Underlying = View; + using ChunkByView = std::ranges::chunk_by_view; + using ChunkByIterator = std::ranges::iterator_t; + + auto make_chunk_by_view = [](auto begin, auto end) { + View view{Iterator(begin), Sentinel(Iterator(end))}; + return ChunkByView(std::move(view), std::ranges::less_equal{}); + }; + + // Test operator== + { + std::array array{0, 1, 2}; + ChunkByView view = make_chunk_by_view(array.begin(), array.end()); + ChunkByIterator i = view.begin(); + ChunkByIterator j = view.begin(); + + std::same_as decltype(auto) result = (i == j); + assert(result); + ++i; + assert(!(i == j)); + } + + // Test synthesized operator!= + { + std::array array{0, 1, 2}; + ChunkByView view = make_chunk_by_view(array.begin(), array.end()); + ChunkByIterator i = view.begin(); + ChunkByIterator j = view.begin(); + + std::same_as decltype(auto) result = (i != j); + assert(!result); + ++i; + assert(i != j); + } + + // Test operator== with std::default_sentinel_t + { + std::array array{0, 1, 2}; + ChunkByView view = make_chunk_by_view(array.begin(), array.end()); + ChunkByIterator i = view.begin(); + + std::same_as decltype(auto) result = (i == std::default_sentinel); + assert(!result); + ++i; + assert(i == std::default_sentinel); + } + + // Test synthesized operator!= with std::default_sentinel_t + { + std::array array{0, 1, 2}; + ChunkByView view = make_chunk_by_view(array.begin(), array.end()); + ChunkByIterator i = view.begin(); + + std::same_as decltype(auto) result = (i != std::default_sentinel); + assert(result); + ++i; + assert(!(i != std::default_sentinel)); + } +} + +struct TestWithPair { + template + constexpr void operator()() const { + // Test with pair of iterators + test(); + + // Test with iterator-sentinel pair + test(); + } +}; + +constexpr bool tests() { + TestWithPair tester; + types::for_each(types::forward_iterator_list{}, tester); + types::for_each(types::forward_iterator_list{}, tester); + + return true; +} + +int main(int, char**) { + tests(); + static_assert(tests()); + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/ctor.default.pass.cpp @@ -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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// std::ranges::chunk_by_view::() = default; + +#include + +#include +#include +#include +#include + +#include "../types.h" +#include "test_iterators.h" + +template +constexpr void testDefaultConstructible() { + // Make sure the iterator is default constructible. + using ChunkByView = std::ranges::chunk_by_view, std::ranges::less_equal>; + using ChunkByIterator = std::ranges::iterator_t; + ChunkByIterator i{}; + ChunkByIterator j; + assert(i == j); + static_assert(noexcept(ChunkByIterator{}) == IsNoexcept); +} + +constexpr bool tests() { + testDefaultConstructible, /*IsNoexcept=*/false>(); + testDefaultConstructible, /*IsNoexcept=*/false>(); + testDefaultConstructible, /*IsNoexcept=*/false>(); + testDefaultConstructible, /*IsNoexcept=*/false>(); + testDefaultConstructible(); + return true; +} + +int main(int, char**) { + tests(); + static_assert(tests()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/decrement.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/decrement.pass.cpp @@ -0,0 +1,221 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// constexpr iterator& operator--(); +// constexpr iterator operator--(int); + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../types.h" +#include "test_iterators.h" +#include "test_macros.h" + +template +concept HasPreDecrement = requires(T t) { + { --t }; +}; + +template +concept HasPostDecrement = requires(T t) { + { t-- }; +}; + +struct TrackingPred : TrackInitialization { + using TrackInitialization::TrackInitialization; + constexpr bool operator()(int x, int y) const { return x <= y; } +}; + +template > +constexpr void test() { + using Underlying = View; + using ChunkByView = std::ranges::chunk_by_view; + using ChunkByIterator = std::ranges::iterator_t; + + static_assert(HasPostDecrement); + static_assert(HasPreDecrement); + + auto make_chunk_by_view = [](auto begin, auto end) { + View view{Iterator{begin}, Sentinel{Iterator{end}}}; + return ChunkByView{std::move(view), std::ranges::less_equal{}}; + }; + + // Test with a single chunk + { + std::array array{0, 1, 2, 3, 4}; + ChunkByView view = make_chunk_by_view(array.begin(), array.end()); + ChunkByIterator it = std::ranges::next(view.begin(), view.end()); + + std::same_as decltype(auto) result = --it; + assert(&result == &it); + assert(base((*result).begin()) == array.begin()); + } + + // Test with two chunks + { + std::array array{0, 1, 2, 0, 1, 2}; + ChunkByView view = make_chunk_by_view(array.begin(), array.end()); + ChunkByIterator it = std::ranges::next(view.begin(), view.end()); + + std::same_as decltype(auto) result = --it; + assert(&result == &it); + assert(base((*result).begin()) == array.begin() + 3); + + --it; + assert(base((*result).begin()) == array.begin()); + } + + // Test going forward and then backward on the same iterator + { + std::array array{7, 8, 9, 4, 5, 6, 1, 2, 3, 0}; + ChunkByView view = make_chunk_by_view(array.begin(), array.end()); + ChunkByIterator it = view.begin(); + + ++it; + --it; + assert(base((*it).begin()) == array.begin()); + assert(base((*it).end()) == array.begin() + 3); + + ++it; + ++it; + --it; + assert(base((*it).begin()) == array.begin() + 3); + assert(base((*it).end()) == array.begin() + 6); + + ++it; + ++it; + --it; + assert(base((*it).begin()) == array.begin() + 6); + assert(base((*it).end()) == array.begin() + 9); + + ++it; + ++it; + --it; + assert(base((*it).begin()) == array.begin() + 9); + } + + // Decrement an iterator multiple times + if constexpr (std::ranges::common_range) { + std::array array{1, 2, 1, 2, 1}; + ChunkByView view = make_chunk_by_view(array.begin(), array.end()); + + ChunkByIterator it = view.end(); + --it; + --it; + --it; + assert(base((*it).begin()) == array.begin()); + } + + // Test with a predicate that takes by non-const reference + if constexpr (!std::to_underlying(Constant)) { + std::array array{1, 2, 3, -3, -2, -1}; + View v{Iterator{array.begin()}, Sentinel{Iterator{array.end()}}}; + auto view = std::views::chunk_by(std::move(v), [](int& x, int& y) { return x <= y; }); + + auto it = std::ranges::next(view.begin()); + assert(base((*it).begin()) == array.begin() + 3); + --it; + assert(base((*it).begin()) == array.begin()); + } + + // Test with a predicate that is invocable but not callable (i.e. cannot be called like regular function 'f()') + { + std::array array = {1, 2, 3, -3, -2, -1}; + auto v = View{Iterator{array.begin()}, Sentinel{Iterator{array.end()}}} + | std::views::transform([](int x) { return IntWrapper{x}; }); + auto view = std::views::chunk_by(std::move(v), &IntWrapper::lessEqual); + + auto it = std::ranges::next(view.begin()); + assert(base((*it).begin().base()) == array.begin() + 3); + --it; + assert(base((*it).begin().base()) == array.begin()); + } + + // Make sure we do not make a copy of the predicate when we decrement + if constexpr (std::ranges::common_range) { + bool moved = false, copied = false; + std::array array{1, 2, 1, 3}; + View v{Iterator(array.begin()), Sentinel(Iterator(array.end()))}; + auto view = std::views::chunk_by(std::move(v), TrackingPred(&moved, &copied)); + assert(std::exchange(moved, false)); + auto it = view.end(); + --it; + it--; + assert(!moved); + assert(!copied); + } + + // Check post-decrement + { + std::array array{0, 1, 2, -3, -2, -1, -6, -5, -4}; + ChunkByView view = make_chunk_by_view(array.begin(), array.end()); + ChunkByIterator it = std::ranges::next(view.begin(), view.end()); + + std::same_as decltype(auto) result = it--; + assert(result != it); + assert(result == std::default_sentinel); + assert(base((*it).begin()) == array.begin() + 6); + + result = it--; + assert(base((*it).begin()) == array.begin() + 3); + assert(base((*result).begin()) == array.begin() + 6); + + result = it--; + assert(base((*it).begin()) == array.begin()); + assert(base((*result).begin()) == array.begin() + 3); + } +} + +template +constexpr void test_with_pair() { + // Test with pair of iterators + test(); + + // Test with iterator-sentinel pair + test(); +} + +constexpr bool tests() { + test_with_pair, IsConst::no>(); + test_with_pair, IsConst::no>(); + test_with_pair, IsConst::no>(); + test_with_pair(); + + test_with_pair, IsConst::yes>(); + test_with_pair, IsConst::yes>(); + test_with_pair, IsConst::yes>(); + test_with_pair(); + + // Make sure `operator--` isn't provided for non bidirectional ranges + { + using ForwardView = View, sentinel_wrapper>>; + using ChunkByView = std::ranges::chunk_by_view; + static_assert(!HasPreDecrement>); + static_assert(!HasPostDecrement>); + } + + return true; +} + +int main(int, char**) { + tests(); + static_assert(tests()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/deref.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/deref.pass.cpp @@ -0,0 +1,69 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// constexpr value_type operator*() const; + +#include + +#include +#include +#include +#include +#include +#include + +#include "../types.h" +#include "test_iterators.h" +#include "test_macros.h" + +template > +constexpr void test() { + using Underlying = View; + using ChunkByView = std::ranges::chunk_by_view; + using ChunkByIterator = std::ranges::iterator_t; + + std::array array{0, 1, 2, 3, -1, 0, 1, 2, -2, 3, 4, 5}; + std::array expected{std::array{0, 1, 2, 3}, std::array{-1, 0, 1, 2}, std::array{-2, 3, 4, 5}}; + Underlying underlying{Iter{array.begin()}, Sent{Iter{array.end()}}}; + ChunkByView view{underlying, std::ranges::less_equal{}}; + + size_t idx = 0; + for (std::same_as auto iter = view.begin(); iter != view.end(); ++idx, ++iter) { + std::same_as auto chunk = *iter; + assert(std::ranges::equal(chunk, expected[idx])); + } +} + +constexpr bool tests() { + // Check iterator-sentinel pair + test>(); + test>(); + test>(); + test>(); + test(); + + // Check iterator pair + test, forward_iterator>(); + test, bidirectional_iterator>(); + test, random_access_iterator>(); + test, contiguous_iterator>(); + test(); + + return true; +} + +int main(int, char**) { + tests(); + static_assert(tests()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/increment.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/increment.pass.cpp @@ -0,0 +1,179 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// constexpr iterator& operator++(); +// constexpr iterator operator++(int); + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../types.h" +#include "test_iterators.h" +#include "test_macros.h" + +struct TrackingPred : TrackInitialization { + using TrackInitialization::TrackInitialization; + constexpr bool operator()(int x, int y) const { return x <= y; } +}; + +template +constexpr void test() { + using Sentinel = sentinel_wrapper; + using Underlying = View; + using ChunkByView = std::ranges::chunk_by_view; + using ChunkByIterator = std::ranges::iterator_t; + + auto make_chunk_by_view = [](auto begin, auto end) { + View view{Iterator{begin}, Sentinel{Iterator{end}}}; + return ChunkByView{std::move(view), std::ranges::less_equal{}}; + }; + + // Increment the iterator when it won't find another satisfied value after begin() + { + std::array array{0, 1, 2, 3, 4}; + ChunkByView view = make_chunk_by_view(array.begin(), array.end()); + ChunkByIterator it = view.begin(); + + std::same_as decltype(auto) result = ++it; + assert(&result == &it); + assert(result == view.end()); + assert(result == std::default_sentinel); + } + + // Increment the iterator and it finds another value after begin() + { + std::array array{1, 2, 3, -1, -2, -3}; + int const* second_chunk = array.begin() + 3; + ChunkByView view = make_chunk_by_view(array.begin(), array.end()); + + ChunkByIterator it = view.begin(); + ++it; + assert(base((*it).begin()) == second_chunk); + } + + // Increment advances all the way to the end of the range + { + std::array array{1, 2, 3, 4, 1}; + ChunkByView view = make_chunk_by_view(array.begin(), array.end()); + + ChunkByIterator it = view.begin(); + ++it; + assert(base((*it).begin()) == array.begin() + 4); + } + + // Increment an iterator multiple times + { + std::array array{0, 1, 0, 2, 0, 3, 0, 4}; + ChunkByView view = make_chunk_by_view(array.begin(), array.end()); + + ChunkByIterator it = view.begin(); + assert(base((*it).begin()) == array.begin()); + ++it; + assert(base((*it).begin()) == array.begin() + 2); + ++it; + assert(base((*it).begin()) == array.begin() + 4); + ++it; + assert(base((*it).begin()) == array.begin() + 6); + ++it; + assert(it == std::default_sentinel); + } + + // Test with a predicate that takes by non-const reference + if constexpr (!std::to_underlying(Constant)) { + std::array array{1, 2, 3, -3, -2, -1}; + View v{Iterator{array.begin()}, Sentinel{Iterator{array.end()}}}; + auto view = std::views::chunk_by(std::move(v), [](int& x, int& y) { return x <= y; }); + + auto it = view.begin(); + assert(base((*it).begin()) == array.begin()); + ++it; + assert(base((*it).begin()) == array.begin() + 3); + } + + // Test with a predicate that is invocable but not callable (i.e. cannot be called like regular function 'f()') + { + std::array array = {1, 2, 3, -3, -2, -1}; + auto v = View{Iterator{array.begin()}, Sentinel{Iterator{array.end()}}} + | std::views::transform([](int x) { return IntWrapper{x}; }); + auto view = std::views::chunk_by(std::move(v), &IntWrapper::lessEqual); + + auto it = view.begin(); + assert(base((*it).begin().base()) == array.begin()); + ++it; + assert(base((*it).begin().base()) == array.begin() + 3); + } + + // Make sure we do not make a copy of the predicate when we increment + // (we should be passing it to ranges::adjacent_find using std::ref) + { + bool moved = false, copied = false; + std::array array{1, 2, 1, 3}; + View v{Iterator(array.begin()), Sentinel(Iterator(array.end()))}; + auto view = std::views::chunk_by(std::move(v), TrackingPred(&moved, &copied)); + assert(std::exchange(moved, false)); + auto it = view.begin(); + ++it; + it++; + assert(!moved); + assert(!copied); + } + + // Check post-increment + { + std::array array{0, 1, 2, -3, -2, -1, -6, -5, -4}; + ChunkByView view = make_chunk_by_view(array.begin(), array.end()); + ChunkByIterator it = view.begin(); + + std::same_as decltype(auto) result = it++; + assert(result != it); + assert(base((*result).begin()) == array.begin()); + assert(base((*it).begin()) == array.begin() + 3); + + result = it++; + assert(base((*result).begin()) == array.begin() + 3); + assert(base((*it).begin()) == array.begin() + 6); + + result = it++; + assert(base((*result).begin()) == array.begin() + 6); + assert(it == std::default_sentinel); + } +} + +constexpr bool tests() { + test, IsConst::no>(); + test, IsConst::no>(); + test, IsConst::no>(); + test, IsConst::no>(); + test(); + + test, IsConst::yes>(); + test, IsConst::yes>(); + test, IsConst::yes>(); + test, IsConst::yes>(); + test(); + + return true; +} + +int main(int, char**) { + tests(); + static_assert(tests()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/types.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/range.chunk.by.iter/types.compile.pass.cpp @@ -0,0 +1,71 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// std::ranges::chunk_by_view::::value_type +// std::ranges::chunk_by_view::::difference_type +// std::ranges::chunk_by_view::::iterator_category +// std::ranges::chunk_by_view::::iterator_concept + +#include + +#include +#include + +#include "../types.h" +#include "test_iterators.h" + +struct TestValueTypeAndDifferenceType { + template + constexpr void operator()() const { + using Underlying = View; + using ChunkByView = std::ranges::chunk_by_view; + using ChunkByIterator = std::ranges::iterator_t; + static_assert(std::same_as>); + static_assert(std::same_as>); + static_assert( + std::same_as>); + static_assert(std::same_as>); + } +}; + +template +using ChunkByIteratorFor = std::ranges::iterator_t, std::ranges::less_equal>>; + +constexpr void test() { + // Check that value_type is range_value_t and difference_type is range_difference_t + types::for_each(types::forward_iterator_list{}, TestValueTypeAndDifferenceType{}); + + // Check iterator_concept for various categories of ranges + { + static_assert( + std::same_as>::iterator_concept, std::forward_iterator_tag>); + static_assert(std::same_as>::iterator_concept, + std::bidirectional_iterator_tag>); + static_assert(std::same_as>::iterator_concept, + std::bidirectional_iterator_tag>); + static_assert( + std::same_as>::iterator_concept, std::bidirectional_iterator_tag>); + static_assert(std::same_as::iterator_concept, std::bidirectional_iterator_tag>); + } + + // Check iterator_category for various categories of ranges + { + static_assert(std::same_as>::iterator_category, std::input_iterator_tag>); + static_assert( + std::same_as>::iterator_category, std::input_iterator_tag>); + static_assert( + std::same_as>::iterator_category, std::input_iterator_tag>); + static_assert( + std::same_as>::iterator_category, std::input_iterator_tag>); + static_assert(std::same_as::iterator_category, std::input_iterator_tag>); + } +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/types.h b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/types.h new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/types.h @@ -0,0 +1,54 @@ +//===----------------------------------------------------------------------===// +// +// 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_BY_TYPES_H +#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_BY_TYPES_H + +#include +#include + +#include "test_iterators.h" + +struct TrackInitialization { + constexpr explicit TrackInitialization(bool* moved, bool* copied) : moved_(moved), copied_(copied) {} + constexpr TrackInitialization(TrackInitialization const& other) : moved_(other.moved_), copied_(other.copied_) { + *copied_ = true; + } + constexpr TrackInitialization(TrackInitialization&& other) : moved_(other.moved_), copied_(other.copied_) { + *moved_ = true; + } + TrackInitialization& operator=(TrackInitialization const&) = default; + TrackInitialization& operator=(TrackInitialization&&) = default; + bool* moved_; + bool* copied_; +}; + +enum class IsConst : bool { no, yes }; + +template Sent = sentinel_wrapper> +struct View : std::ranges::view_base { + constexpr explicit View(Iter b, Sent e) : begin_(b), end_(e) {} + constexpr Iter begin() { return begin_; } + constexpr Sent end() { return end_; } + +private: + Iter begin_; + Sent end_; +}; + +template +View(I b, S e) -> View; + +struct IntWrapper { + constexpr IntWrapper(int v) : value_(v) {} + + int value_; + constexpr bool lessEqual(IntWrapper other) const { return value_ <= other.value_; } +}; + +#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_CHUNK_BY_TYPES_H diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -801,7 +801,6 @@ "name": "__cpp_lib_ranges_chunk_by", "values": {"c++23": 202202}, "headers": ["ranges"], - "unimplemented": True, }, { "name": "__cpp_lib_ranges_iota",