Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[libc++][ranges] P1223R5: find_last #99312

Merged
merged 10 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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``
strega-nil marked this conversation as resolved.
Show resolved Hide resolved
---------------------------------------------------------- -----------------
``__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 @@ -55,6 +55,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 - ``find_last``
strega-nil marked this conversation as resolved.
Show resolved Hide resolved

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 static subrange<_Iter>
strega-nil marked this conversation as resolved.
Show resolved Hide resolved
__find_last_impl(_Iter __first, _Sent __last, _Pred __pred, _Proj& __proj) {
if (__first == __last) {
return subrange<_Iter>(__first, __first);
}

if constexpr (bidirectional_iterator<_Iter>) {
strega-nil marked this conversation as resolved.
Show resolved Hide resolved
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>
constexpr decltype(auto) operator()(_Elem&& __elem) const {
strega-nil marked this conversation as resolved.
Show resolved Hide resolved
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 {
ldionne marked this conversation as resolved.
Show resolved Hide resolved
_Pred& __pred;
template <class _Elem>
constexpr decltype(auto) operator()(_Elem&& __elem) const {
strega-nil marked this conversation as resolved.
Show resolved Hide resolved
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>
constexpr decltype(auto) operator()(_Elem&& __elem) const {
strega-nil marked this conversation as resolved.
Show resolved Hide resolved
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
21 changes: 21 additions & 0 deletions libcxx/include/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,26 @@ 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
strega-nil marked this conversation as resolved.
Show resolved Hide resolved

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 +2009,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 @@ -483,6 +484,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
Loading