Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
165 changes: 165 additions & 0 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -2797,6 +2797,171 @@ namespace ranges {
};

_EXPORT_STD inline constexpr _Fold_right_last_fn fold_right_last{_Not_quite_object::_Construct_tag{}};

class _Find_last_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

template <forward_iterator _It, sentinel_for<_It> _Se, class _Ty, class _Pj = identity>
requires indirect_binary_predicate<ranges::equal_to, projected<_It, _Pj>, const _Ty*>
_NODISCARD constexpr subrange<_It> operator()(_It _First, _Se _Last, const _Ty& _Value, _Pj _Proj = {}) const {
_Adl_verify_range(_First, _Last);

auto _UFirst = _Unwrap_iter<_Se>(_STD move(_First));
if constexpr (bidirectional_iterator<_It>) {
auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last));
auto _UResult = _Find_last_unchecked(_STD move(_UFirst), _STD move(_ULast), _Value, _Pass_fn(_Proj));
return _Rewrap_subrange<subrange<_It>>(_First, _STD move(_UResult));
} else {
auto _ULast = _Unwrap_sent<_It>(_STD move(_Last));
auto _UResult = _Find_last_unchecked(_STD move(_UFirst), _STD move(_ULast), _Value, _Pass_fn(_Proj));
return _Rewrap_subrange<subrange<_It>>(_First, _STD move(_UResult));
}
}

template <forward_range _Rng, class _Ty, class _Pj = identity>
requires indirect_binary_predicate<ranges::equal_to, projected<iterator_t<_Rng>, _Pj>, const _Ty*>
_NODISCARD constexpr borrowed_subrange_t<_Rng> operator()(
_Rng&& _Range, const _Ty& _Value, _Pj _Proj = {}) const {
if constexpr (bidirectional_range<_Rng>) {
auto _UResult = _Find_last_unchecked(
_Ubegin(_Range), _Get_final_iterator_unwrapped(_Range), _Value, _Pass_fn(_Proj));
return _Rewrap_subrange<borrowed_subrange_t<_Rng>>(_Range, _STD move(_UResult));
} else {
auto _UResult = _Find_last_unchecked(_Ubegin(_Range), _Uend(_Range), _Value, _Pass_fn(_Proj));
return _Rewrap_subrange<borrowed_subrange_t<_Rng>>(_Range, _STD move(_UResult));
}
}

private:
template <class _It, class _Se, class _Ty, class _Pj>
_NODISCARD static constexpr subrange<_It> _Find_last_unchecked(
_It _First, _Se _Last, const _Ty& _Value, _Pj _Proj) {
_STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It>);
_STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>);
_STL_INTERNAL_STATIC_ASSERT(indirect_binary_predicate<ranges::equal_to, projected<_It, _Pj>, const _Ty*>);

if constexpr (_Bidi_common<_It, _Se>) {
for (auto _Result = _Last; _Result != _First;) {
if (_STD invoke(_Proj, *--_Result) == _Value) {
return {_STD move(_Result), _STD move(_Last)};
}
}
return {_Last, _Last};
} else if constexpr (same_as<_It, _Se>) {
auto _Result = _Last;
for (; _First != _Last; ++_First) {
if (_STD invoke(_Proj, *_First) == _Value) {
_Result = _First;
}
}
return {_STD move(_Result), _STD move(_Last)};
} else {
auto _Result = _First;
bool _Found = false;
for (;; ++_First) {
if (_First == _Last) {
if (!_Found) {
_Result = _First;
}
break;
}

if (_STD invoke(_Proj, *_First) == _Value) {
_Result = _First;
_Found = true;
}
}
return {_STD move(_Result), _STD move(_First)};
}
}
};

_EXPORT_STD inline constexpr _Find_last_fn find_last{_Not_quite_object::_Construct_tag{}};

template <bool _Search_for>
class _Find_last_if_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

template <forward_iterator _It, sentinel_for<_It> _Se, class _Pj = identity,
indirect_unary_predicate<projected<_It, _Pj>> _Pr>
_NODISCARD constexpr subrange<_It> operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const {
_Adl_verify_range(_First, _Last);

auto _UFirst = _Unwrap_iter<_Se>(_STD move(_First));
if constexpr (bidirectional_iterator<_It>) {
auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last));
auto _UResult =
_Find_last_if_unchecked(_STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj));
return _Rewrap_subrange<subrange<_It>>(_First, _STD move(_UResult));
} else {
auto _ULast = _Unwrap_sent<_It>(_STD move(_Last));
auto _UResult =
_Find_last_if_unchecked(_STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj));
return _Rewrap_subrange<subrange<_It>>(_First, _STD move(_UResult));
}
}

template <forward_range _Rng, class _Pj = identity,
indirect_unary_predicate<projected<iterator_t<_Rng>, _Pj>> _Pr>
_NODISCARD constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const {
if constexpr (bidirectional_range<_Rng>) {
auto _UResult = _Find_last_if_unchecked(
_Ubegin(_Range), _Get_final_iterator_unwrapped(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj));
return _Rewrap_subrange<borrowed_subrange_t<_Rng>>(_Range, _STD move(_UResult));
} else {
auto _UResult =
_Find_last_if_unchecked(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj));
return _Rewrap_subrange<borrowed_subrange_t<_Rng>>(_Range, _STD move(_UResult));
}
}

private:
template <class _It, class _Se, class _Pj, class _Pr>
_NODISCARD static constexpr subrange<_It> _Find_last_if_unchecked(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj) {
_STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It>);
_STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>);
_STL_INTERNAL_STATIC_ASSERT(indirect_unary_predicate<_Pr, projected<_It, _Pj>>);

if constexpr (_Bidi_common<_It, _Se>) {
for (auto _Result = _Last; _Result != _First;) {
if (_STD invoke_r<bool>(_Pred, _STD invoke(_Proj, *--_Result)) == _Search_for) {
return {_STD move(_Result), _STD move(_Last)};
}
}
return {_Last, _Last};
} else if constexpr (same_as<_It, _Se>) {
auto _Result = _Last;
for (; _First != _Last; ++_First) {
if (_STD invoke_r<bool>(_Pred, _STD invoke(_Proj, *_First)) == _Search_for) {
_Result = _First;
}
}
return {_STD move(_Result), _STD move(_Last)};
} else {
auto _Result = _First;
bool _Found = false;
for (;; ++_First) {
if (_First == _Last) {
if (!_Found) {
_Result = _First;
}
break;
}

if (_STD invoke_r<bool>(_Pred, _STD invoke(_Proj, *_First)) == _Search_for) {
_Result = _First;
_Found = true;
}
}
return {_STD move(_Result), _STD move(_First)};
}
}
};

_EXPORT_STD inline constexpr _Find_last_if_fn<true> find_last_if{_Not_quite_object::_Construct_tag{}};
_EXPORT_STD inline constexpr _Find_last_if_fn<false> find_last_if_not{_Not_quite_object::_Construct_tag{}};
#endif // _HAS_CXX23
} // namespace ranges
#endif // __cpp_lib_concepts
Expand Down
13 changes: 0 additions & 13 deletions stl/inc/functional
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,6 @@ _STL_DISABLE_CLANG_WARNINGS
#undef new

_STD_BEGIN
#if _HAS_CXX23
_EXPORT_STD template <class _Result_type, class _Callable, class... _Types,
enable_if_t<is_invocable_r_v<_Result_type, _Callable, _Types...>, int> = 0>
_NODISCARD constexpr _Result_type invoke_r(_Callable&& _Obj, _Types&&... _Args) noexcept(
is_nothrow_invocable_r_v<_Result_type, _Callable, _Types...>) {
if constexpr (is_void_v<_Result_type>) {
(void) _STD invoke(static_cast<_Callable&&>(_Obj), static_cast<_Types&&>(_Args)...);
} else {
return _STD invoke(static_cast<_Callable&&>(_Obj), static_cast<_Types&&>(_Args)...);
}
}
#endif // _HAS_CXX23

// plus, minus, and multiplies are defined in <xstddef>

_EXPORT_STD template <class _Ty = void>
Expand Down
13 changes: 13 additions & 0 deletions stl/inc/xutility
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,19 @@ constexpr _Ref_fn<_Fn> _Pass_fn(_Fn& _Val) { // pass functor by "reference"
return {_Val};
}

#if _HAS_CXX23
_EXPORT_STD template <class _Result_type, class _Callable, class... _Types,
enable_if_t<is_invocable_r_v<_Result_type, _Callable, _Types...>, int> = 0>
_NODISCARD constexpr _Result_type invoke_r(_Callable&& _Obj, _Types&&... _Args) noexcept(
is_nothrow_invocable_r_v<_Result_type, _Callable, _Types...>) {
if constexpr (is_void_v<_Result_type>) {
(void) _STD invoke(static_cast<_Callable&&>(_Obj), static_cast<_Types&&>(_Args)...);
} else {
return _STD invoke(static_cast<_Callable&&>(_Obj), static_cast<_Types&&>(_Args)...);
}
}
#endif // _HAS_CXX23

struct _Unused_parameter { // generic unused parameter struct
constexpr _Unused_parameter() noexcept = default;
template <class _Ty>
Expand Down
2 changes: 2 additions & 0 deletions stl/inc/yvals_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@
// P1132R7 out_ptr(), inout_ptr()
// P1147R1 Printing volatile Pointers
// P1206R7 Conversions From Ranges To Containers
// P1223R5 ranges::find_last, ranges::find_last_if, ranges::find_last_if_not
// P1272R4 byteswap()
// P1328R1 constexpr type_info::operator==()
// P1413R3 Deprecate aligned_storage And aligned_union
Expand Down Expand Up @@ -1677,6 +1678,7 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect
#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 // per LWG-3807
#define __cpp_lib_ranges_fold 202207L
#define __cpp_lib_ranges_iota 202202L
#define __cpp_lib_ranges_join_with 202202L
Expand Down
3 changes: 3 additions & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,9 @@ tests\P1206R7_vector_assign_range
tests\P1206R7_vector_from_range
tests\P1206R7_vector_insert_range
tests\P1208R6_source_location
tests\P1223R5_ranges_alg_find_last
tests\P1223R5_ranges_alg_find_last_if
tests\P1223R5_ranges_alg_find_last_if_not
tests\P1272R4_byteswap
tests\P1423R3_char8_t_remediation
tests\P1425R4_queue_stack_constructors
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P1223R5_ranges_alg_find_last/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\concepts_latest_matrix.lst
77 changes: 77 additions & 0 deletions tests/std/tests/P1223R5_ranges_alg_find_last/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <algorithm>
#include <cassert>
#include <ranges>
#include <span>
#include <utility>

#include <range_algorithm_support.hpp>
using namespace std;
using P = pair<int, int>;

// Validate dangling story
STATIC_ASSERT(same_as<decltype(ranges::find_last(borrowed<false>{}, 42)), ranges::dangling>);
STATIC_ASSERT(same_as<decltype(ranges::find_last(borrowed<true>{}, 42)), ranges::subrange<int*>>);

template <class T, class U>
constexpr void check_value(const T& found, const U& value) {
if constexpr (same_as<T, P>) {
assert(found.first == value);
assert(found.second == 1729);
} else {
assert(found.peek().first == value);
assert(found.peek().second == 1729);
}
}

struct instantiator {
static constexpr P haystack[6] = {{0, 42}, {2, 42}, {4, 42}, {0, 1729}, {2, 1729}, {4, 1729}};

template <ranges::forward_range Read>
static constexpr void call() {
using ranges::find_last, ranges::common_range, ranges::iterator_t;

for (const auto& [value, _] : haystack) {
{ // Validate range overload [found case]
Read wrapped_input{haystack};
const auto result = find_last(wrapped_input, value, get_first);
STATIC_ASSERT(same_as<decltype(result), const ranges::subrange<iterator_t<Read>>>);
check_value(result.front(), value);
}
{ // Validate iterator + sentinel overload [found case]
Read wrapped_input{haystack};
const auto result = find_last(wrapped_input.begin(), wrapped_input.end(), value, get_first);
STATIC_ASSERT(same_as<decltype(result), const ranges::subrange<iterator_t<Read>>>);
check_value(result.front(), value);
}
}
{ // Validate range overload [not found case]
Read wrapped_input{haystack};
auto result = find_last(wrapped_input, 42, get_first);
STATIC_ASSERT(same_as<iterator_t<decltype(result)>, iterator_t<Read>>);
STATIC_ASSERT(common_range<decltype(result)>);
assert(result.begin() == wrapped_input.end());
}
{ // Validate iterator + sentinel overload [not found case]
Read wrapped_input{haystack};
auto result = find_last(wrapped_input.begin(), wrapped_input.end(), 42, get_first);
STATIC_ASSERT(same_as<iterator_t<decltype(result)>, iterator_t<Read>>);
STATIC_ASSERT(common_range<decltype(result)>);
assert(result.begin() == wrapped_input.end());
}
{ // Validate empty range
Read wrapped_empty{span<P, 0>{}};
auto iter_result = find_last(wrapped_empty.begin(), wrapped_empty.end(), 0, get_first);
auto range_result = find_last(wrapped_empty, 0, get_first);
assert(iter_result.begin() == wrapped_empty.end());
assert(range_result.begin() == wrapped_empty.end());
}
}
};

int main() {
STATIC_ASSERT((test_fwd<instantiator, const P>(), true));
test_fwd<instantiator, const P>();
}
4 changes: 4 additions & 0 deletions tests/std/tests/P1223R5_ranges_alg_find_last_if/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\concepts_latest_matrix.lst
Loading