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 // <algorithm>
10
11 // UNSUPPORTED: c++03, c++11, c++14, c++17
12 // UNSUPPORTED: libcpp-has-no-incomplete-ranges
13
14 // template<input_iterator I, sentinel_for<I> S, class Proj = identity,
15 // indirect_unary_predicate<projected<I, Proj>> Pred>
16 // constexpr I ranges::find_if(I first, S last, Pred pred, Proj proj = {});
17 // template<input_range R, class Proj = identity,
18 // indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
19 // constexpr borrowed_iterator_t<R>
20 // ranges::find_if(R&& r, Pred pred, Proj proj = {});
21
22 #include <algorithm>
23 #include <array>
24 #include <cassert>
25 #include <ranges>
26
27 #include "almost_satisfies_types.h"
28 #include "boolean_testable.h"
29 #include "test_iterators.h"
30
31 struct Predicate {
32 bool operator()(int);
33 };
34
35 template <class It, class Sent = It>
36 concept HasFindIfIt = requires(It it, Sent sent) { std::ranges::find_if(it, sent, Predicate{}); };
37 static_assert(HasFindIfIt<int*>);
38 static_assert(!HasFindIfIt<InputIteratorNotDerivedFrom>);
39 static_assert(!HasFindIfIt<InputIteratorNotIndirectlyReadable>);
40 static_assert(!HasFindIfIt<InputIteratorNotInputOrOutputIterator>);
41 static_assert(!HasFindIfIt<cpp20_input_iterator<int*>, SentinelForNotSemiregular>);
42 static_assert(!HasFindIfIt<cpp20_input_iterator<int*>, InputRangeNotSentinelEqualityComparableWith>);
43
44 static_assert(!HasFindIfIt<int*, int>);
45 static_assert(!HasFindIfIt<int, int*>);
46
47 template <class Pred>
48 concept HasFindIfPred = requires(int* it, Pred pred) {std::ranges::find_if(it, it, pred); };
49
50 static_assert(!HasFindIfPred<IndirectUnaryPredicateNotCopyConstructible>);
51 static_assert(!HasFindIfPred<IndirectUnaryPredicateNotPredicate>);
52
53 template <class R>
54 concept HasFindIfR = requires(R r) { std::ranges::find_if(r, Predicate{}); };
55 static_assert(HasFindIfR<std::array<int, 0>>);
56 static_assert(!HasFindIfR<int>);
57 static_assert(!HasFindIfR<InputRangeNotDerivedFrom>);
58 static_assert(!HasFindIfR<InputRangeNotIndirectlyReadable>);
59 static_assert(!HasFindIfR<InputRangeNotInputOrOutputIterator>);
60 static_assert(!HasFindIfR<InputRangeNotSentinelSemiregular>);
61 static_assert(!HasFindIfR<InputRangeNotSentinelEqualityComparableWith>);
62
63 template <class It, class Sent = It>
test_iterators()64 constexpr void test_iterators() {
65 {
66 int a[] = {1, 2, 3, 4};
67 std::same_as<It> auto ret = std::ranges::find_if(It(a), Sent(It(a + 4)), [](int x) mutable { return x == 4; });
68 assert(base(ret) == a + 3);
69 assert(*ret == 4);
70 }
71 {
72 int a[] = {1, 2, 3, 4};
73 auto range = std::ranges::subrange(It(a), Sent(It(a + 4)));
74 std::same_as<It> auto ret = std::ranges::find_if(range, [](int x) mutable { return x == 4; });
75 assert(base(ret) == a + 3);
76 assert(*ret == 4);
77 }
78 }
79
80 struct NonConstComparable {
operator ==(const NonConstComparable &,const NonConstComparable &)81 friend constexpr bool operator==(const NonConstComparable&, const NonConstComparable&) { return false; }
operator ==(NonConstComparable &,NonConstComparable &)82 friend constexpr bool operator==(NonConstComparable&, NonConstComparable&) { return false; }
operator ==(const NonConstComparable &,NonConstComparable &)83 friend constexpr bool operator==(const NonConstComparable&, NonConstComparable&) { return false; }
operator ==(NonConstComparable &,const NonConstComparable &)84 friend constexpr bool operator==(NonConstComparable&, const NonConstComparable&) { return true; }
85 };
86
test()87 constexpr bool test() {
88 test_iterators<int*>();
89 test_iterators<const int*>();
90 test_iterators<cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>>();
91 test_iterators<bidirectional_iterator<int*>>();
92 test_iterators<forward_iterator<int*>>();
93 test_iterators<random_access_iterator<int*>>();
94 test_iterators<contiguous_iterator<int*>>();
95
96 {
97 // check that projections are used properly and that they are called with the iterator directly
98 {
99 int a[] = {1, 2, 3, 4};
100 auto ret = std::ranges::find_if(a, a + 4, [&](int* i) { return i == a + 3; }, [](int& i) { return &i; });
101 assert(ret == a + 3);
102 }
103 {
104 int a[] = {1, 2, 3, 4};
105 auto ret = std::ranges::find_if(a, [&](int* i) { return i == a + 3; }, [](int& i) { return &i; });
106 assert(ret == a + 3);
107 }
108 }
109
110 {
111 // check that the first element is returned
112 {
113 struct S {
114 int comp;
115 int other;
116 };
117 S a[] = { {0, 0}, {0, 2}, {0, 1} };
118 auto ret = std::ranges::find_if(a, [](int i){ return i == 0; }, &S::comp);
119 assert(ret == a);
120 assert(ret->comp == 0);
121 assert(ret->other == 0);
122 }
123 {
124 struct S {
125 int comp;
126 int other;
127 };
128 S a[] = { {0, 0}, {0, 2}, {0, 1} };
129 auto ret = std::ranges::find_if(a, a + 3, [](int i) { return i == 0; }, &S::comp);
130 assert(ret == a);
131 assert(ret->comp == 0);
132 assert(ret->other == 0);
133 }
134 }
135
136 {
137 // check that end + 1 iterator is returned with no match
138 {
139 int a[] = {1, 1, 1};
140 auto ret = std::ranges::find_if(a, a + 3, [](int) { return false; });
141 assert(ret == a + 3);
142 }
143 {
144 int a[] = {1, 1, 1};
145 auto ret = std::ranges::find_if(a, [](int){ return false; });
146 assert(ret == a + 3);
147 }
148 }
149
150 {
151 // check that ranges::dangling is returned
152 [[maybe_unused]] std::same_as<std::ranges::dangling> auto ret =
153 std::ranges::find_if(std::array{1, 2}, [](int){ return false; });
154 }
155
156 {
157 // check that an iterator is returned with a borrowing range
158 int a[] = {1, 2, 3, 4};
159 std::same_as<int*> auto ret = std::ranges::find_if(std::views::all(a), [](int){ return true; });
160 assert(ret == a);
161 assert(*ret == 1);
162 }
163
164 {
165 // check that std::invoke is used
166 struct S { int i; };
167 S a[] = { S{1}, S{3}, S{2} };
168 std::same_as<S*> auto ret = std::ranges::find_if(a, [](int) { return false; }, &S::i);
169 assert(ret == a + 3);
170 }
171
172 {
173 // count projection and predicate invocation count
174 {
175 int a[] = {1, 2, 3, 4};
176 int predicate_count = 0;
177 int projection_count = 0;
178 auto ret = std::ranges::find_if(a, a + 4,
179 [&](int i) { ++predicate_count; return i == 2; },
180 [&](int i) { ++projection_count; return i; });
181 assert(ret == a + 1);
182 assert(*ret == 2);
183 assert(predicate_count == 2);
184 assert(projection_count == 2);
185 }
186 {
187 int a[] = {1, 2, 3, 4};
188 int predicate_count = 0;
189 int projection_count = 0;
190 auto ret = std::ranges::find_if(a,
191 [&](int i) { ++predicate_count; return i == 2; },
192 [&](int i) { ++projection_count; return i; });
193 assert(ret == a + 1);
194 assert(*ret == 2);
195 assert(predicate_count == 2);
196 assert(projection_count == 2);
197 }
198 }
199
200 {
201 // check that the return type of `iter::operator*` doesn't change
202 {
203 NonConstComparable a[] = { NonConstComparable{} };
204 auto ret = std::ranges::find_if(a, a + 1, [](auto&& e) { return e == NonConstComparable{}; });
205 assert(ret == a);
206 }
207 {
208 NonConstComparable a[] = { NonConstComparable{} };
209 auto ret = std::ranges::find_if(a, [](auto&& e) { return e == NonConstComparable{}; });
210 assert(ret == a);
211 }
212 }
213
214 {
215 // check that an empty range works
216 {
217 std::array<int ,0> a = {};
218 auto ret = std::ranges::find_if(a.begin(), a.end(), [](int) { return true; });
219 assert(ret == a.begin());
220 }
221 {
222 std::array<int, 0> a = {};
223 auto ret = std::ranges::find_if(a, [](int) { return true; });
224 assert(ret == a.begin());
225 }
226 }
227
228 {
229 // check that the implicit conversion to bool works
230 {
231 int a[] = {1, 2, 3, 4};
232 auto ret = std::ranges::find_if(a, a + 4, [](const int& i) { return BooleanTestable{i == 3}; });
233 assert(ret == a + 2);
234 }
235 {
236 int a[] = {1, 2, 3, 4};
237 auto ret = std::ranges::find_if(a, [](const int& b) { return BooleanTestable{b == 3}; });
238 assert(ret == a + 2);
239 }
240 }
241
242 return true;
243 }
244
main(int,char **)245 int main(int, char**) {
246 test();
247 static_assert(test());
248
249 return 0;
250 }
251