diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 8253371f45..25729d359b 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -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 _Se, class _Ty, class _Pj = identity> + requires indirect_binary_predicate, 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>(_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>(_First, _STD move(_UResult)); + } + } + + template + requires indirect_binary_predicate, _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>(_Range, _STD move(_UResult)); + } else { + auto _UResult = _Find_last_unchecked(_Ubegin(_Range), _Uend(_Range), _Value, _Pass_fn(_Proj)); + return _Rewrap_subrange>(_Range, _STD move(_UResult)); + } + } + + private: + template + _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, 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 + class _Find_last_if_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _Se, class _Pj = identity, + indirect_unary_predicate> _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>(_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>(_First, _STD move(_UResult)); + } + } + + template , _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>(_Range, _STD move(_UResult)); + } else { + auto _UResult = + _Find_last_if_unchecked(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Rewrap_subrange>(_Range, _STD move(_UResult)); + } + } + + private: + template + _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(_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(_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(_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 find_last_if{_Not_quite_object::_Construct_tag{}}; + _EXPORT_STD inline constexpr _Find_last_if_fn find_last_if_not{_Not_quite_object::_Construct_tag{}}; #endif // _HAS_CXX23 } // namespace ranges #endif // __cpp_lib_concepts diff --git a/stl/inc/functional b/stl/inc/functional index e68769a91f..23735d3505 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -31,19 +31,6 @@ _STL_DISABLE_CLANG_WARNINGS #undef new _STD_BEGIN -#if _HAS_CXX23 -_EXPORT_STD template , 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 _EXPORT_STD template diff --git a/stl/inc/xutility b/stl/inc/xutility index b8a210a37a..b9402ee837 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -385,6 +385,19 @@ constexpr _Ref_fn<_Fn> _Pass_fn(_Fn& _Val) { // pass functor by "reference" return {_Val}; } +#if _HAS_CXX23 +_EXPORT_STD template , 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 diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 24d25b184f..dd54727214 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -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 @@ -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 diff --git a/tests/std/test.lst b/tests/std/test.lst index 9af561348b..44a0e5a6d9 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -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 diff --git a/tests/std/tests/P1223R5_ranges_alg_find_last/env.lst b/tests/std/tests/P1223R5_ranges_alg_find_last/env.lst new file mode 100644 index 0000000000..18e2d7c71e --- /dev/null +++ b/tests/std/tests/P1223R5_ranges_alg_find_last/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_latest_matrix.lst diff --git a/tests/std/tests/P1223R5_ranges_alg_find_last/test.cpp b/tests/std/tests/P1223R5_ranges_alg_find_last/test.cpp new file mode 100644 index 0000000000..eed280b658 --- /dev/null +++ b/tests/std/tests/P1223R5_ranges_alg_find_last/test.cpp @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +#include +using namespace std; +using P = pair; + +// Validate dangling story +STATIC_ASSERT(same_as{}, 42)), ranges::dangling>); +STATIC_ASSERT(same_as{}, 42)), ranges::subrange>); + +template +constexpr void check_value(const T& found, const U& value) { + if constexpr (same_as) { + 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 + 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>>); + 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>>); + 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>); + STATIC_ASSERT(common_range); + 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>); + STATIC_ASSERT(common_range); + assert(result.begin() == wrapped_input.end()); + } + { // Validate empty range + Read wrapped_empty{span{}}; + 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(), true)); + test_fwd(); +} diff --git a/tests/std/tests/P1223R5_ranges_alg_find_last_if/env.lst b/tests/std/tests/P1223R5_ranges_alg_find_last_if/env.lst new file mode 100644 index 0000000000..18e2d7c71e --- /dev/null +++ b/tests/std/tests/P1223R5_ranges_alg_find_last_if/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_latest_matrix.lst diff --git a/tests/std/tests/P1223R5_ranges_alg_find_last_if/test.cpp b/tests/std/tests/P1223R5_ranges_alg_find_last_if/test.cpp new file mode 100644 index 0000000000..c47ded5425 --- /dev/null +++ b/tests/std/tests/P1223R5_ranges_alg_find_last_if/test.cpp @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +#include +using namespace std; +using P = pair; + +constexpr auto matches = [](const int val) { return val == 42; }; +constexpr auto equals = [](auto x) { return [x](auto&& y) { return y == x; }; }; + +// Validate dangling story +STATIC_ASSERT(same_as{}, matches)), ranges::dangling>); +STATIC_ASSERT(same_as{}, matches)), ranges::subrange>); + +template +constexpr void check_value(const T& found, const U& value) { + if constexpr (same_as) { + 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 + static constexpr void call() { + using ranges::find_last_if, 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_if(wrapped_input, equals(value), get_first); + STATIC_ASSERT(same_as>>); + check_value(result.front(), value); + } + { // Validate iterator + sentinel overload [found case] + Read wrapped_input{haystack}; + const auto result = find_last_if(wrapped_input.begin(), wrapped_input.end(), equals(value), get_first); + STATIC_ASSERT(same_as>>); + check_value(result.front(), value); + } + } + { // Validate range overload [not found case] + Read wrapped_input{haystack}; + auto result = find_last_if(wrapped_input, equals(42), get_first); + STATIC_ASSERT(same_as, iterator_t>); + assert(result.begin() == wrapped_input.end()); + } + { // Validate iterator + sentinel overload [not found case] + Read wrapped_input{haystack}; + auto result = find_last_if(wrapped_input.begin(), wrapped_input.end(), equals(42), get_first); + STATIC_ASSERT(same_as, iterator_t>); + assert(result.begin() == wrapped_input.end()); + } + { // Validate empty range + Read wrapped_empty{span{}}; + auto iter_result = find_last_if(wrapped_empty.begin(), wrapped_empty.end(), equals(0), get_first); + auto range_result = find_last_if(wrapped_empty, equals(0), get_first); + assert(iter_result.begin() == wrapped_empty.end()); + assert(range_result.begin() == wrapped_empty.end()); + } + } +}; + +int main() { + STATIC_ASSERT((test_fwd(), true)); + test_fwd(); +} diff --git a/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/env.lst b/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/env.lst new file mode 100644 index 0000000000..18e2d7c71e --- /dev/null +++ b/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_latest_matrix.lst diff --git a/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/test.cpp b/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/test.cpp new file mode 100644 index 0000000000..1a6a8465d9 --- /dev/null +++ b/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/test.cpp @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +#include +using namespace std; +using P = pair; + +constexpr auto not_matches = [](const int val) { return val != 42; }; +constexpr auto not_equals = [](auto x) { return [x](auto&& y) { return y != x; }; }; + +// Validate dangling story +STATIC_ASSERT(same_as{}, not_matches)), ranges::dangling>); +STATIC_ASSERT(same_as{}, not_matches)), ranges::subrange>); + +template +constexpr void check_value(const T& found, const U& value) { + if constexpr (same_as) { + 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 + static constexpr void call() { + using ranges::find_last_if_not, 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_if_not(wrapped_input, not_equals(value), get_first); + STATIC_ASSERT(same_as>>); + check_value(result.front(), value); + } + { // Validate iterator + sentinel overload [found case] + Read wrapped_input{haystack}; + const auto result = + find_last_if_not(wrapped_input.begin(), wrapped_input.end(), not_equals(value), get_first); + STATIC_ASSERT(same_as>>); + check_value(result.front(), value); + } + } + { // Validate range overload [not found case] + Read wrapped_input{haystack}; + auto result = find_last_if_not(wrapped_input, not_equals(42), get_first); + STATIC_ASSERT(same_as, iterator_t>); + assert(result.begin() == wrapped_input.end()); + } + { // Validate iterator + sentinel overload [not found case] + Read wrapped_input{haystack}; + auto result = find_last_if_not(wrapped_input.begin(), wrapped_input.end(), not_equals(42), get_first); + STATIC_ASSERT(same_as, iterator_t>); + assert(result.begin() == wrapped_input.end()); + } + { // Validate empty range + Read wrapped_empty{span{}}; + auto iter_result = find_last_if_not(wrapped_empty.begin(), wrapped_empty.end(), not_equals(0), get_first); + auto range_result = find_last_if_not(wrapped_empty, not_equals(0), get_first); + assert(iter_result.begin() == wrapped_empty.end()); + assert(range_result.begin() == wrapped_empty.end()); + } + } +}; + +int main() { + STATIC_ASSERT((test_fwd(), true)); + test_fwd(); +} diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 59b2ebcc03..4ba8d60af1 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -1574,6 +1574,20 @@ STATIC_ASSERT(__cpp_lib_ranges_contains == 202207L); #endif #endif +#if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 +#ifndef __cpp_lib_ranges_find_last // per LWG-3807 +#error __cpp_lib_ranges_find_last is not defined +#elif __cpp_lib_ranges_find_last != 202207L +#error __cpp_lib_ranges_find_last is not 202207L +#else +STATIC_ASSERT(__cpp_lib_ranges_find_last == 202207L); +#endif +#else +#ifdef __cpp_lib_ranges_find_last +#error __cpp_lib_ranges_find_last is defined +#endif +#endif + #if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 #ifndef __cpp_lib_ranges_fold #error __cpp_lib_ranges_fold is not defined