xref: /llvm-project/libcxx/test/std/ranges/range.adaptors/range.chunk.by/begin.pass.cpp (revision 065dc485bd4b33f6110993cc4b353f1e7c36cac3)
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