Skip to content

Commit c162555

Browse files
committed
P1223R5: find_last
Implements [P1223R5][] completely. Includes an implementation of `find_last`, `find_last_if`, and `find_last_if_not`. [P1223R5]: https://wg21.link/p1223r5
1 parent 81704f6 commit c162555

24 files changed

+1249
-3
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/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/include/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ 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
101+
__algorithm/ranges_find_last_if.h
102+
__algorithm/ranges_find_last_if_not.h
100103
__algorithm/ranges_for_each.h
101104
__algorithm/ranges_for_each_n.h
102105
__algorithm/ranges_generate.h

libcxx/include/__algorithm/ranges_find_if.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#include <__functional/invoke.h>
1515
#include <__functional/ranges_operations.h>
1616
#include <__iterator/concepts.h>
17+
#include <__iterator/next.h>
18+
#include <__iterator/prev.h>
1719
#include <__iterator/projected.h>
1820
#include <__ranges/access.h>
1921
#include <__ranges/concepts.h>
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
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+
namespace __find_last {
39+
struct __fn {
40+
template <class _Iter, class _Sent, class _Type, class _Proj>
41+
_LIBCPP_HIDE_FROM_ABI constexpr static subrange<_Iter>
42+
__find_last_impl(_Iter __first, _Sent __last, const _Type& __value, _Proj& __proj) {
43+
if (__first == __last) {
44+
return subrange<_Iter>(__first, __first);
45+
}
46+
47+
if constexpr (bidirectional_iterator<_Iter>) {
48+
auto __last_it = ranges::next(__first, __last);
49+
for (auto __it = ranges::prev(__last_it); __it != __first; --__it) {
50+
if (std::invoke(__proj, *__it) == __value) {
51+
return subrange<_Iter>(std::move(__it), std::move(__last_it));
52+
}
53+
}
54+
if (std::invoke(__proj, *__first) == __value) {
55+
return subrange<_Iter>(std::move(__first), std::move(__last_it));
56+
}
57+
return subrange<_Iter>(__last_it, __last_it);
58+
} else {
59+
bool __found = false;
60+
_Iter __found_it;
61+
for (; __first != __last; ++__first) {
62+
if (std::invoke(__proj, *__first) == __value) {
63+
__found = true;
64+
__found_it = __first;
65+
}
66+
}
67+
68+
if (__found) {
69+
return subrange<_Iter>(std::move(__found_it), std::move(__first));
70+
} else {
71+
return subrange<_Iter>(__first, __first);
72+
}
73+
}
74+
}
75+
76+
template <forward_iterator _Iter, sentinel_for<_Iter> _Sent, class _Type, class _Proj = identity>
77+
requires indirect_binary_predicate<ranges::equal_to, projected<_Iter, _Proj>, const _Type*>
78+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr subrange<_Iter>
79+
operator()(_Iter __first, _Sent __last, const _Type& __value, _Proj __proj = {}) const {
80+
return __find_last_impl(std::move(__first), std::move(__last), __value, __proj);
81+
}
82+
83+
template <forward_range _Range, class _Type, class _Proj = identity>
84+
requires indirect_binary_predicate<ranges::equal_to, projected<iterator_t<_Range>, _Proj>, const _Type*>
85+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr borrowed_subrange_t<_Range>
86+
operator()(_Range&& __range, const _Type& __value, _Proj __proj = {}) const {
87+
return __find_last_impl(ranges::begin(__range), ranges::end(__range), __value, __proj);
88+
}
89+
};
90+
} // namespace __find_last
91+
92+
inline namespace __cpo {
93+
inline constexpr auto find_last = __find_last::__fn{};
94+
} // namespace __cpo
95+
} // namespace ranges
96+
97+
_LIBCPP_END_NAMESPACE_STD
98+
99+
#endif // _LIBCPP_STD_VER >= 23
100+
101+
_LIBCPP_POP_MACROS
102+
103+
#endif // _LIBCPP___ALGORITHM_RANGES_FIND_LAST_H
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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_IF_H
10+
#define _LIBCPP___ALGORITHM_RANGES_FIND_LAST_IF_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/next.h>
18+
#include <__iterator/prev.h>
19+
#include <__iterator/projected.h>
20+
#include <__ranges/access.h>
21+
#include <__ranges/concepts.h>
22+
#include <__ranges/subrange.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+
_LIBCPP_PUSH_MACROS
30+
#include <__undef_macros>
31+
32+
#if _LIBCPP_STD_VER >= 23
33+
34+
_LIBCPP_BEGIN_NAMESPACE_STD
35+
36+
namespace ranges {
37+
38+
template <class _Iter, class _Sent, class _Pred, class _Proj>
39+
_LIBCPP_HIDE_FROM_ABI constexpr static subrange<_Iter>
40+
__find_last_if_impl(_Iter __first, _Sent __last, _Pred& __pred, _Proj& __proj) {
41+
if (__first == __last) {
42+
return subrange<_Iter>(__first, __first);
43+
}
44+
45+
if constexpr (bidirectional_iterator<_Iter>) {
46+
auto __last_it = ranges::next(__first, __last);
47+
for (auto __it = ranges::prev(__last_it); __it != __first; --__it) {
48+
if (std::invoke(__pred, std::invoke(__proj, *__it))) {
49+
return subrange<_Iter>(std::move(__it), std::move(__last_it));
50+
}
51+
}
52+
if (std::invoke(__pred, std::invoke(__proj, *__first))) {
53+
return subrange<_Iter>(std::move(__first), std::move(__last_it));
54+
}
55+
return subrange<_Iter>(__last_it, __last_it);
56+
} else {
57+
bool __found = false;
58+
_Iter __found_it;
59+
for (; __first != __last; ++__first) {
60+
if (std::invoke(__pred, std::invoke(__proj, *__first))) {
61+
__found = true;
62+
__found_it = __first;
63+
}
64+
}
65+
66+
if (__found) {
67+
return subrange<_Iter>(std::move(__found_it), std::move(__first));
68+
} else {
69+
return subrange<_Iter>(__first, __first);
70+
}
71+
}
72+
}
73+
74+
namespace __find_last_if {
75+
struct __fn {
76+
template <forward_iterator _Iter,
77+
sentinel_for<_Iter> _Sent,
78+
class _Proj = identity,
79+
indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
80+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr subrange<_Iter>
81+
operator()(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {}) const {
82+
return __find_last_if_impl(std::move(__first), std::move(__last), __pred, __proj);
83+
}
84+
85+
template <forward_range _Range,
86+
class _Proj = identity,
87+
indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Pred>
88+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr borrowed_subrange_t<_Range>
89+
operator()(_Range&& __range, _Pred __pred, _Proj __proj = {}) const {
90+
return __find_last_if_impl(ranges::begin(__range), ranges::end(__range), __pred, __proj);
91+
}
92+
};
93+
} // namespace __find_last_if
94+
95+
inline namespace __cpo {
96+
inline constexpr auto find_last_if = __find_last_if::__fn{};
97+
} // namespace __cpo
98+
} // namespace ranges
99+
100+
_LIBCPP_END_NAMESPACE_STD
101+
102+
#endif // _LIBCPP_STD_VER >= 23
103+
104+
_LIBCPP_POP_MACROS
105+
106+
#endif // _LIBCPP___ALGORITHM_RANGES_FIND_LAST_IF_H
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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_IF_NOT_H
10+
#define _LIBCPP___ALGORITHM_RANGES_FIND_LAST_IF_NOT_H
11+
12+
#include <__algorithm/ranges_find_last_if.h>
13+
#include <__config>
14+
#include <__functional/identity.h>
15+
#include <__functional/invoke.h>
16+
#include <__functional/ranges_operations.h>
17+
#include <__iterator/concepts.h>
18+
#include <__iterator/projected.h>
19+
#include <__ranges/access.h>
20+
#include <__ranges/concepts.h>
21+
#include <__ranges/subrange.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+
_LIBCPP_PUSH_MACROS
29+
#include <__undef_macros>
30+
31+
#if _LIBCPP_STD_VER >= 23
32+
33+
_LIBCPP_BEGIN_NAMESPACE_STD
34+
35+
namespace ranges {
36+
namespace __find_last_if_not {
37+
struct __fn {
38+
template <forward_iterator _Iter,
39+
sentinel_for<_Iter> _Sent,
40+
class _Proj = identity,
41+
indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
42+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr subrange<_Iter>
43+
operator()(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {}) const {
44+
auto __pred2 = [&__pred](auto&& __e) -> bool { return !std::invoke(__pred, std::forward<decltype(__e)>(__e)); };
45+
return ranges::__find_last_if_impl(std::move(__first), std::move(__last), __pred2, __proj);
46+
}
47+
48+
template <forward_range _Range,
49+
class _Proj = identity,
50+
indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Pred>
51+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr borrowed_subrange_t<_Range>
52+
operator()(_Range&& __range, _Pred __pred, _Proj __proj = {}) const {
53+
auto __pred2 = [&__pred](auto&& __e) -> bool { return !std::invoke(__pred, std::forward<decltype(__e)>(__e)); };
54+
return ranges::__find_last_if_impl(ranges::begin(__range), ranges::end(__range), __pred2, __proj);
55+
}
56+
};
57+
} // namespace __find_last_if_not
58+
59+
inline namespace __cpo {
60+
inline constexpr auto find_last_if_not = __find_last_if_not::__fn{};
61+
} // namespace __cpo
62+
} // namespace ranges
63+
64+
_LIBCPP_END_NAMESPACE_STD
65+
66+
#endif // _LIBCPP_STD_VER >= 23
67+
68+
_LIBCPP_POP_MACROS
69+
70+
#endif // _LIBCPP___ALGORITHM_RANGES_FIND_LAST_IF_NOT_H

libcxx/include/algorithm

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,26 @@ namespace ranges {
101101
indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
102102
constexpr borrowed_iterator_t<R>
103103
find_if_not(R&& r, Pred pred, Proj proj = {}); // since C++20
104+
//
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 = {});
108+
template<forward_range R, class T, class Proj = identity>
109+
requires
110+
indirect_binary_predicate<ranges::equal_to, projected<iterator_t<R>, Proj>, const T*>
111+
constexpr borrowed_subrange_t<R> find_last(R&& r, const T& value, Proj proj = {});
112+
template<forward_iterator I, sentinel_for<I> S, class Proj = identity,
113+
indirect_unary_predicate<projected<I, Proj>> Pred>
114+
constexpr subrange<I> find_last_if(I first, S last, Pred pred, Proj proj = {});
115+
template<forward_range R, class Proj = identity,
116+
indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
117+
constexpr borrowed_subrange_t<R> find_last_if(R&& r, Pred pred, Proj proj = {});
118+
template<forward_iterator I, sentinel_for<I> S, class Proj = identity,
119+
indirect_unary_predicate<projected<I, Proj>> Pred>
120+
constexpr subrange<I> find_last_if_not(I first, S last, Pred pred, Proj proj = {});
121+
template<forward_range R, class Proj = identity,
122+
indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
123+
constexpr borrowed_subrange_t<R> find_last_if_not(R&& r, Pred pred, Proj proj = {});
104124
105125
template<class T, class Proj = identity,
106126
indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less>
@@ -1989,6 +2009,9 @@ template <class BidirectionalIterator, class Compare>
19892009
# include <__algorithm/fold.h>
19902010
# include <__algorithm/ranges_contains_subrange.h>
19912011
# include <__algorithm/ranges_ends_with.h>
2012+
# include <__algorithm/ranges_find_last.h>
2013+
# include <__algorithm/ranges_find_last_if.h>
2014+
# include <__algorithm/ranges_find_last_if_not.h>
19922015
# include <__algorithm/ranges_starts_with.h>
19932016
#endif // _LIBCPP_STD_VER >= 23
19942017

libcxx/include/module.modulemap

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,9 @@ 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" }
768+
module std_private_algorithm_ranges_find_last_if [system] { header "__algorithm/ranges_find_last_if.h" }
769+
module std_private_algorithm_ranges_find_last_if_not [system] { header "__algorithm/ranges_find_last_if_not.h" }
767770
module std_private_algorithm_ranges_for_each [system] {
768771
header "__algorithm/ranges_for_each.h"
769772
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>
@@ -483,6 +484,7 @@ __cpp_lib_void_t 201411L <type_traits>
483484
// # define __cpp_lib_ranges_chunk 202202L
484485
# define __cpp_lib_ranges_chunk_by 202202L
485486
# define __cpp_lib_ranges_contains 202207L
487+
# define __cpp_lib_ranges_find_last 202207L
486488
// # define __cpp_lib_ranges_iota 202202L
487489
// # define __cpp_lib_ranges_join_with 202202L
488490
# define __cpp_lib_ranges_repeat 202207L

libcxx/modules/std/algorithm.inc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,11 @@ export namespace std {
7777
using std::ranges::find_if_not;
7878
} // namespace ranges
7979

80+
// [alg.find.last], find last
8081
namespace ranges {
81-
#if 0
8282
using std::ranges::find_last;
8383
using std::ranges::find_last_if;
8484
using std::ranges::find_last_if_not;
85-
#endif
8685
} // namespace ranges
8786

8887
// [alg.find.end], find end

0 commit comments

Comments
 (0)