[libc++][ranges] Implement P2443R1: views::chunk_by

This patch implements https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2443r1.html (`views::chunk_by`).

Reviewed By: #libc, var-const

Differential Revision: https://reviews.llvm.org/D144767
This commit is contained in:
Jakub Mazurkiewicz 2023-09-05 16:09:53 -07:00 committed by Konstantin Varlamov
parent 3e19b10b02
commit 065dc485bd
45 changed files with 2381 additions and 59 deletions

View File

@ -346,7 +346,7 @@ Status
--------------------------------------------------- -----------------
``__cpp_lib_ranges_chunk`` *unimplemented*
--------------------------------------------------- -----------------
``__cpp_lib_ranges_chunk_by`` *unimplemented*
``__cpp_lib_ranges_chunk_by`` ``202202L``
--------------------------------------------------- -----------------
``__cpp_lib_ranges_iota`` *unimplemented*
--------------------------------------------------- -----------------

View File

@ -41,7 +41,7 @@ Implemented Papers
- P2497R0 - Testing for success or failure of ``<charconv>`` functions
- P2697R1 - Interfacing ``bitset`` with ``string_view``
- P2443R1 - ``views::chunk_by``
Improvements and New Features
-----------------------------

View File

@ -49,7 +49,7 @@
"`P2440R1 <https://wg21.link/P2440R1>`__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","","","|ranges|"
"`P2441R2 <https://wg21.link/P2441R2>`__","LWG","``views::join_with``","February 2022","","","|ranges|"
"`P2442R1 <https://wg21.link/P2442R1>`__","LWG","Windowing range adaptors: ``views::chunk`` and ``views::slide``","February 2022","","","|ranges|"
"`P2443R1 <https://wg21.link/P2443R1>`__","LWG","``views::chunk_by``","February 2022","","","|ranges|"
"`P2443R1 <https://wg21.link/P2443R1>`__","LWG","``views::chunk_by``","February 2022","|Complete|","18.0","|ranges|"
"","","","","","",""
"`P0009R18 <https://wg21.link/P0009R18>`__","LWG","mdspan: A Non-Owning Multidimensional Array Reference","July 2022","|In progress| [#note-P0009R18]_|",""
"`P0429R9 <https://wg21.link/P0429R9>`__","LWG","A Standard ``flat_map``","July 2022","",""

Can't render this file because it has a wrong number of fields in line 2.

View File

@ -31,7 +31,7 @@ C++23,`adjacent_transform <https://wg21.link/P2321R2>`_,Hui Xie,No patch yet,Not
C++23,`join_with <https://wg21.link/P2441R2>`_,Unassigned,No patch yet,Not started
C++23,`slide <https://wg21.link/P2442R1>`_,Unassigned,No patch yet,Not started
C++23,`chunk <https://wg21.link/P2442R1>`_,Unassigned,No patch yet,Not started
C++23,`chunk_by <https://wg21.link/P2443R1>`_,Unassigned,No patch yet,Not started
C++23,`chunk_by <https://wg21.link/P2443R1>`_,Jakub Mazurkiewicz,`D144767 <https://llvm.org/D144767>`,
C++23,`as_const <https://wg21.link/P2278R4>`_,Unassigned,No patch yet,Not started
C++23,`as_rvalue <https://wg21.link/P2446R2>`_,Nikolas Klauser,`D137637 <https://llvm.org/D137637>`_,
C++23,`stride <https://wg21.link/P1899R3>`_,Hristo Hristov and Will Hawkins,`D156924 <https://llvm.org/D156924>`_,In Progress

1 Standard View Assignee CL Status
31 C++23 `join_with <https://wg21.link/P2441R2>`_ Unassigned No patch yet Not started
32 C++23 `slide <https://wg21.link/P2442R1>`_ Unassigned No patch yet Not started
33 C++23 `chunk <https://wg21.link/P2442R1>`_ Unassigned No patch yet Not started
34 C++23 `chunk_by <https://wg21.link/P2443R1>`_ Unassigned Jakub Mazurkiewicz No patch yet `D144767 <https://llvm.org/D144767>` Not started
35 C++23 `as_const <https://wg21.link/P2278R4>`_ Unassigned No patch yet Not started
36 C++23 `as_rvalue <https://wg21.link/P2446R2>`_ Nikolas Klauser `D137637 <https://llvm.org/D137637>`_
37 C++23 `stride <https://wg21.link/P1899R3>`_ Hristo Hristov and Will Hawkins `D156924 <https://llvm.org/D156924>`_ In Progress

View File

@ -602,6 +602,7 @@ set(files
__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

View File

@ -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 @@ _LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP_STD_VER >= 20
_LIBCPP_POP_MACROS
#endif // _LIBCPP___ALGORITHM_RANGES_ADJACENT_FIND_H

View File

@ -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 <forward_range _View, indirect_binary_predicate<iterator_t<_View>, iterator_t<_View>> _Pred>
requires view<_View> && is_object_v<_Pred>
class chunk_by_view : public view_interface<chunk_by_view<_View, _Pred>> {
_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<iterator_t<_View>>;
_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]<class _Tp, class _Up>(_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 <class _Range, class _Pred>
chunk_by_view(_Range&&, _Pred) -> chunk_by_view<views::all_t<_Range>, _Pred>;
template <forward_range _View, indirect_binary_predicate<iterator_t<_View>, 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<iterator_t<_View>>;
using difference_type = range_difference_t<_View>;
using iterator_category = input_iterator_tag;
using iterator_concept = conditional_t<bidirectional_range<_View>, 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 <class _Range, class _Pred>
_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 <class _Pred>
requires constructible_from<decay_t<_Pred>, _Pred>
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Pred&& __pred) const
noexcept(is_nothrow_constructible_v<decay_t<_Pred>, _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

View File

@ -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 @@ public:
_LIBCPP_END_NAMESPACE_STD
_LIBCPP_POP_MACROS
#endif // _LIBCPP___RANGES_MOVABLE_BOX_H

View File

@ -1659,6 +1659,7 @@ module std_private_ranges_all [system] {
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"

View File

@ -320,17 +320,24 @@ namespace std::ranges {
class zip_view; // C++23
template<class... Views>
inline constexpr bool enable_borrowed_range<zip_view<Views...>> = // C++23
inline constexpr bool enable_borrowed_range<zip_view<Views...>> = // C++23
(enable_borrowed_range<Views> && ...);
namespace views { inline constexpr unspecified zip = unspecified; } // C++23
namespace views { inline constexpr unspecified zip = unspecified; } // C++23
// [range.as.rvalue]
template <view V>
requires input_range<V>
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<forward_range V, indirect_binary_predicate<iterator_t<V>, iterator_t<V>> Pred>
requires view<V> && is_object_v<Pred>
class chunk_by_view; // C++23
namespace views { inline constexpr unspecified chunk_by = unspecified; } // C++23
}
namespace std {
@ -373,6 +380,7 @@ namespace std {
#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>

View File

@ -435,7 +435,7 @@ __cpp_lib_within_lifetime 202306L <type_traits>
// # 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

View File

@ -309,6 +309,7 @@ export namespace std {
namespace views {
using std::ranges::views::slide;
}
#endif
// [range.chunk.by], chunk by view
using std::ranges::chunk_by_view;
@ -317,6 +318,7 @@ export namespace std {
using std::ranges::views::chunk_by;
}
#if 0
// [range.stride], stride view
using std::ranges::stride_view;

View File

@ -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
// <ranges>
// Test the libc++ extension that std::views::chunk_by is marked as [[nodiscard]].
#include <functional>
#include <ranges>
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}}
}

View File

@ -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
// <ranges>
// Call begin() on chunk_by_view with empty predicate
#include <ranges>
#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;
}

View File

@ -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
// <ranges>
// Call find-next() on chunk_by_view with empty predicate
#include <ranges>
#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;
}

View File

@ -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
// <ranges>
// Call find-prev() on chunk_by_view with begin iterator
// Call find-prev() on chunk_by_view with empty predicate
#include <functional>
#include <ranges>
#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;
}

View File

@ -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
// <ranges>
// Dereference past end chunk_by_view iterator
#include <functional>
#include <ranges>
#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;
}

View File

@ -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
// <ranges>
// Increment past end chunk_by_view iterator
#include <functional>
#include <ranges>
#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;
}

View File

@ -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

View File

@ -687,11 +687,11 @@ ranges initializer_list
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

1 algorithm atomic
687 ranges iosfwd
688 ranges iterator
689 ranges limits
690 ranges new
691 ranges optional
692 ranges span
693 ranges tuple
694 ranges type_traits
ranges variant
695 ranges version
696 ratio climits
697 ratio cstdint

View File

@ -692,11 +692,11 @@ ranges initializer_list
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

1 algorithm atomic
692 ranges iosfwd
693 ranges iterator
694 ranges limits
695 ranges new
696 ranges optional
697 ranges span
698 ranges tuple
699 ranges type_traits
ranges variant
700 ranges version
701 ratio climits
702 ratio cstdint

View File

@ -694,11 +694,11 @@ ranges initializer_list
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

1 algorithm atomic
694 ranges iosfwd
695 ranges iterator
696 ranges limits
697 ranges new
698 ranges optional
699 ranges span
700 ranges tuple
701 ranges type_traits
ranges variant
702 ranges version
703 ratio climits
704 ratio cstdint

View File

@ -694,11 +694,11 @@ ranges initializer_list
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

1 algorithm atomic
694 ranges iosfwd
695 ranges iterator
696 ranges limits
697 ranges new
698 ranges optional
699 ranges span
700 ranges tuple
701 ranges type_traits
ranges variant
702 ranges version
703 ratio climits
704 ratio cstdint

View File

@ -700,11 +700,11 @@ ranges initializer_list
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

1 algorithm atomic
700 ranges iosfwd
701 ranges iterator
702 ranges limits
703 ranges new
704 ranges optional
705 ranges span
706 ranges tuple
707 ranges type_traits
ranges variant
708 ranges version
709 ratio climits
710 ratio cstdint

View File

@ -499,6 +499,7 @@ ranges initializer_list
ranges iosfwd
ranges iterator
ranges limits
ranges new
ranges optional
ranges span
ranges tuple

1 algorithm climits
499 ranges iosfwd
500 ranges iterator
501 ranges limits
502 ranges new
503 ranges optional
504 ranges span
505 ranges tuple

View File

@ -499,6 +499,7 @@ ranges initializer_list
ranges iosfwd
ranges iterator
ranges limits
ranges new
ranges optional
ranges span
ranges tuple

1 algorithm climits
499 ranges iosfwd
500 ranges iterator
501 ranges limits
502 ranges new
503 ranges optional
504 ranges span
505 ranges tuple

View File

@ -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)

View File

@ -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)

View File

@ -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
// <ranges>
// std::views::chunk_by
#include <ranges>
#include <algorithm>
#include <cassert>
#include <concepts>
#include <initializer_list>
#include <type_traits>
#include <utility>
#include "test_iterators.h"
template <class View, class T>
concept CanBePiped = requires(View&& view, T&& t) {
{ std::forward<View>(view) | std::forward<T>(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<int*>;
using Sentinel = sentinel_wrapper<Iterator>;
constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) {}
constexpr Iterator begin() const { return Iterator(begin_); }
constexpr Sentinel end() const { return Sentinel(Iterator(end_)); }
private:
int* begin_;
int* end_;
};
template <typename View>
constexpr void compareViews(View v, std::initializer_list<std::initializer_list<int>> list) {
auto b1 = v.begin();
auto e1 = v.end();
auto b2 = list.begin();
auto e2 = list.end();
for (; b1 != e1 && b2 != e2; ++b1, ++b2) {
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 <class T>
constexpr const T&& asConstRvalue(T&& t) {
return static_cast<T const&&>(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<RangeAdaptorObject>);
// The type of a customization point object, ignoring cv-qualifiers, shall model semiregular
static_assert(std::semiregular<std::remove_const<RangeAdaptorObject>>);
}
// Test `views::chunk_by(pred)(v)`
{
using Result = std::ranges::chunk_by_view<Range, Pred>;
Range const range(buff, buff + 8);
Pred pred;
{
// 'views::chunk_by(pred)' - &&
std::same_as<Result> 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<Result> 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<Result> 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<Result> 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, Pred>;
Range const range(buff, buff + 8);
Pred pred;
{
// 'views::chunk_by(pred)' - &&
std::same_as<Result> 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<Result> 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<Result> 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<Result> 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, Pred>;
Range const range(buff, buff + 8);
Pred pred;
{
// 'views::chunk_by' - &&
auto range_adaptor = std::views::chunk_by;
std::same_as<Result> 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<Result> 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<Result> 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<Result> 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<std::ranges::filter_view<Range, decltype(pred1)>, Pred>;
std::same_as<Result> 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<std::ranges::filter_view<Range, decltype(pred1)>, Pred>;
auto const partial = std::views::filter(pred1) | std::views::chunk_by(pred2);
std::same_as<Result> decltype(auto) result = range | partial;
compareViews(result, {{-2, -1}, {1, 2}});
}
}
// Test SFINAE friendliness
{
struct NotAView {};
struct NotInvocable {};
static_assert(!CanBePiped<Range, decltype(std::views::chunk_by)>);
static_assert(CanBePiped<Range, decltype(std::views::chunk_by(Pred{}))>);
static_assert(!CanBePiped<NotAView, decltype(std::views::chunk_by(Pred{}))>);
static_assert(!CanBePiped<std::initializer_list<int>, decltype(std::views::chunk_by(Pred{}))>);
static_assert(!CanBePiped<Range, decltype(std::views::chunk_by(NotInvocable{}))>);
static_assert(!std::is_invocable_v<decltype(std::views::chunk_by)>);
static_assert(!std::is_invocable_v<decltype(std::views::chunk_by), Pred, Range>);
static_assert(std::is_invocable_v<decltype(std::views::chunk_by), Range, Pred>);
static_assert(!std::is_invocable_v<decltype(std::views::chunk_by), Range, Pred, Pred>);
static_assert(!std::is_invocable_v<decltype(std::views::chunk_by), NonCopyablePredicate>);
}
{ static_assert(std::is_same_v<decltype(std::ranges::views::chunk_by), decltype(std::views::chunk_by)>); }
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -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
// <ranges>
// constexpr View base() const& requires copy_constructible<View>;
// constexpr View base() &&;
#include <ranges>
#include <cassert>
#include <concepts>
#include <utility>
struct Range : std::ranges::view_base {
constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) {}
constexpr Range(Range const& other) : 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<Range>);
static_assert(std::ranges::forward_range<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<NonCopyableRange>);
template <typename T>
concept CanCallBaseOn = requires(T t) { std::forward<T>(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<Range, Pred> const view(range, Pred{});
std::same_as<Range> 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<Range, Pred> view(range, Pred{});
std::same_as<Range> 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<std::ranges::chunk_by_view<NonCopyableRange, Pred> const&>);
static_assert(!CanCallBaseOn<std::ranges::chunk_by_view<NonCopyableRange, Pred>&>);
static_assert(!CanCallBaseOn<std::ranges::chunk_by_view<NonCopyableRange, Pred> const&&>);
static_assert(CanCallBaseOn<std::ranges::chunk_by_view<NonCopyableRange, Pred>&&>);
static_assert(CanCallBaseOn<std::ranges::chunk_by_view<NonCopyableRange, Pred>>);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -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
// <ranges>
// constexpr iterator begin();
#include <ranges>
#include <cassert>
#include <utility>
#include "test_iterators.h"
#include "types.h"
struct Range : std::ranges::view_base {
using Iterator = forward_iterator<int*>;
using Sentinel = sentinel_wrapper<Iterator>;
constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) {}
constexpr Iterator begin() const { return Iterator(begin_); }
constexpr Sentinel end() const { return Sentinel(Iterator(end_)); }
private:
int* begin_;
int* end_;
};
struct TrackingPred : TrackInitialization {
using TrackInitialization::TrackInitialization;
constexpr bool operator()(int x, int y) { return x != -y; }
};
template <class T>
concept HasBegin = requires(T t) { t.begin(); };
static_assert(HasBegin<std::ranges::chunk_by_view<Range, TrackingPred>>);
static_assert(!HasBegin<const std::ranges::chunk_by_view<Range, TrackingPred>>);
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<decltype(view)>;
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;
}

View File

@ -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
// <ranges>
// Check constraints on the type itself.
//
// template <forward_range View, indirect_binary_predicate<iterator_t<View>, iterator_t<View>> Pred>
// requires view<View> && is_object_v<Pred>
// class chunk_by_view;
#include <ranges>
#include <concepts>
#include <cstddef>
#include <iterator>
#include <type_traits>
#include "almost_satisfies_types.h"
#include "test_iterators.h"
template <class View, class Pred>
concept CanFormChunkByView = requires { typename std::ranges::chunk_by_view<View, Pred>; };
// 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<View>);
static_assert(std::indirect_binary_predicate<Pred, int*, int*>);
static_assert(std::ranges::view<View>);
static_assert(std::is_object_v<Pred>);
static_assert(!CanFormChunkByView<View, Pred>);
} // 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<View>);
static_assert(!std::indirect_binary_predicate<Pred, int*, int*>);
static_assert(std::ranges::view<View>);
static_assert(std::is_object_v<Pred>);
static_assert(!CanFormChunkByView<View, Pred>);
} // 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<View>);
static_assert(std::indirect_binary_predicate<Pred, int*, int*>);
static_assert(!std::ranges::view<View>);
static_assert(std::is_object_v<Pred>);
static_assert(!CanFormChunkByView<View, Pred>);
} // 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<View>);
static_assert(std::indirect_binary_predicate<Pred, int*, int*>);
static_assert(std::ranges::view<View>);
static_assert(!std::is_object_v<Pred>);
static_assert(!CanFormChunkByView<View, Pred>);
} // 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<View>);
static_assert(std::indirect_binary_predicate<Pred, int*, int*>);
static_assert(std::ranges::view<View>);
static_assert(std::is_object_v<Pred>);
static_assert(CanFormChunkByView<View, Pred>);
} // namespace test_when_all_the_constraints_are_satisfied

View File

@ -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
// <ranges>
// template <class Range, class Pred>
// chunk_by_view(Range&&, Pred) -> chunk_by_view<views::all_t<Range>, Pred>;
#include <ranges>
#include <cassert>
#include <type_traits>
#include "test_iterators.h"
struct View : std::ranges::view_base {
View() = default;
forward_iterator<int*> begin() const;
sentinel_wrapper<forward_iterator<int*>> end() const;
};
static_assert(std::ranges::view<View>);
// A range that is not a view
struct Range {
Range() = default;
forward_iterator<int*> begin() const;
sentinel_wrapper<forward_iterator<int*>> end() const;
};
static_assert(std::ranges::range<Range>);
static_assert(!std::ranges::view<Range>);
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<decltype(view), std::ranges::chunk_by_view<View, Pred>>);
}
{
Range r;
Pred pred;
std::ranges::chunk_by_view view(r, pred);
static_assert(std::is_same_v<decltype(view), std::ranges::chunk_by_view<std::ranges::ref_view<Range>, Pred>>);
}
{
Pred pred;
std::ranges::chunk_by_view view(Range{}, pred);
static_assert(std::is_same_v<decltype(view), std::ranges::chunk_by_view<std::ranges::owning_view<Range>, Pred>>);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -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
// <ranges>
// chunk_by_view() requires std::default_initializable<View> &&
// std::default_initializable<Pred> = default;
#include <ranges>
#include <cassert>
#include <type_traits>
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<const int*> v, std::initializer_list<int> 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<DefaultConstructibleView, DefaultConstructiblePredicate>;
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<DefaultConstructibleView, DefaultConstructiblePredicate>;
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<std::ranges::chunk_by_view<NoDefaultView, DefaultConstructiblePredicate>>);
static_assert(
!std::is_default_constructible_v<std::ranges::chunk_by_view<DefaultConstructibleView, NoDefaultPredicate>>);
static_assert(!std::is_default_constructible_v<std::ranges::chunk_by_view<NoDefaultView, NoDefaultPredicate>>);
}
// Check noexcept-ness
{
{
using View = std::ranges::chunk_by_view<MayThrowView, MayThrowPredicate>;
static_assert(!noexcept(View()));
}
{
using View = std::ranges::chunk_by_view<MayThrowView, NoexceptPredicate>;
static_assert(!noexcept(View()));
}
{
using View = std::ranges::chunk_by_view<NoexceptView, MayThrowPredicate>;
static_assert(!noexcept(View()));
}
{
using View = std::ranges::chunk_by_view<NoexceptView, NoexceptPredicate>;
static_assert(noexcept(View()));
}
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -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
// <ranges>
// constexpr chunk_by_view(View, Pred);
#include <ranges>
#include <algorithm>
#include <array>
#include <cassert>
#include <utility>
#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<Range>);
static_assert(std::ranges::forward_range<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 <class T>
void implicitConstructionTest(T);
template <class T, class... Args>
concept ImplicitConstructibleFrom =
requires(Args&&... args) { implicitConstructionTest({std::forward<Args>(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<Range, Pred> 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<Range, Pred>;
static_assert(!ImplicitConstructibleFrom<ChunkByView, Range, Pred>);
static_assert(!ImplicitConstructibleFrom<ChunkByView, const Range&, const Pred&>);
}
// Make sure we move the view
{
bool moved = false, copied = false;
TrackingRange range(&moved, &copied);
Pred pred;
[[maybe_unused]] std::ranges::chunk_by_view<TrackingRange, Pred> 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<Range, TrackingPred> view(range, std::move(pred));
assert(moved);
assert(!copied);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -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
// <ranges>
// constexpr auto end();
#include <ranges>
#include <cassert>
#include <concepts>
#include <functional>
#include "test_iterators.h"
struct NonCommonRange : std::ranges::view_base {
using Iterator = forward_iterator<int*>;
using Sentinel = sentinel_wrapper<Iterator>;
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<NonCommonRange>);
static_assert(!std::ranges::common_range<NonCommonRange>);
struct CommonRange : std::ranges::view_base {
using Iterator = bidirectional_iterator<int*>;
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<CommonRange>);
static_assert(std::ranges::common_range<CommonRange>);
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<ChunkByView>);
ASSERT_SAME_TYPE(std::ranges::sentinel_t<ChunkByView>, 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<decltype(view)>);
ASSERT_SAME_TYPE(std::default_sentinel_t, decltype(end));
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -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
// <ranges>
// constexpr Pred const& pred() const;
#include <ranges>
#include <cassert>
#include <concepts>
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<Range, Pred> const view(Range{}, pred);
std::same_as<Pred const&> 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<Range, Pred> view(Range{}, pred);
std::same_as<Pred const&> 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;
}

View File

@ -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
// <ranges>
// friend constexpr bool operator==(const iterator& x, const iterator& y);
// friend constexpr bool operator==(const iterator& x, default_sentinel_t);
#include <ranges>
#include <array>
#include <cassert>
#include <concepts>
#include <functional>
#include <utility>
#include "../types.h"
#include "test_iterators.h"
#include "test_macros.h"
template <class Iterator, class Sentinel = sentinel_wrapper<Iterator>>
constexpr void test() {
using Underlying = View<Iterator, Sentinel>;
using ChunkByView = std::ranges::chunk_by_view<Underlying, std::ranges::less_equal>;
using ChunkByIterator = std::ranges::iterator_t<ChunkByView>;
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<bool> 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<bool> 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<bool> 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<bool> decltype(auto) result = (i != std::default_sentinel);
assert(result);
++i;
assert(!(i != std::default_sentinel));
}
}
struct TestWithPair {
template <class Iterator>
constexpr void operator()() const {
// Test with pair of iterators
test<Iterator, Iterator>();
// Test with iterator-sentinel pair
test<Iterator>();
}
};
constexpr bool tests() {
TestWithPair tester;
types::for_each(types::forward_iterator_list<int*>{}, tester);
types::for_each(types::forward_iterator_list<int const*>{}, tester);
return true;
}
int main(int, char**) {
tests();
static_assert(tests());
return 0;
}

View File

@ -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
// <ranges>
// std::ranges::chunk_by_view<V>::<iterator>() = default;
#include <ranges>
#include <cassert>
#include <functional>
#include <type_traits>
#include <utility>
#include "../types.h"
#include "test_iterators.h"
template <class Iterator, bool IsNoexcept>
constexpr void testDefaultConstructible() {
// Make sure the iterator is default constructible.
using ChunkByView = std::ranges::chunk_by_view<View<Iterator>, std::ranges::less_equal>;
using ChunkByIterator = std::ranges::iterator_t<ChunkByView>;
ChunkByIterator i{};
ChunkByIterator j;
assert(i == j);
static_assert(noexcept(ChunkByIterator{}) == IsNoexcept);
}
constexpr bool tests() {
testDefaultConstructible<forward_iterator<int*>, /*IsNoexcept=*/false>();
testDefaultConstructible<bidirectional_iterator<int*>, /*IsNoexcept=*/false>();
testDefaultConstructible<random_access_iterator<int*>, /*IsNoexcept=*/false>();
testDefaultConstructible<contiguous_iterator<int*>, /*IsNoexcept=*/false>();
testDefaultConstructible<int*, /*IsNoexcept=*/true>();
return true;
}
int main(int, char**) {
tests();
static_assert(tests());
return 0;
}

View File

@ -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
// <ranges>
// constexpr iterator& operator--();
// constexpr iterator operator--(int);
#include <ranges>
#include <array>
#include <cassert>
#include <concepts>
#include <functional>
#include <span>
#include <type_traits>
#include <utility>
#include "../types.h"
#include "test_iterators.h"
#include "test_macros.h"
template <class T>
concept HasPreDecrement = requires(T t) {
{ --t };
};
template <class T>
concept HasPostDecrement = requires(T t) {
{ t-- };
};
struct TrackingPred : TrackInitialization {
using TrackInitialization::TrackInitialization;
constexpr bool operator()(int x, int y) const { return x <= y; }
};
template <class Iterator, IsConst Constant, class Sentinel = sentinel_wrapper<Iterator>>
constexpr void test() {
using Underlying = View<Iterator, Sentinel>;
using ChunkByView = std::ranges::chunk_by_view<Underlying, std::ranges::less_equal>;
using ChunkByIterator = std::ranges::iterator_t<ChunkByView>;
static_assert(HasPostDecrement<ChunkByIterator>);
static_assert(HasPreDecrement<ChunkByIterator>);
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<ChunkByIterator&> 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<ChunkByIterator&> 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<Underlying>) {
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<Underlying>) {
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<ChunkByIterator> 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 <class Iterator, IsConst Constant>
constexpr void test_with_pair() {
// Test with pair of iterators
test<Iterator, Constant>();
// Test with iterator-sentinel pair
test<Iterator, Constant, Iterator>();
}
constexpr bool tests() {
test_with_pair<bidirectional_iterator<int*>, IsConst::no>();
test_with_pair<random_access_iterator<int*>, IsConst::no>();
test_with_pair<contiguous_iterator<int*>, IsConst::no>();
test_with_pair<int*, IsConst::no>();
test_with_pair<bidirectional_iterator<int const*>, IsConst::yes>();
test_with_pair<random_access_iterator<int const*>, IsConst::yes>();
test_with_pair<contiguous_iterator<int const*>, IsConst::yes>();
test_with_pair<int const*, IsConst::yes>();
// Make sure `operator--` isn't provided for non bidirectional ranges
{
using ForwardView = View<forward_iterator<int*>, sentinel_wrapper<forward_iterator<int*>>>;
using ChunkByView = std::ranges::chunk_by_view<ForwardView, std::ranges::less_equal>;
static_assert(!HasPreDecrement<std::ranges::iterator_t<ChunkByView>>);
static_assert(!HasPostDecrement<std::ranges::iterator_t<ChunkByView>>);
}
return true;
}
int main(int, char**) {
tests();
static_assert(tests());
return 0;
}

View File

@ -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
// <ranges>
// constexpr value_type operator*() const;
#include <ranges>
#include <algorithm>
#include <array>
#include <cassert>
#include <concepts>
#include <functional>
#include <utility>
#include "../types.h"
#include "test_iterators.h"
#include "test_macros.h"
template <class Iter, class Sent = sentinel_wrapper<Iter>>
constexpr void test() {
using Underlying = View<Iter, Sent>;
using ChunkByView = std::ranges::chunk_by_view<Underlying, std::ranges::less_equal>;
using ChunkByIterator = std::ranges::iterator_t<ChunkByView>;
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<ChunkByIterator> auto iter = view.begin(); iter != view.end(); ++idx, ++iter) {
std::same_as<typename ChunkByIterator::value_type> auto chunk = *iter;
assert(std::ranges::equal(chunk, expected[idx]));
}
}
constexpr bool tests() {
// Check iterator-sentinel pair
test<forward_iterator<int*>>();
test<bidirectional_iterator<int*>>();
test<random_access_iterator<int*>>();
test<contiguous_iterator<int*>>();
test<int*>();
// Check iterator pair
test<forward_iterator<int*>, forward_iterator<int*>>();
test<bidirectional_iterator<int*>, bidirectional_iterator<int*>>();
test<random_access_iterator<int*>, random_access_iterator<int*>>();
test<contiguous_iterator<int*>, contiguous_iterator<int*>>();
test<int*, int*>();
return true;
}
int main(int, char**) {
tests();
static_assert(tests());
return 0;
}

View File

@ -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
// <ranges>
// constexpr iterator& operator++();
// constexpr iterator operator++(int);
#include <ranges>
#include <array>
#include <cassert>
#include <concepts>
#include <functional>
#include <span>
#include <type_traits>
#include <utility>
#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 <class Iterator, IsConst Constant>
constexpr void test() {
using Sentinel = sentinel_wrapper<Iterator>;
using Underlying = View<Iterator, Sentinel>;
using ChunkByView = std::ranges::chunk_by_view<Underlying, std::ranges::less_equal>;
using ChunkByIterator = std::ranges::iterator_t<ChunkByView>;
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<ChunkByIterator&> 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<ChunkByIterator> 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<forward_iterator<int*>, IsConst::no>();
test<bidirectional_iterator<int*>, IsConst::no>();
test<random_access_iterator<int*>, IsConst::no>();
test<contiguous_iterator<int*>, IsConst::no>();
test<int*, IsConst::no>();
test<forward_iterator<int const*>, IsConst::yes>();
test<bidirectional_iterator<int const*>, IsConst::yes>();
test<random_access_iterator<int const*>, IsConst::yes>();
test<contiguous_iterator<int const*>, IsConst::yes>();
test<int const*, IsConst::yes>();
return true;
}
int main(int, char**) {
tests();
static_assert(tests());
return 0;
}

View File

@ -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
// <ranges>
// std::ranges::chunk_by_view::<iterator>::value_type
// std::ranges::chunk_by_view::<iterator>::difference_type
// std::ranges::chunk_by_view::<iterator>::iterator_category
// std::ranges::chunk_by_view::<iterator>::iterator_concept
#include <ranges>
#include <functional>
#include <type_traits>
#include "../types.h"
#include "test_iterators.h"
struct TestValueTypeAndDifferenceType {
template <class Iter>
constexpr void operator()() const {
using Underlying = View<Iter>;
using ChunkByView = std::ranges::chunk_by_view<Underlying, std::ranges::less_equal>;
using ChunkByIterator = std::ranges::iterator_t<ChunkByView>;
static_assert(std::same_as<typename ChunkByIterator::value_type, std::ranges::range_value_t<ChunkByView>>);
static_assert(std::same_as<typename ChunkByIterator::value_type, std::ranges::subrange<Iter>>);
static_assert(
std::same_as<typename ChunkByIterator::difference_type, std::ranges::range_difference_t<ChunkByView>>);
static_assert(std::same_as<typename ChunkByIterator::difference_type, std::ranges::range_difference_t<Underlying>>);
}
};
template <class Iter>
using ChunkByIteratorFor = std::ranges::iterator_t<std::ranges::chunk_by_view<View<Iter>, 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<int*>{}, TestValueTypeAndDifferenceType{});
// Check iterator_concept for various categories of ranges
{
static_assert(
std::same_as<ChunkByIteratorFor<forward_iterator<int*>>::iterator_concept, std::forward_iterator_tag>);
static_assert(std::same_as<ChunkByIteratorFor<bidirectional_iterator<int*>>::iterator_concept,
std::bidirectional_iterator_tag>);
static_assert(std::same_as<ChunkByIteratorFor<random_access_iterator<int*>>::iterator_concept,
std::bidirectional_iterator_tag>);
static_assert(
std::same_as<ChunkByIteratorFor<contiguous_iterator<int*>>::iterator_concept, std::bidirectional_iterator_tag>);
static_assert(std::same_as<ChunkByIteratorFor<int*>::iterator_concept, std::bidirectional_iterator_tag>);
}
// Check iterator_category for various categories of ranges
{
static_assert(std::same_as<ChunkByIteratorFor<forward_iterator<int*>>::iterator_category, std::input_iterator_tag>);
static_assert(
std::same_as<ChunkByIteratorFor<bidirectional_iterator<int*>>::iterator_category, std::input_iterator_tag>);
static_assert(
std::same_as<ChunkByIteratorFor<random_access_iterator<int*>>::iterator_category, std::input_iterator_tag>);
static_assert(
std::same_as<ChunkByIteratorFor<contiguous_iterator<int*>>::iterator_category, std::input_iterator_tag>);
static_assert(std::same_as<ChunkByIteratorFor<int*>::iterator_category, std::input_iterator_tag>);
}
}

View File

@ -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 <ranges>
#include <utility>
#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 <std::forward_iterator Iter, std::sentinel_for<Iter> Sent = sentinel_wrapper<Iter>>
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 <class I, class S>
View(I b, S e) -> View<I, S>;
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

View File

@ -801,7 +801,6 @@ feature_test_macros = [
"name": "__cpp_lib_ranges_chunk_by",
"values": {"c++23": 202202},
"headers": ["ranges"],
"unimplemented": True,
},
{
"name": "__cpp_lib_ranges_iota",