Skip to content

Commit 097d62a

Browse files
strega-nilyuxuanchen1997
authored andcommitted
[libc++][ranges] P1223R5: find_last (#99312)
Implements [P1223R5][] completely. Includes an implementation of `find_last`, `find_last_if`, and `find_last_if_not`. [P1223R5]: https://wg21.link/p1223r5
1 parent 3ee2926 commit 097d62a

23 files changed

+1045
-6
lines changed

libcxx/docs/FeatureTestMacroTable.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,8 @@ Status
360360
---------------------------------------------------------- -----------------
361361
``__cpp_lib_ranges_contains`` ``202207L``
362362
---------------------------------------------------------- -----------------
363+
``__cpp_lib_ranges_find_last`` ``202207L``
364+
---------------------------------------------------------- -----------------
363365
``__cpp_lib_ranges_iota`` *unimplemented*
364366
---------------------------------------------------------- -----------------
365367
``__cpp_lib_ranges_join_with`` *unimplemented*

libcxx/docs/ReleaseNotes/19.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ Implemented Papers
5858
- P2231R1 - Missing ``constexpr`` in ``std::optional`` and ``std::variant``
5959
- P0019R8 - ``std::atomic_ref``
6060
- P2389R2 - Alias template ``dims`` for the ``extents`` of ``mdspan``
61+
- P1223R5 - ``ranges::find_last()``, ``ranges::find_last_if()``, and ``ranges::find_last_if_not()``
6162

6263
Improvements and New Features
6364
-----------------------------

libcxx/docs/Status/Cxx23Papers.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
"`P0429R9 <https://wg21.link/P0429R9>`__","LWG","A Standard ``flat_map``","July 2022","",""
5656
"`P1169R4 <https://wg21.link/P1169R4>`__","LWG","``static operator()``","July 2022","|Complete|","16.0"
5757
"`P1222R4 <https://wg21.link/P1222R4>`__","LWG","A Standard ``flat_set``","July 2022","",""
58-
"`P1223R5 <https://wg21.link/P1223R5>`__","LWG","``ranges::find_last()``, ``ranges::find_last_if()``, and ``ranges::find_last_if_not()``","July 2022","","","|ranges|"
58+
"`P1223R5 <https://wg21.link/P1223R5>`__","LWG","``ranges::find_last()``, ``ranges::find_last_if()``, and ``ranges::find_last_if_not()``","July 2022","|Complete|","19.0","|ranges|"
5959
"`P1467R9 <https://wg21.link/P1467R9>`__","LWG","Extended ``floating-point`` types and standard names","July 2022","",""
6060
"`P1642R11 <https://wg21.link/P1642R11>`__","LWG","Freestanding ``[utilities]``, ``[ranges]``, and ``[iterators]``","July 2022","",""
6161
"`P1899R3 <https://wg21.link/P1899R3>`__","LWG","``stride_view``","July 2022","","","|ranges|"

libcxx/docs/Status/RangesAlgorithms.csv

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
Standard,Algorithm,Assignee,CL,Status
22
C++20,all C++20 algorithms,N/A,N/A,✅
3-
C++23,`find_last <https://wg21.link/P1223R5>`_,Unassigned,No patch yet,Not started
4-
C++23,`find_last_if <https://wg21.link/P1223R5>`_,Unassigned,No patch yet,Not started
5-
C++23,`find_last_if_not <https://wg21.link/P1223R5>`_,Unassigned,No patch yet,Not started
3+
C++23,`find_last <https://wg21.link/P1223R5>`_,Nicole Mazzuca,`#99312 <https://github.com/llvm/llvm-project/pull/99312>`_,Complete
4+
C++23,`find_last_if <https://wg21.link/P1223R5>`_,Nicole Mazzuca,`#99312 <https://github.com/llvm/llvm-project/pull/99312>`_,Complete
5+
C++23,`find_last_if_not <https://wg21.link/P1223R5>`_,Nicole Mazzuca,`#99312 <https://github.com/llvm/llvm-project/pull/99312>`_,Complete
66
C++23,`starts_with <https://wg21.link/P1659R3>`_,Zijun Zhao,`D150735 <https://llvm.org/D150735>`_,Complete
77
C++23,`ends_with <https://wg21.link/P1659R3>`_,Zijun Zhao, `D150831 <https://llvm.org/D150831>`_,Complete
88
C++23,`shift_left <https://wg21.link/p2440r1>`_,Unassigned,No patch yet,Not started

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ set(files
9797
__algorithm/ranges_find_first_of.h
9898
__algorithm/ranges_find_if.h
9999
__algorithm/ranges_find_if_not.h
100+
__algorithm/ranges_find_last.h
100101
__algorithm/ranges_for_each.h
101102
__algorithm/ranges_for_each_n.h
102103
__algorithm/ranges_generate.h
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
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_FIND_LAST_H
10+
#define _LIBCPP___ALGORITHM_RANGES_FIND_LAST_H
11+
12+
#include <__config>
13+
#include <__functional/identity.h>
14+
#include <__functional/invoke.h>
15+
#include <__functional/ranges_operations.h>
16+
#include <__iterator/concepts.h>
17+
#include <__iterator/indirectly_comparable.h>
18+
#include <__iterator/next.h>
19+
#include <__iterator/prev.h>
20+
#include <__iterator/projected.h>
21+
#include <__ranges/access.h>
22+
#include <__ranges/concepts.h>
23+
#include <__ranges/subrange.h>
24+
#include <__utility/move.h>
25+
26+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
27+
# pragma GCC system_header
28+
#endif
29+
30+
_LIBCPP_PUSH_MACROS
31+
#include <__undef_macros>
32+
33+
#if _LIBCPP_STD_VER >= 23
34+
35+
_LIBCPP_BEGIN_NAMESPACE_STD
36+
37+
namespace ranges {
38+
39+
template <class _Iter, class _Sent, class _Pred, class _Proj>
40+
_LIBCPP_HIDE_FROM_ABI constexpr subrange<_Iter>
41+
__find_last_impl(_Iter __first, _Sent __last, _Pred __pred, _Proj& __proj) {
42+
if (__first == __last) {
43+
return subrange<_Iter>(__first, __first);
44+
}
45+
46+
if constexpr (bidirectional_iterator<_Iter>) {
47+
auto __last_it = ranges::next(__first, __last);
48+
for (auto __it = ranges::prev(__last_it); __it != __first; --__it) {
49+
if (__pred(std::invoke(__proj, *__it))) {
50+
return subrange<_Iter>(std::move(__it), std::move(__last_it));
51+
}
52+
}
53+
if (__pred(std::invoke(__proj, *__first))) {
54+
return subrange<_Iter>(std::move(__first), std::move(__last_it));
55+
}
56+
return subrange<_Iter>(__last_it, __last_it);
57+
} else {
58+
bool __found = false;
59+
_Iter __found_it;
60+
for (; __first != __last; ++__first) {
61+
if (__pred(std::invoke(__proj, *__first))) {
62+
__found = true;
63+
__found_it = __first;
64+
}
65+
}
66+
67+
if (__found) {
68+
return subrange<_Iter>(std::move(__found_it), std::move(__first));
69+
} else {
70+
return subrange<_Iter>(__first, __first);
71+
}
72+
}
73+
}
74+
75+
namespace __find_last {
76+
struct __fn {
77+
template <class _Type>
78+
struct __op {
79+
const _Type& __value;
80+
template <class _Elem>
81+
_LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator()(_Elem&& __elem) const {
82+
return std::forward<_Elem>(__elem) == __value;
83+
}
84+
};
85+
86+
template <forward_iterator _Iter, sentinel_for<_Iter> _Sent, class _Type, class _Proj = identity>
87+
requires indirect_binary_predicate<ranges::equal_to, projected<_Iter, _Proj>, const _Type*>
88+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static subrange<_Iter>
89+
operator()(_Iter __first, _Sent __last, const _Type& __value, _Proj __proj = {}) {
90+
return ranges::__find_last_impl(std::move(__first), std::move(__last), __op<_Type>{__value}, __proj);
91+
}
92+
93+
template <forward_range _Range, class _Type, class _Proj = identity>
94+
requires indirect_binary_predicate<ranges::equal_to, projected<iterator_t<_Range>, _Proj>, const _Type*>
95+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static borrowed_subrange_t<_Range>
96+
operator()(_Range&& __range, const _Type& __value, _Proj __proj = {}) {
97+
return ranges::__find_last_impl(ranges::begin(__range), ranges::end(__range), __op<_Type>{__value}, __proj);
98+
}
99+
};
100+
} // namespace __find_last
101+
102+
namespace __find_last_if {
103+
struct __fn {
104+
template <class _Pred>
105+
struct __op {
106+
_Pred& __pred;
107+
template <class _Elem>
108+
_LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator()(_Elem&& __elem) const {
109+
return std::invoke(__pred, std::forward<_Elem>(__elem));
110+
}
111+
};
112+
113+
template <forward_iterator _Iter,
114+
sentinel_for<_Iter> _Sent,
115+
class _Proj = identity,
116+
indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
117+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static subrange<_Iter>
118+
operator()(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {}) {
119+
return ranges::__find_last_impl(std::move(__first), std::move(__last), __op<_Pred>{__pred}, __proj);
120+
}
121+
122+
template <forward_range _Range,
123+
class _Proj = identity,
124+
indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Pred>
125+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static borrowed_subrange_t<_Range>
126+
operator()(_Range&& __range, _Pred __pred, _Proj __proj = {}) {
127+
return ranges::__find_last_impl(ranges::begin(__range), ranges::end(__range), __op<_Pred>{__pred}, __proj);
128+
}
129+
};
130+
} // namespace __find_last_if
131+
132+
namespace __find_last_if_not {
133+
struct __fn {
134+
template <class _Pred>
135+
struct __op {
136+
_Pred& __pred;
137+
template <class _Elem>
138+
_LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator()(_Elem&& __elem) const {
139+
return !std::invoke(__pred, std::forward<_Elem>(__elem));
140+
}
141+
};
142+
143+
template <forward_iterator _Iter,
144+
sentinel_for<_Iter> _Sent,
145+
class _Proj = identity,
146+
indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
147+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static subrange<_Iter>
148+
operator()(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {}) {
149+
return ranges::__find_last_impl(std::move(__first), std::move(__last), __op<_Pred>{__pred}, __proj);
150+
}
151+
152+
template <forward_range _Range,
153+
class _Proj = identity,
154+
indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Pred>
155+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static borrowed_subrange_t<_Range>
156+
operator()(_Range&& __range, _Pred __pred, _Proj __proj = {}) {
157+
return ranges::__find_last_impl(ranges::begin(__range), ranges::end(__range), __op<_Pred>{__pred}, __proj);
158+
}
159+
};
160+
} // namespace __find_last_if_not
161+
162+
inline namespace __cpo {
163+
inline constexpr auto find_last = __find_last::__fn{};
164+
inline constexpr auto find_last_if = __find_last_if::__fn{};
165+
inline constexpr auto find_last_if_not = __find_last_if_not::__fn{};
166+
} // namespace __cpo
167+
} // namespace ranges
168+
169+
_LIBCPP_END_NAMESPACE_STD
170+
171+
#endif // _LIBCPP_STD_VER >= 23
172+
173+
_LIBCPP_POP_MACROS
174+
175+
#endif // _LIBCPP___ALGORITHM_RANGES_FIND_LAST_H

libcxx/include/algorithm

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,31 @@ namespace ranges {
102102
constexpr borrowed_iterator_t<R>
103103
find_if_not(R&& r, Pred pred, Proj proj = {}); // since C++20
104104
105+
template<forward_iterator I, sentinel_for<I> S, class T, class Proj = identity>
106+
requires indirect_binary_predicate<ranges::equal_to, projected<I, Proj>, const T*>
107+
constexpr subrange<I> find_last(I first, S last, const T& value, Proj proj = {}); // since C++23
108+
109+
template<forward_range R, class T, class Proj = identity>
110+
requires
111+
indirect_binary_predicate<ranges::equal_to, projected<iterator_t<R>, Proj>, const T*>
112+
constexpr borrowed_subrange_t<R> find_last(R&& r, const T& value, Proj proj = {}); // since C++23
113+
114+
template<forward_iterator I, sentinel_for<I> S, class Proj = identity,
115+
indirect_unary_predicate<projected<I, Proj>> Pred>
116+
constexpr subrange<I> find_last_if(I first, S last, Pred pred, Proj proj = {}); // since C++23
117+
118+
template<forward_range R, class Proj = identity,
119+
indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
120+
constexpr borrowed_subrange_t<R> find_last_if(R&& r, Pred pred, Proj proj = {}); // since C++23
121+
122+
template<forward_iterator I, sentinel_for<I> S, class Proj = identity,
123+
indirect_unary_predicate<projected<I, Proj>> Pred>
124+
constexpr subrange<I> find_last_if_not(I first, S last, Pred pred, Proj proj = {}); // since C++23
125+
126+
template<forward_range R, class Proj = identity,
127+
indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
128+
constexpr borrowed_subrange_t<R> find_last_if_not(R&& r, Pred pred, Proj proj = {}); // since C++23
129+
105130
template<class T, class Proj = identity,
106131
indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less>
107132
constexpr const T& min(const T& a, const T& b, Comp comp = {}, Proj proj = {}); // since C++20
@@ -1989,6 +2014,7 @@ template <class BidirectionalIterator, class Compare>
19892014
# include <__algorithm/fold.h>
19902015
# include <__algorithm/ranges_contains_subrange.h>
19912016
# include <__algorithm/ranges_ends_with.h>
2017+
# include <__algorithm/ranges_find_last.h>
19922018
# include <__algorithm/ranges_starts_with.h>
19932019
#endif // _LIBCPP_STD_VER >= 23
19942020

libcxx/include/module.modulemap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,7 @@ module std_private_algorithm_ranges_find_end [system
764764
module std_private_algorithm_ranges_find_first_of [system] { header "__algorithm/ranges_find_first_of.h" }
765765
module std_private_algorithm_ranges_find_if [system] { header "__algorithm/ranges_find_if.h" }
766766
module std_private_algorithm_ranges_find_if_not [system] { header "__algorithm/ranges_find_if_not.h" }
767+
module std_private_algorithm_ranges_find_last [system] { header "__algorithm/ranges_find_last.h" }
767768
module std_private_algorithm_ranges_for_each [system] {
768769
header "__algorithm/ranges_for_each.h"
769770
export std_private_algorithm_in_fun_result

libcxx/include/version

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ __cpp_lib_ranges_chunk 202202L <ranges>
190190
__cpp_lib_ranges_chunk_by 202202L <ranges>
191191
__cpp_lib_ranges_concat 202403L <ranges>
192192
__cpp_lib_ranges_contains 202207L <algorithm>
193+
__cpp_lib_ranges_find_last 202207L <algorithm>
193194
__cpp_lib_ranges_iota 202202L <numeric>
194195
__cpp_lib_ranges_join_with 202202L <ranges>
195196
__cpp_lib_ranges_repeat 202207L <ranges>
@@ -484,6 +485,7 @@ __cpp_lib_void_t 201411L <type_traits>
484485
// # define __cpp_lib_ranges_chunk 202202L
485486
# define __cpp_lib_ranges_chunk_by 202202L
486487
# define __cpp_lib_ranges_contains 202207L
488+
# define __cpp_lib_ranges_find_last 202207L
487489
// # define __cpp_lib_ranges_iota 202202L
488490
// # define __cpp_lib_ranges_join_with 202202L
489491
# define __cpp_lib_ranges_repeat 202207L

libcxx/modules/std/algorithm.inc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,14 @@ export namespace std {
7777
using std::ranges::find_if_not;
7878
} // namespace ranges
7979

80+
#if _LIBCPP_STD_VER >= 23
81+
// [alg.find.last], find last
8082
namespace ranges {
81-
#if 0
8283
using std::ranges::find_last;
8384
using std::ranges::find_last_if;
8485
using std::ranges::find_last_if_not;
85-
#endif
8686
} // namespace ranges
87+
#endif
8788

8889
// [alg.find.end], find end
8990
using std::find_end;

libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,16 @@ constexpr bool all_the_algorithms()
119119
(void)std::ranges::find_if(a, UnaryTrue(&copies)); assert(copies == 0);
120120
(void)std::ranges::find_if_not(first, last, UnaryTrue(&copies)); assert(copies == 0);
121121
(void)std::ranges::find_if_not(a, UnaryTrue(&copies)); assert(copies == 0);
122+
#if TEST_STD_VER >= 23
123+
(void)std::ranges::find_last_if(first, last, UnaryTrue(&copies));
124+
assert(copies == 0);
125+
(void)std::ranges::find_last_if(a, UnaryTrue(&copies));
126+
assert(copies == 0);
127+
(void)std::ranges::find_last_if_not(first, last, UnaryTrue(&copies));
128+
assert(copies == 0);
129+
(void)std::ranges::find_last_if_not(a, UnaryTrue(&copies));
130+
assert(copies == 0);
131+
#endif
122132
(void)std::ranges::for_each(first, last, UnaryVoid(&copies)); assert(copies == 1); copies = 0;
123133
(void)std::ranges::for_each(a, UnaryVoid(&copies)); assert(copies == 1); copies = 0;
124134
(void)std::ranges::for_each_n(first, count, UnaryVoid(&copies)); assert(copies == 1); copies = 0;

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,20 @@ constexpr bool all_the_algorithms()
115115
(void)std::ranges::find_if(a, UnaryTrue(), Proj(&copies)); assert(copies == 0);
116116
(void)std::ranges::find_if_not(first, last, UnaryTrue(), Proj(&copies)); assert(copies == 0);
117117
(void)std::ranges::find_if_not(a, UnaryTrue(), Proj(&copies)); assert(copies == 0);
118+
#if TEST_STD_VER >= 23
119+
(void)std::ranges::find_last(first, last, value, Proj(&copies));
120+
assert(copies == 0);
121+
(void)std::ranges::find_last(a, value, Proj(&copies));
122+
assert(copies == 0);
123+
(void)std::ranges::find_last_if(first, last, UnaryTrue(), Proj(&copies));
124+
assert(copies == 0);
125+
(void)std::ranges::find_last_if(a, UnaryTrue(), Proj(&copies));
126+
assert(copies == 0);
127+
(void)std::ranges::find_last_if_not(first, last, UnaryTrue(), Proj(&copies));
128+
assert(copies == 0);
129+
(void)std::ranges::find_last_if_not(a, UnaryTrue(), Proj(&copies));
130+
assert(copies == 0);
131+
#endif
118132
(void)std::ranges::for_each(first, last, UnaryVoid(), Proj(&copies)); assert(copies == 0);
119133
(void)std::ranges::for_each(a, UnaryVoid(), Proj(&copies)); assert(copies == 0);
120134
(void)std::ranges::for_each_n(first, count, UnaryVoid(), Proj(&copies)); assert(copies == 0);

libcxx/test/libcxx/diagnostics/algorithm.nodiscard.verify.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,18 @@ void test() {
372372
// expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}}
373373
std::ranges::contains_subrange(iter, iter, iter, iter);
374374
// expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}}
375+
std::ranges::find_last_if_not(iter, iter, pred);
376+
// expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}}
377+
std::ranges::find_last_if_not(range, pred);
378+
// expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}}
379+
std::ranges::find_last_if(iter, iter, pred);
380+
// expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}}
381+
std::ranges::find_last_if(range, pred);
382+
// expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}}
383+
std::ranges::find_last(iter, iter, 1);
384+
// expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}}
385+
std::ranges::find_last(range, 1);
386+
// expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}}
375387
std::ranges::fold_left(range, 0, std::plus());
376388
// expected-warning@-1{{ignoring return value of function declared with 'nodiscard' attribute}}
377389
std::ranges::fold_left(iter, iter, 0, std::plus());

0 commit comments

Comments
 (0)