Skip to content

Commit 02e9afd

Browse files
committed
[libc++] Implement ranges::contains
Differential Revision: https://reviews.llvm.org/D159232
1 parent 067bd7d commit 02e9afd

File tree

6 files changed

+265
-0
lines changed

6 files changed

+265
-0
lines changed

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ set(files
104104
__algorithm/ranges_any_of.h
105105
__algorithm/ranges_binary_search.h
106106
__algorithm/ranges_clamp.h
107+
__algorithm/ranges_contains.h
107108
__algorithm/ranges_copy.h
108109
__algorithm/ranges_copy_backward.h
109110
__algorithm/ranges_copy_if.h
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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+
#ifndef _LIBCPP___ALGORITHM_RANGES_CONTAINS_H
10+
#define _LIBCPP___ALGORITHM_RANGES_CONTAINS_H
11+
12+
#include <__algorithm/in_in_result.h>
13+
#include <__algorithm/ranges_find.h>
14+
#include <__config>
15+
#include <__functional/identity.h>
16+
#include <__functional/ranges_operations.h>
17+
#include <__functional/reference_wrapper.h>
18+
#include <__iterator/concepts.h>
19+
#include <__iterator/indirectly_comparable.h>
20+
#include <__iterator/projected.h>
21+
#include <__ranges/access.h>
22+
#include <__ranges/concepts.h>
23+
#include <__utility/move.h>
24+
25+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
26+
# pragma GCC system_header
27+
#endif
28+
29+
#if _LIBCPP_STD_VER >= 23
30+
31+
_LIBCPP_BEGIN_NAMESPACE_STD
32+
33+
namespace ranges {
34+
namespace __contains {
35+
struct __fn {
36+
template <input_iterator _Iter, sentinel_for<_Iter> _Sent, class _Type, class _Proj = identity>
37+
requires indirect_binary_predicate<ranges::equal_to, projected<_Iter, _Proj>, const _Type*>
38+
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr bool
39+
operator()(_Iter __first, _Sent __last, const _Type& __value, _Proj __proj = {}) const {
40+
return ranges::find(std::move(__first), std::move(__last), __value, std::ref(__proj)) != __last;
41+
}
42+
43+
template <input_range _Range, class _Type, class _Proj = identity>
44+
requires indirect_binary_predicate<ranges::equal_to, projected<iterator_t<_Range>, _Proj>, const _Type*>
45+
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr bool
46+
operator()(_Range&& __range, const _Type& __value, _Proj __proj = {}) const {
47+
return ranges::find(ranges::begin(__range), ranges::end(__range), __value, std::ref(__proj)) != ranges::end(__range);
48+
}
49+
};
50+
} // namespace __contains
51+
inline namespace __cpo {
52+
inline constexpr auto contains = __contains::__fn{};
53+
} // namespace __cpo
54+
} // namespace ranges
55+
56+
_LIBCPP_END_NAMESPACE_STD
57+
58+
#endif // _LIBCPP_STD_VER >= 23
59+
60+
#endif // _LIBCPP___ALGORITHM_RANGES_CONTAINS_H

libcxx/include/algorithm

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,14 @@ namespace ranges {
226226
template<class I1, class I2>
227227
using copy_backward_result = in_out_result<I1, I2>; // since C++20
228228
229+
template<input_iterator I, sentinel_for<I> S, class T, class Proj = identity>
230+
requires indirect_binary_predicate<ranges::equal_to, projected<I, Proj>, const T*>
231+
constexpr bool ranges::contains(I first, S last, const T& value, Proj proj = {}); // since C++23
232+
233+
template<input_range R, class T, class Proj = identity>
234+
requires indirect_binary_predicate<ranges::equal_to, projected<iterator_t<R>, Proj>, const T*>
235+
constexpr bool ranges::contains(R&& r, const T& value, Proj proj = {}); // since C++23
236+
229237
template<input_iterator I, sentinel_for<I> S, weakly_incrementable O>
230238
requires indirectly_copyable<I, O>
231239
constexpr ranges::copy_result<I, O> ranges::copy(I first, S last, O result); // since C++20
@@ -1827,6 +1835,7 @@ template <class BidirectionalIterator, class Compare>
18271835
#include <__algorithm/ranges_any_of.h>
18281836
#include <__algorithm/ranges_binary_search.h>
18291837
#include <__algorithm/ranges_clamp.h>
1838+
#include <__algorithm/ranges_contains.h>
18301839
#include <__algorithm/ranges_copy.h>
18311840
#include <__algorithm/ranges_copy_backward.h>
18321841
#include <__algorithm/ranges_copy_if.h>

libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ constexpr bool all_the_algorithms()
8080
(void)std::ranges::binary_search(first, last, value, Less(), Proj(&copies)); assert(copies == 0);
8181
(void)std::ranges::binary_search(a, value, Less(), Proj(&copies)); assert(copies == 0);
8282
(void)std::ranges::clamp(T(), T(), T(), Less(), Proj(&copies)); assert(copies == 0);
83+
#if TEST_STD_VER >= 23
84+
(void)std::ranges::contains(first, last, value, Proj(&copies)); assert(copies == 0);
85+
(void)std::ranges::contains(a, value, Proj(&copies)); assert(copies == 0);
86+
#endif
8387
(void)std::ranges::count(first, last, value, Proj(&copies)); assert(copies == 0);
8488
(void)std::ranges::count(a, value, Proj(&copies)); assert(copies == 0);
8589
(void)std::ranges::count_if(first, last, UnaryTrue(), Proj(&copies)); assert(copies == 0);
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
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, c++20
12+
// template<input_iterator I, sentinel_for<I> S, class T, class Proj = identity>
13+
// requires indirect_binary_predicate<ranges::equal_to, projected<I, Proj>, const T*>
14+
// constexpr bool ranges::contains(I first, S last, const T& value, Proj proj = {}); // since C++23
15+
16+
// template<input_range R, class T, class Proj = identity>
17+
// requires indirect_binary_predicate<ranges::equal_to, projected<iterator_t<R>, Proj>, const T*>
18+
// constexpr bool ranges::contains(R&& r, const T& value, Proj proj = {}); // since C++23
19+
20+
#include <algorithm>
21+
#include <array>
22+
#include <cassert>
23+
#include <ranges>
24+
#include <vector>
25+
26+
#include "almost_satisfies_types.h"
27+
#include "boolean_testable.h"
28+
#include "test_iterators.h"
29+
30+
struct NotEqualityComparable {};
31+
32+
template <class Iter, class Sent = Iter>
33+
concept HasContainsIt = requires(Iter iter, Sent sent) { std::ranges::contains(iter, sent, *iter); };
34+
35+
static_assert(HasContainsIt<int*>);
36+
static_assert(!HasContainsIt<NotEqualityComparable*>);
37+
static_assert(!HasContainsIt<InputIteratorNotDerivedFrom>);
38+
static_assert(!HasContainsIt<InputIteratorNotIndirectlyReadable>);
39+
static_assert(!HasContainsIt<InputIteratorNotInputOrOutputIterator>);
40+
static_assert(!HasContainsIt<cpp20_input_iterator<int*>, SentinelForNotSemiregular>);
41+
static_assert(!HasContainsIt<cpp20_input_iterator<int*>, InputRangeNotSentinelEqualityComparableWith>);
42+
43+
static_assert(!HasContainsIt<int*, int>);
44+
static_assert(!HasContainsIt<int, int*>);
45+
46+
template <class Range, class ValT>
47+
concept HasContainsR = requires(Range range) { std::ranges::contains(range, ValT{}); };
48+
49+
static_assert(HasContainsR<std::array<int, 1>, int>);
50+
static_assert(!HasContainsR<int, int>);
51+
static_assert(!HasContainsR<std::array<NotEqualityComparable, 1>, NotEqualityComparable>);
52+
static_assert(!HasContainsR<InputRangeNotDerivedFrom, int>);
53+
static_assert(!HasContainsR<InputRangeNotIndirectlyReadable, int>);
54+
static_assert(!HasContainsR<InputRangeNotInputOrOutputIterator, int>);
55+
static_assert(!HasContainsR<InputRangeNotSentinelSemiregular, int>);
56+
static_assert(!HasContainsR<InputRangeNotSentinelEqualityComparableWith, int>);
57+
58+
static std::vector<int> comparable_data;
59+
60+
// clang-format off
61+
template <class Iter, class Sent = Iter>
62+
constexpr void test_iterators() {
63+
using ValueT = std::iter_value_t<Iter>;
64+
{ // simple tests
65+
{
66+
ValueT a[] = {1, 2, 3, 4, 5, 6};
67+
std::same_as<bool> auto ret =
68+
std::ranges::contains(Iter(a), Sent(Iter(a + 6)), 3);
69+
assert(ret);
70+
}
71+
{
72+
ValueT a[] = {1, 2, 3, 4, 5, 6};
73+
auto range = std::ranges::subrange(Iter(a), Sent(Iter(a + 6)));
74+
std::same_as<bool> decltype(auto) ret =
75+
std::ranges::contains(range, 3);
76+
assert(ret);
77+
}
78+
}
79+
80+
{ // check that an empty range works
81+
{
82+
ValueT a[] = {};
83+
auto ret = std::ranges::contains(Iter(a), Sent(Iter(a)), 1);
84+
assert(!ret);
85+
}
86+
{
87+
ValueT a[] = {};
88+
auto range = std::ranges::subrange(Iter(a), Sent(Iter(a)));
89+
auto ret = std::ranges::contains(range, 1);
90+
assert(!ret);
91+
}
92+
}
93+
94+
{ // check that no match
95+
{
96+
ValueT a[] = {13, 1, 21, 4, 5};
97+
auto ret = std::ranges::contains(Iter(a), Sent(Iter(a + 5)), 10);
98+
assert(!ret);
99+
}
100+
{
101+
ValueT a[] = {13, 1, 21, 4, 5};
102+
auto range = std::ranges::subrange(Iter(a), Sent(Iter(a + 5)));
103+
auto ret = std::ranges::contains(range, 10);
104+
assert(!ret);
105+
}
106+
}
107+
108+
if (!std::is_constant_evaluated())
109+
comparable_data.clear();
110+
}
111+
template <class ElementT>
112+
class TriviallyComparable {
113+
ElementT el_;
114+
115+
public:
116+
TEST_CONSTEXPR TriviallyComparable(ElementT el) : el_(el) {}
117+
bool operator==(const TriviallyComparable&) const = default;
118+
};
119+
120+
template <class IndexT>
121+
class Comparable {
122+
IndexT index_;
123+
124+
public:
125+
Comparable(IndexT i)
126+
: index_([&]() {
127+
IndexT size = static_cast<IndexT>(comparable_data.size());
128+
comparable_data.push_back(i);
129+
return size;
130+
}()) {}
131+
132+
bool operator==(const Comparable& other) const {
133+
return comparable_data[other.index_] == comparable_data[index_];
134+
}
135+
136+
friend bool operator==(const Comparable& lhs, long long rhs) { return comparable_data[lhs.index_] == rhs; }
137+
};
138+
139+
constexpr bool test() {
140+
types::for_each(types::type_list<char, wchar_t, int, long,
141+
TriviallyComparable<char>, TriviallyComparable<wchar_t>>{},
142+
[]<class T> {
143+
types::for_each(types::cpp20_input_iterator_list<T*>{},
144+
[]<class Iter> {
145+
if constexpr (std::forward_iterator<Iter>)
146+
test_iterators<Iter>();
147+
test_iterators<Iter, sentinel_wrapper<Iter>>();
148+
test_iterators<Iter, sized_sentinel<Iter>>();
149+
});
150+
});
151+
152+
{
153+
// count invocations of the projection
154+
{
155+
int a[] = {1, 9, 0, 13, 25};
156+
int projection_count = 0;
157+
auto ret = std::ranges::contains(a, a + 5, 0,
158+
[&](int i) { ++projection_count; return i; });
159+
assert(ret);
160+
assert(projection_count == 3);
161+
}
162+
{
163+
int a[] ={1, 9, 0, 13, 25};
164+
int projection_count = 0;
165+
auto range = std::ranges::subrange(a, a + 5);
166+
auto ret = std::ranges::contains(range, 0, [&](int i) { ++projection_count; return i; });
167+
assert(ret);
168+
assert(projection_count == 3);
169+
}
170+
}
171+
return true;
172+
}
173+
174+
int main(int, char**) {
175+
test();
176+
static_assert(test());
177+
178+
types::for_each(types::type_list<Comparable<char>, Comparable<wchar_t>>{},
179+
[]<class T> {
180+
types::for_each(types::cpp20_input_iterator_list<T*>{},
181+
[]<class Iter> {
182+
if constexpr (std::forward_iterator<Iter>)
183+
test_iterators<Iter>();
184+
test_iterators<Iter, sentinel_wrapper<Iter>>();
185+
test_iterators<Iter, sized_sentinel<Iter>>();
186+
});
187+
});
188+
189+
return 0;
190+
}

libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ static_assert(test(std::ranges::all_of, a, odd));
6565
static_assert(test(std::ranges::any_of, a, odd));
6666
static_assert(test(std::ranges::binary_search, a, 42));
6767
static_assert(test(std::ranges::clamp, 42, 42, 42));
68+
static_assert(test(std::ranges::contains, a, 42));
6869
static_assert(test(std::ranges::copy, a, a));
6970
static_assert(test(std::ranges::copy_backward, a, a));
7071
static_assert(test(std::ranges::copy_if, a, a, odd));

0 commit comments

Comments
 (0)