Skip to content

Commit

Permalink
[libc++][ranges] P1223R5: find_last (#99312)
Browse files Browse the repository at this point in the history
Implements [P1223R5][] completely.

Includes an implementation of `find_last`, `find_last_if`, and
`find_last_if_not`.

[P1223R5]: https://wg21.link/p1223r5
  • Loading branch information
strega-nil authored Jul 19, 2024
1 parent 09fec46 commit 04760bf
Show file tree
Hide file tree
Showing 23 changed files with 1,045 additions and 6 deletions.
2 changes: 2 additions & 0 deletions libcxx/docs/FeatureTestMacroTable.rst
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,8 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_ranges_contains`` ``202207L``
---------------------------------------------------------- -----------------
``__cpp_lib_ranges_find_last`` ``202207L``
---------------------------------------------------------- -----------------
``__cpp_lib_ranges_iota`` *unimplemented*
---------------------------------------------------------- -----------------
``__cpp_lib_ranges_join_with`` *unimplemented*
Expand Down
1 change: 1 addition & 0 deletions libcxx/docs/ReleaseNotes/19.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Implemented Papers
- P2231R1 - Missing ``constexpr`` in ``std::optional`` and ``std::variant``
- P0019R8 - ``std::atomic_ref``
- P2389R2 - Alias template ``dims`` for the ``extents`` of ``mdspan``
- P1223R5 - ``ranges::find_last()``, ``ranges::find_last_if()``, and ``ranges::find_last_if_not()``

Improvements and New Features
-----------------------------
Expand Down
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx23Papers.csv
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
"`P0429R9 <https://wg21.link/P0429R9>`__","LWG","A Standard ``flat_map``","July 2022","",""
"`P1169R4 <https://wg21.link/P1169R4>`__","LWG","``static operator()``","July 2022","|Complete|","16.0"
"`P1222R4 <https://wg21.link/P1222R4>`__","LWG","A Standard ``flat_set``","July 2022","",""
"`P1223R5 <https://wg21.link/P1223R5>`__","LWG","``ranges::find_last()``, ``ranges::find_last_if()``, and ``ranges::find_last_if_not()``","July 2022","","","|ranges|"
"`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|"
"`P1467R9 <https://wg21.link/P1467R9>`__","LWG","Extended ``floating-point`` types and standard names","July 2022","",""
"`P1642R11 <https://wg21.link/P1642R11>`__","LWG","Freestanding ``[utilities]``, ``[ranges]``, and ``[iterators]``","July 2022","",""
"`P1899R3 <https://wg21.link/P1899R3>`__","LWG","``stride_view``","July 2022","","","|ranges|"
Expand Down
6 changes: 3 additions & 3 deletions libcxx/docs/Status/RangesAlgorithms.csv
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
Standard,Algorithm,Assignee,CL,Status
C++20,all C++20 algorithms,N/A,N/A,✅
C++23,`find_last <https://wg21.link/P1223R5>`_,Unassigned,No patch yet,Not started
C++23,`find_last_if <https://wg21.link/P1223R5>`_,Unassigned,No patch yet,Not started
C++23,`find_last_if_not <https://wg21.link/P1223R5>`_,Unassigned,No patch yet,Not started
C++23,`find_last <https://wg21.link/P1223R5>`_,Nicole Mazzuca,`#99312 <https://github.com/llvm/llvm-project/pull/99312>`_,Complete
C++23,`find_last_if <https://wg21.link/P1223R5>`_,Nicole Mazzuca,`#99312 <https://github.com/llvm/llvm-project/pull/99312>`_,Complete
C++23,`find_last_if_not <https://wg21.link/P1223R5>`_,Nicole Mazzuca,`#99312 <https://github.com/llvm/llvm-project/pull/99312>`_,Complete
C++23,`starts_with <https://wg21.link/P1659R3>`_,Zijun Zhao,`D150735 <https://llvm.org/D150735>`_,Complete
C++23,`ends_with <https://wg21.link/P1659R3>`_,Zijun Zhao, `D150831 <https://llvm.org/D150831>`_,Complete
C++23,`shift_left <https://wg21.link/p2440r1>`_,Unassigned,No patch yet,Not started
Expand Down
1 change: 1 addition & 0 deletions libcxx/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ set(files
__algorithm/ranges_find_first_of.h
__algorithm/ranges_find_if.h
__algorithm/ranges_find_if_not.h
__algorithm/ranges_find_last.h
__algorithm/ranges_for_each.h
__algorithm/ranges_for_each_n.h
__algorithm/ranges_generate.h
Expand Down
175 changes: 175 additions & 0 deletions libcxx/include/__algorithm/ranges_find_last.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCPP___ALGORITHM_RANGES_FIND_LAST_H
#define _LIBCPP___ALGORITHM_RANGES_FIND_LAST_H

#include <__config>
#include <__functional/identity.h>
#include <__functional/invoke.h>
#include <__functional/ranges_operations.h>
#include <__iterator/concepts.h>
#include <__iterator/indirectly_comparable.h>
#include <__iterator/next.h>
#include <__iterator/prev.h>
#include <__iterator/projected.h>
#include <__ranges/access.h>
#include <__ranges/concepts.h>
#include <__ranges/subrange.h>
#include <__utility/move.h>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif

_LIBCPP_PUSH_MACROS
#include <__undef_macros>

#if _LIBCPP_STD_VER >= 23

_LIBCPP_BEGIN_NAMESPACE_STD

namespace ranges {

template <class _Iter, class _Sent, class _Pred, class _Proj>
_LIBCPP_HIDE_FROM_ABI constexpr subrange<_Iter>
__find_last_impl(_Iter __first, _Sent __last, _Pred __pred, _Proj& __proj) {
if (__first == __last) {
return subrange<_Iter>(__first, __first);
}

if constexpr (bidirectional_iterator<_Iter>) {
auto __last_it = ranges::next(__first, __last);
for (auto __it = ranges::prev(__last_it); __it != __first; --__it) {
if (__pred(std::invoke(__proj, *__it))) {
return subrange<_Iter>(std::move(__it), std::move(__last_it));
}
}
if (__pred(std::invoke(__proj, *__first))) {
return subrange<_Iter>(std::move(__first), std::move(__last_it));
}
return subrange<_Iter>(__last_it, __last_it);
} else {
bool __found = false;
_Iter __found_it;
for (; __first != __last; ++__first) {
if (__pred(std::invoke(__proj, *__first))) {
__found = true;
__found_it = __first;
}
}

if (__found) {
return subrange<_Iter>(std::move(__found_it), std::move(__first));
} else {
return subrange<_Iter>(__first, __first);
}
}
}

namespace __find_last {
struct __fn {
template <class _Type>
struct __op {
const _Type& __value;
template <class _Elem>
_LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator()(_Elem&& __elem) const {
return std::forward<_Elem>(__elem) == __value;
}
};

template <forward_iterator _Iter, sentinel_for<_Iter> _Sent, class _Type, class _Proj = identity>
requires indirect_binary_predicate<ranges::equal_to, projected<_Iter, _Proj>, const _Type*>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static subrange<_Iter>
operator()(_Iter __first, _Sent __last, const _Type& __value, _Proj __proj = {}) {
return ranges::__find_last_impl(std::move(__first), std::move(__last), __op<_Type>{__value}, __proj);
}

template <forward_range _Range, class _Type, class _Proj = identity>
requires indirect_binary_predicate<ranges::equal_to, projected<iterator_t<_Range>, _Proj>, const _Type*>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static borrowed_subrange_t<_Range>
operator()(_Range&& __range, const _Type& __value, _Proj __proj = {}) {
return ranges::__find_last_impl(ranges::begin(__range), ranges::end(__range), __op<_Type>{__value}, __proj);
}
};
} // namespace __find_last

namespace __find_last_if {
struct __fn {
template <class _Pred>
struct __op {
_Pred& __pred;
template <class _Elem>
_LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator()(_Elem&& __elem) const {
return std::invoke(__pred, std::forward<_Elem>(__elem));
}
};

template <forward_iterator _Iter,
sentinel_for<_Iter> _Sent,
class _Proj = identity,
indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static subrange<_Iter>
operator()(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {}) {
return ranges::__find_last_impl(std::move(__first), std::move(__last), __op<_Pred>{__pred}, __proj);
}

template <forward_range _Range,
class _Proj = identity,
indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Pred>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static borrowed_subrange_t<_Range>
operator()(_Range&& __range, _Pred __pred, _Proj __proj = {}) {
return ranges::__find_last_impl(ranges::begin(__range), ranges::end(__range), __op<_Pred>{__pred}, __proj);
}
};
} // namespace __find_last_if

namespace __find_last_if_not {
struct __fn {
template <class _Pred>
struct __op {
_Pred& __pred;
template <class _Elem>
_LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator()(_Elem&& __elem) const {
return !std::invoke(__pred, std::forward<_Elem>(__elem));
}
};

template <forward_iterator _Iter,
sentinel_for<_Iter> _Sent,
class _Proj = identity,
indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static subrange<_Iter>
operator()(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {}) {
return ranges::__find_last_impl(std::move(__first), std::move(__last), __op<_Pred>{__pred}, __proj);
}

template <forward_range _Range,
class _Proj = identity,
indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Pred>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static borrowed_subrange_t<_Range>
operator()(_Range&& __range, _Pred __pred, _Proj __proj = {}) {
return ranges::__find_last_impl(ranges::begin(__range), ranges::end(__range), __op<_Pred>{__pred}, __proj);
}
};
} // namespace __find_last_if_not

inline namespace __cpo {
inline constexpr auto find_last = __find_last::__fn{};
inline constexpr auto find_last_if = __find_last_if::__fn{};
inline constexpr auto find_last_if_not = __find_last_if_not::__fn{};
} // namespace __cpo
} // namespace ranges

_LIBCPP_END_NAMESPACE_STD

#endif // _LIBCPP_STD_VER >= 23

_LIBCPP_POP_MACROS

#endif // _LIBCPP___ALGORITHM_RANGES_FIND_LAST_H
26 changes: 26 additions & 0 deletions libcxx/include/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,31 @@ namespace ranges {
constexpr borrowed_iterator_t<R>
find_if_not(R&& r, Pred pred, Proj proj = {}); // since C++20
template<forward_iterator I, sentinel_for<I> S, class T, class Proj = identity>
requires indirect_binary_predicate<ranges::equal_to, projected<I, Proj>, const T*>
constexpr subrange<I> find_last(I first, S last, const T& value, Proj proj = {}); // since C++23
template<forward_range R, class T, class Proj = identity>
requires
indirect_binary_predicate<ranges::equal_to, projected<iterator_t<R>, Proj>, const T*>
constexpr borrowed_subrange_t<R> find_last(R&& r, const T& value, Proj proj = {}); // since C++23
template<forward_iterator I, sentinel_for<I> S, class Proj = identity,
indirect_unary_predicate<projected<I, Proj>> Pred>
constexpr subrange<I> find_last_if(I first, S last, Pred pred, Proj proj = {}); // since C++23
template<forward_range R, class Proj = identity,
indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
constexpr borrowed_subrange_t<R> find_last_if(R&& r, Pred pred, Proj proj = {}); // since C++23
template<forward_iterator I, sentinel_for<I> S, class Proj = identity,
indirect_unary_predicate<projected<I, Proj>> Pred>
constexpr subrange<I> find_last_if_not(I first, S last, Pred pred, Proj proj = {}); // since C++23
template<forward_range R, class Proj = identity,
indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
constexpr borrowed_subrange_t<R> find_last_if_not(R&& r, Pred pred, Proj proj = {}); // since C++23
template<class T, class Proj = identity,
indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less>
constexpr const T& min(const T& a, const T& b, Comp comp = {}, Proj proj = {}); // since C++20
Expand Down Expand Up @@ -1989,6 +2014,7 @@ template <class BidirectionalIterator, class Compare>
# include <__algorithm/fold.h>
# include <__algorithm/ranges_contains_subrange.h>
# include <__algorithm/ranges_ends_with.h>
# include <__algorithm/ranges_find_last.h>
# include <__algorithm/ranges_starts_with.h>
#endif // _LIBCPP_STD_VER >= 23

Expand Down
1 change: 1 addition & 0 deletions libcxx/include/module.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,7 @@ module std_private_algorithm_ranges_find_end [system
module std_private_algorithm_ranges_find_first_of [system] { header "__algorithm/ranges_find_first_of.h" }
module std_private_algorithm_ranges_find_if [system] { header "__algorithm/ranges_find_if.h" }
module std_private_algorithm_ranges_find_if_not [system] { header "__algorithm/ranges_find_if_not.h" }
module std_private_algorithm_ranges_find_last [system] { header "__algorithm/ranges_find_last.h" }
module std_private_algorithm_ranges_for_each [system] {
header "__algorithm/ranges_for_each.h"
export std_private_algorithm_in_fun_result
Expand Down
2 changes: 2 additions & 0 deletions libcxx/include/version
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ __cpp_lib_ranges_chunk 202202L <ranges>
__cpp_lib_ranges_chunk_by 202202L <ranges>
__cpp_lib_ranges_concat 202403L <ranges>
__cpp_lib_ranges_contains 202207L <algorithm>
__cpp_lib_ranges_find_last 202207L <algorithm>
__cpp_lib_ranges_iota 202202L <numeric>
__cpp_lib_ranges_join_with 202202L <ranges>
__cpp_lib_ranges_repeat 202207L <ranges>
Expand Down Expand Up @@ -484,6 +485,7 @@ __cpp_lib_void_t 201411L <type_traits>
// # define __cpp_lib_ranges_chunk 202202L
# define __cpp_lib_ranges_chunk_by 202202L
# define __cpp_lib_ranges_contains 202207L
# define __cpp_lib_ranges_find_last 202207L
// # define __cpp_lib_ranges_iota 202202L
// # define __cpp_lib_ranges_join_with 202202L
# define __cpp_lib_ranges_repeat 202207L
Expand Down
5 changes: 3 additions & 2 deletions libcxx/modules/std/algorithm.inc
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,14 @@ export namespace std {
using std::ranges::find_if_not;
} // namespace ranges

#if _LIBCPP_STD_VER >= 23
// [alg.find.last], find last
namespace ranges {
#if 0
using std::ranges::find_last;
using std::ranges::find_last_if;
using std::ranges::find_last_if_not;
#endif
} // namespace ranges
#endif

// [alg.find.end], find end
using std::find_end;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,16 @@ constexpr bool all_the_algorithms()
(void)std::ranges::find_if(a, UnaryTrue(&copies)); assert(copies == 0);
(void)std::ranges::find_if_not(first, last, UnaryTrue(&copies)); assert(copies == 0);
(void)std::ranges::find_if_not(a, UnaryTrue(&copies)); assert(copies == 0);
#if TEST_STD_VER >= 23
(void)std::ranges::find_last_if(first, last, UnaryTrue(&copies));
assert(copies == 0);
(void)std::ranges::find_last_if(a, UnaryTrue(&copies));
assert(copies == 0);
(void)std::ranges::find_last_if_not(first, last, UnaryTrue(&copies));
assert(copies == 0);
(void)std::ranges::find_last_if_not(a, UnaryTrue(&copies));
assert(copies == 0);
#endif
(void)std::ranges::for_each(first, last, UnaryVoid(&copies)); assert(copies == 1); copies = 0;
(void)std::ranges::for_each(a, UnaryVoid(&copies)); assert(copies == 1); copies = 0;
(void)std::ranges::for_each_n(first, count, UnaryVoid(&copies)); assert(copies == 1); copies = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,20 @@ constexpr bool all_the_algorithms()
(void)std::ranges::find_if(a, UnaryTrue(), Proj(&copies)); assert(copies == 0);
(void)std::ranges::find_if_not(first, last, UnaryTrue(), Proj(&copies)); assert(copies == 0);
(void)std::ranges::find_if_not(a, UnaryTrue(), Proj(&copies)); assert(copies == 0);
#if TEST_STD_VER >= 23
(void)std::ranges::find_last(first, last, value, Proj(&copies));
assert(copies == 0);
(void)std::ranges::find_last(a, value, Proj(&copies));
assert(copies == 0);
(void)std::ranges::find_last_if(first, last, UnaryTrue(), Proj(&copies));
assert(copies == 0);
(void)std::ranges::find_last_if(a, UnaryTrue(), Proj(&copies));
assert(copies == 0);
(void)std::ranges::find_last_if_not(first, last, UnaryTrue(), Proj(&copies));
assert(copies == 0);
(void)std::ranges::find_last_if_not(a, UnaryTrue(), Proj(&copies));
assert(copies == 0);
#endif
(void)std::ranges::for_each(first, last, UnaryVoid(), Proj(&copies)); assert(copies == 0);
(void)std::ranges::for_each(a, UnaryVoid(), Proj(&copies)); assert(copies == 0);
(void)std::ranges::for_each_n(first, count, UnaryVoid(), Proj(&copies)); assert(copies == 0);
Expand Down
12 changes: 12 additions & 0 deletions libcxx/test/libcxx/diagnostics/algorithm.nodiscard.verify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,18 @@ void test() {
// expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::ranges::contains_subrange(iter, iter, iter, iter);
// expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::ranges::find_last_if_not(iter, iter, pred);
// expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::ranges::find_last_if_not(range, pred);
// expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::ranges::find_last_if(iter, iter, pred);
// expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::ranges::find_last_if(range, pred);
// expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::ranges::find_last(iter, iter, 1);
// expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::ranges::find_last(range, 1);
// expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::ranges::fold_left(range, 0, std::plus());
// expected-warning@-1{{ignoring return value of function declared with 'nodiscard' attribute}}
std::ranges::fold_left(iter, iter, 0, std::plus());
Expand Down
Loading

0 comments on commit 04760bf

Please sign in to comment.