Skip to content

Commit fdd089b

Browse files
[libc++] Implement ranges::contains (#65148)
Differential Revision: https://reviews.llvm.org/D159232 ``` Running ./ranges_contains.libcxx.out Run on (10 X 24.121 MHz CPU s) CPU Caches: L1 Data 64 KiB (x10) L1 Instruction 128 KiB (x10) L2 Unified 4096 KiB (x5) Load Average: 3.37, 6.77, 5.27 -------------------------------------------------------------------- Benchmark Time CPU Iterations -------------------------------------------------------------------- bm_contains_char/16 1.88 ns 1.87 ns 371607095 bm_contains_char/256 7.48 ns 7.47 ns 93292285 bm_contains_char/4096 99.7 ns 99.6 ns 7013185 bm_contains_char/65536 1296 ns 1294 ns 540436 bm_contains_char/1048576 23887 ns 23860 ns 29302 bm_contains_char/16777216 389420 ns 389095 ns 1796 bm_contains_int/16 7.14 ns 7.14 ns 97776288 bm_contains_int/256 90.4 ns 90.3 ns 7558089 bm_contains_int/4096 1294 ns 1290 ns 543052 bm_contains_int/65536 20482 ns 20443 ns 34334 bm_contains_int/1048576 328817 ns 327965 ns 2147 bm_contains_int/16777216 5246279 ns 5239361 ns 133 bm_contains_bool/16 2.19 ns 2.19 ns 322565780 bm_contains_bool/256 3.42 ns 3.41 ns 205025467 bm_contains_bool/4096 22.1 ns 22.1 ns 31780479 bm_contains_bool/65536 333 ns 332 ns 2106606 bm_contains_bool/1048576 5126 ns 5119 ns 135901 bm_contains_bool/16777216 81656 ns 81574 ns 8569 ``` --------- Co-authored-by: Nathan Gauër <[email protected]>
1 parent 2276491 commit fdd089b

File tree

15 files changed

+450
-2
lines changed

15 files changed

+450
-2
lines changed

libcxx/benchmarks/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ set(BENCHMARK_TESTS
185185
algorithms/pop_heap.bench.cpp
186186
algorithms/pstl.stable_sort.bench.cpp
187187
algorithms/push_heap.bench.cpp
188+
algorithms/ranges_contains.bench.cpp
188189
algorithms/ranges_ends_with.bench.cpp
189190
algorithms/ranges_make_heap.bench.cpp
190191
algorithms/ranges_make_heap_then_sort_heap.bench.cpp
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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+
#include <algorithm>
10+
#include <benchmark/benchmark.h>
11+
#include <iterator>
12+
#include <vector>
13+
14+
#include "test_iterators.h"
15+
16+
static void bm_contains_char(benchmark::State& state) {
17+
std::vector<char> a(state.range(), 'a');
18+
19+
for (auto _ : state) {
20+
benchmark::DoNotOptimize(a);
21+
22+
benchmark::DoNotOptimize(std::ranges::contains(a.begin(), a.end(), 'B'));
23+
}
24+
}
25+
BENCHMARK(bm_contains_char)->RangeMultiplier(16)->Range(16, 16 << 20);
26+
27+
static void bm_contains_int(benchmark::State& state) {
28+
std::vector<int> a(state.range(), 1);
29+
30+
for (auto _ : state) {
31+
benchmark::DoNotOptimize(a);
32+
33+
benchmark::DoNotOptimize(std::ranges::contains(a.begin(), a.end(), 2));
34+
}
35+
}
36+
BENCHMARK(bm_contains_int)->RangeMultiplier(16)->Range(16, 16 << 20);
37+
38+
static void bm_contains_bool(benchmark::State& state) {
39+
std::vector<bool> a(state.range(), true);
40+
41+
for (auto _ : state) {
42+
benchmark::DoNotOptimize(a);
43+
44+
benchmark::DoNotOptimize(std::ranges::contains(a.begin(), a.end(), false));
45+
}
46+
}
47+
BENCHMARK(bm_contains_bool)->RangeMultiplier(16)->Range(16, 16 << 20);
48+
49+
BENCHMARK_MAIN();

libcxx/include/CMakeLists.txt

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

libcxx/include/__functional/identity.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#define _LIBCPP___FUNCTIONAL_IDENTITY_H
1212

1313
#include <__config>
14+
#include <__functional/reference_wrapper.h>
1415
#include <__type_traits/integral_constant.h>
1516
#include <__utility/forward.h>
1617

@@ -34,6 +35,10 @@ struct __identity {
3435

3536
template <>
3637
struct __is_identity<__identity> : true_type {};
38+
template <>
39+
struct __is_identity<reference_wrapper<__identity> > : true_type {};
40+
template <>
41+
struct __is_identity<reference_wrapper<const __identity> > : true_type {};
3742

3843
#if _LIBCPP_STD_VER >= 20
3944

@@ -48,6 +53,10 @@ struct identity {
4853

4954
template <>
5055
struct __is_identity<identity> : true_type {};
56+
template <>
57+
struct __is_identity<reference_wrapper<identity> > : true_type {};
58+
template <>
59+
struct __is_identity<reference_wrapper<const identity> > : true_type {};
5160

5261
#endif // _LIBCPP_STD_VER >= 20
5362

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
@@ -1845,6 +1853,7 @@ template <class BidirectionalIterator, class Compare>
18451853
#include <__algorithm/ranges_any_of.h>
18461854
#include <__algorithm/ranges_binary_search.h>
18471855
#include <__algorithm/ranges_clamp.h>
1856+
#include <__algorithm/ranges_contains.h>
18481857
#include <__algorithm/ranges_copy.h>
18491858
#include <__algorithm/ranges_copy_backward.h>
18501859
#include <__algorithm/ranges_copy_if.h>

libcxx/include/module.modulemap.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,7 @@ module std_private_algorithm_ranges_clamp [system
787787
header "__algorithm/ranges_clamp.h"
788788
export std_private_functional_ranges_operations
789789
}
790+
module std_private_algorithm_ranges_contains [system] { header "__algorithm/ranges_contains.h" }
790791
module std_private_algorithm_ranges_copy [system] {
791792
header "__algorithm/ranges_copy.h"
792793
export std_private_algorithm_in_out_result

libcxx/modules/std/algorithm.inc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@ export namespace std {
4141
}
4242

4343
// [alg.contains], contains
44-
#if 0
4544
namespace ranges {
4645
using std::ranges::contains;
46+
#if 0
4747
using std::ranges::contains_subrange;
48-
} // namespace ranges
4948
#endif
49+
} // namespace ranges
5050

5151
// [alg.foreach], for each
5252
using std::for_each;

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ constexpr bool all_the_algorithms()
8181
(void)std::ranges::binary_search(first, last, value, Less(), Proj(&copies)); assert(copies == 0);
8282
(void)std::ranges::binary_search(a, value, Less(), Proj(&copies)); assert(copies == 0);
8383
(void)std::ranges::clamp(T(), T(), T(), Less(), Proj(&copies)); assert(copies == 0);
84+
#if TEST_STD_VER >= 23
85+
(void)std::ranges::contains(first, last, value, Proj(&copies));
86+
assert(copies == 0);
87+
(void)std::ranges::contains(a, value, Proj(&copies));
88+
assert(copies == 0);
89+
#endif
8490
(void)std::ranges::count(first, last, value, Proj(&copies)); assert(copies == 0);
8591
(void)std::ranges::count(a, value, Proj(&copies)); assert(copies == 0);
8692
(void)std::ranges::count_if(first, last, UnaryTrue(), Proj(&copies)); assert(copies == 0);

libcxx/test/libcxx/diagnostics/nodiscard_extensions.compile.pass.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ void test_algorithms() {
4545
#if TEST_STD_VER >= 17
4646
std::clamp(2, 1, 3);
4747
std::clamp(2, 3, 1, std::greater<int>());
48+
#endif
49+
#if TEST_STD_VER >= 23
50+
std::ranges::contains(arr, arr + 1, 1);
4851
#endif
4952
std::count_if(std::begin(arr), std::end(arr), P());
5053
std::count(std::begin(arr), std::end(arr), 1);

libcxx/test/libcxx/diagnostics/nodiscard_extensions.verify.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ void test_algorithms() {
6060
std::clamp(2, 1, 3, std::greater<int>());
6161
#endif
6262

63+
#if TEST_STD_VER >= 23
64+
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
65+
std::ranges::contains(arr, arr + 1, 1);
66+
#endif
67+
6368
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
6469
std::count_if(std::begin(arr), std::end(arr), P());
6570

0 commit comments

Comments
 (0)