1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
10
11 // <ranges>
12
13 // constexpr iterator begin();
14
15 #include <ranges>
16
17 #include <cassert>
18 #include <utility>
19
20 #include "test_iterators.h"
21 #include "types.h"
22
23 struct Range : std::ranges::view_base {
24 using Iterator = forward_iterator<int*>;
25 using Sentinel = sentinel_wrapper<Iterator>;
RangeRange26 constexpr explicit Range(int* b, int* e) : begin_(b), end_(e) {}
beginRange27 constexpr Iterator begin() const { return Iterator(begin_); }
endRange28 constexpr Sentinel end() const { return Sentinel(Iterator(end_)); }
29
30 private:
31 int* begin_;
32 int* end_;
33 };
34
35 struct TrackingPred : TrackInitialization {
36 using TrackInitialization::TrackInitialization;
operator ()TrackingPred37 constexpr bool operator()(int x, int y) { return x != -y; }
38 };
39
40 template <class T>
41 concept HasBegin = requires(T t) { t.begin(); };
42
43 static_assert(HasBegin<std::ranges::chunk_by_view<Range, TrackingPred>>);
44 static_assert(!HasBegin<const std::ranges::chunk_by_view<Range, TrackingPred>>);
45
test()46 constexpr bool test() {
47 int buff[] = {-4, -3, -2, -1, 1, 2, 3, 4};
48
49 // Check the return type of `begin()`
50 {
51 Range range(buff, buff + 1);
52 auto pred = [](int, int) { return true; };
53 std::ranges::chunk_by_view view(range, pred);
54 using ChunkByIterator = std::ranges::iterator_t<decltype(view)>;
55 ASSERT_SAME_TYPE(ChunkByIterator, decltype(view.begin()));
56 }
57
58 // begin() over an empty range
59 {
60 Range range(buff, buff);
61 auto pred = [](int, int) { return true; };
62 std::ranges::chunk_by_view view(range, pred);
63 auto it = view.begin();
64 assert(it == view.begin());
65 assert(it == view.end());
66 }
67
68 // begin() over a 1-element range
69 {
70 Range range(buff, buff + 1);
71 auto pred = [](int x, int y) { return x == y; };
72 std::ranges::chunk_by_view view(range, pred);
73 auto it = view.begin();
74 assert(base((*it).begin()) == buff);
75 assert(base((*it).end()) == buff + 1);
76 }
77
78 // begin() over a 2-element range
79 {
80 Range range(buff, buff + 2);
81 auto pred = [](int x, int y) { return x == y; };
82 std::ranges::chunk_by_view view(range, pred);
83 auto it = view.begin();
84 assert(base((*it).begin()) == buff);
85 assert(base((*it).end()) == buff + 1);
86 assert(base((*++it).begin()) == buff + 1);
87 assert(base((*it).end()) == buff + 2);
88 }
89
90 // begin() over a longer range
91 {
92 Range range(buff, buff + 8);
93 auto pred = [](int x, int y) { return x != -y; };
94 std::ranges::chunk_by_view view(range, pred);
95 auto it = view.begin();
96 assert(base((*it).end()) == buff + 4);
97 }
98
99 // Make sure we do not make a copy of the predicate when we call begin()
100 // (we should be passing it to ranges::adjacent_find using std::ref)
101 {
102 bool moved = false, copied = false;
103 Range range(buff, buff + 2);
104 std::ranges::chunk_by_view view(range, TrackingPred(&moved, &copied));
105 std::exchange(moved, false);
106 [[maybe_unused]] auto it = view.begin();
107 assert(!moved);
108 assert(!copied);
109 }
110
111 // Test with a non-const predicate
112 {
113 Range range(buff, buff + 8);
114 auto pred = [](int x, int y) mutable { return x != -y; };
115 std::ranges::chunk_by_view view(range, pred);
116 auto it = view.begin();
117 assert(base((*it).end()) == buff + 4);
118 }
119
120 // Test with a predicate that takes by non-const reference
121 {
122 Range range(buff, buff + 8);
123 auto pred = [](int& x, int& y) { return x != -y; };
124 std::ranges::chunk_by_view view(range, pred);
125 auto it = view.begin();
126 assert(base((*it).end()) == buff + 4);
127 }
128
129 // Test caching
130 {
131 // Make sure that we cache the result of begin() on subsequent calls
132 Range range(buff, buff + 8);
133 int called = 0;
134 auto pred = [&](int x, int y) {
135 ++called;
136 return x != -y;
137 };
138
139 std::ranges::chunk_by_view view(range, pred);
140 assert(called == 0);
141 for (int k = 0; k != 3; ++k) {
142 auto it = view.begin();
143 assert(base((*it).end()) == buff + 4);
144 assert(called == 4); // 4, because the cached iterator is 'buff + 4' (end of the first chunk)
145 }
146 }
147
148 return true;
149 }
150
main(int,char **)151 int main(int, char**) {
152 test();
153 static_assert(test());
154
155 return 0;
156 }
157