diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 945936ee546..c1ad93e1168 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -386,6 +386,21 @@ namespace ranges { inline constexpr _For_each_n_fn for_each_n{_Not_quite_object::_Construct_tag{}}; // VARIABLE ranges::find + // clang-format off + // concept-constrained for strict enforcement as it is used by several algorithms + template _Se, class _Ty, class _Pj> + requires indirect_binary_predicate, const _Ty*> + _NODISCARD constexpr _It _Find_unchecked(_It _First, const _Se _Last, const _Ty& _Val, _Pj _Proj) { + for (; _First != _Last; ++_First) { + if (_STD invoke(_Proj, *_First) == _Val) { + break; + } + } + + return _First; + } + // clang-format on + class _Find_fn : private _Not_quite_object { public: using _Not_quite_object::_Not_quite_object; @@ -395,16 +410,10 @@ namespace ranges { requires indirect_binary_predicate, const _Ty*> _NODISCARD constexpr _It operator()(_It _First, _Se _Last, const _Ty& _Val, _Pj _Proj = {}) const { _Adl_verify_range(_First, _Last); - // Performance note: investigate using _Find_unchecked when same_as<_Pj, identity> - auto _UFirst = _Get_unwrapped(_STD move(_First)); - const auto _ULast = _Get_unwrapped(_STD move(_Last)); - for (; _UFirst != _ULast; ++_UFirst) { - if (_STD invoke(_Proj, *_UFirst) == _Val) { - break; - } - } + auto _UResult = _RANGES _Find_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Val, _Pass_fn(_Proj)); - _Seek_wrapped(_First, _STD move(_UFirst)); + _Seek_wrapped(_First, _STD move(_UResult)); return _First; } @@ -412,7 +421,12 @@ namespace ranges { requires indirect_binary_predicate, _Pj>, const _Ty*> _NODISCARD constexpr borrowed_iterator_t<_Rng> operator()( _Rng&& _Range, const _Ty& _Val, _Pj _Proj = {}) const { - return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _Val, _Pass_fn(_Proj)); + auto _First = _RANGES begin(_Range); + auto _UResult = + _RANGES _Find_unchecked(_Get_unwrapped(_STD move(_First)), _Uend(_Range), _Val, _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult)); + return _First; } // clang-format on }; @@ -430,6 +444,18 @@ _NODISCARD _FwdIt find_if(_ExPo&& _Exec, _FwdIt _First, const _FwdIt _Last, _Pr #ifdef __cpp_lib_concepts namespace ranges { // VARIABLE ranges::find_if + // concept-constrained for strict enforcement as it is used by several algorithms + template _Se, class _Pj, indirect_unary_predicate> _Pr> + _NODISCARD constexpr _It _Find_if_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { + for (; _First != _Last; ++_First) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { + break; + } + } + + return _First; + } + class _Find_if_fn : private _Not_quite_object { public: using _Not_quite_object::_Not_quite_object; @@ -438,22 +464,22 @@ namespace ranges { indirect_unary_predicate> _Pr> _NODISCARD constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { _Adl_verify_range(_First, _Last); - auto _UFirst = _Get_unwrapped(_STD move(_First)); - const auto _ULast = _Get_unwrapped(_STD move(_Last)); - for (; _UFirst != _ULast; ++_UFirst) { - if (_STD invoke(_Pred, _STD invoke(_Proj, *_UFirst))) { - break; - } - } + auto _UResult = _RANGES _Find_if_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Pass_fn(_Pred), _Pass_fn(_Proj)); - _Seek_wrapped(_First, _STD move(_UFirst)); + _Seek_wrapped(_First, _STD move(_UResult)); return _First; } template , _Pj>> _Pr> _NODISCARD constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { - return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + auto _First = _RANGES begin(_Range); + auto _UResult = _RANGES _Find_if_unchecked( + _Get_unwrapped(_STD move(_First)), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult)); + return _First; } }; @@ -3871,13 +3897,247 @@ _FwdIt2 remove_copy_if(_ExPo&&, _FwdIt1 _First, _FwdIt1 _Last, _FwdIt2 _Dest, _P template = 0> _NODISCARD _FwdIt remove( _ExPo&& _Exec, const _FwdIt _First, const _FwdIt _Last, const _Ty& _Val) noexcept; // terminates -#endif // _HAS_CXX17 -#if _HAS_CXX17 template = 0> _NODISCARD _FwdIt remove_if(_ExPo&& _Exec, _FwdIt _First, const _FwdIt _Last, _Pr _Pred) noexcept; // terminates #endif // _HAS_CXX17 +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::remove + class _Remove_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, class _Ty, class _Pj = identity> + requires indirect_binary_predicate, const _Ty*> + _NODISCARD constexpr subrange<_It> operator()(_It _First, _Se _Last, const _Ty& _Val, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UResult = _Remove_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Val, _Pass_fn(_Proj)); + + return _Rewrap_subrange>(_First, _STD move(_UResult)); + } + + template + requires permutable> + && indirect_binary_predicate, _Pj>, const _Ty*> + _NODISCARD constexpr borrowed_subrange_t<_Rng> operator()( + _Rng&& _Range, const _Ty& _Val, _Pj _Proj = {}) const { + auto _UResult = _Remove_unchecked(_Ubegin(_Range), _Uend(_Range), _Val, _Pass_fn(_Proj)); + + return _Rewrap_subrange>(_Range, _STD move(_UResult)); + } + // clang-format on + private: + template + _NODISCARD static constexpr subrange<_It> _Remove_unchecked( + _It _First, const _Se _Last, const _Ty& _Val, _Pj _Proj) { + // Remove projected values equal to _Val from [_First, _Last) + _STL_INTERNAL_STATIC_ASSERT(permutable<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(indirect_binary_predicate, const _Ty*>); + + _First = _RANGES _Find_unchecked(_STD move(_First), _Last, _Val, _Proj); + auto _Next = _First; + if (_First == _Last) { + return {_STD move(_Next), _STD move(_First)}; + } + + while (++_First != _Last) { + if (_STD invoke(_Proj, *_First) != _Val) { + *_Next = _RANGES iter_move(_First); + ++_Next; + } + } + + return {_STD move(_Next), _STD move(_First)}; + } + }; + + inline constexpr _Remove_fn remove{_Not_quite_object::_Construct_tag{}}; + + // VARIABLE ranges::remove_if + class _Remove_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 _UResult = _Remove_if_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + return _Rewrap_subrange>(_First, _STD move(_UResult)); + } + + // clang-format off + template , _Pj>> _Pr> + requires permutable> + _NODISCARD constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { + auto _UResult = _Remove_if_unchecked(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + return _Rewrap_subrange>(_Range, _STD move(_UResult)); + } + // clang-format on + private: + template + _NODISCARD static constexpr subrange<_It> _Remove_if_unchecked( + _It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { + // Remove values whose projection satisfies _Pred from [_First, _Last) + _STL_INTERNAL_STATIC_ASSERT(permutable<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(indirect_unary_predicate<_Pr, projected<_It, _Pj>>); + + _First = _RANGES _Find_if_unchecked(_STD move(_First), _Last, _Pred, _Proj); + auto _Next = _First; + if (_First == _Last) { + return {_STD move(_Next), _STD move(_First)}; + } + + while (++_First != _Last) { + if (!_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { + *_Next = _RANGES iter_move(_First); + ++_Next; + } + } + + return {_STD move(_Next), _STD move(_First)}; + } + }; + + inline constexpr _Remove_if_fn remove_if{_Not_quite_object::_Construct_tag{}}; + + // ALIAS TEMPLATE remove_copy_result + template + using remove_copy_result = in_out_result<_In, _Out>; + + // VARIABLE ranges::remove_copy + class _Remove_copy_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, weakly_incrementable _Out, class _Ty, class _Pj = identity> + requires indirectly_copyable<_It, _Out> + && indirect_binary_predicate, const _Ty*> + constexpr remove_copy_result<_It, _Out> operator()( + _It _First, _Se _Last, _Out _Result, const _Ty& _Val, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UResult = _Remove_copy_unchecked(_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), + _Get_unwrapped_unverified(_STD move(_Result)), _Val, _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult.in)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First), _STD move(_Result)}; + } + + template + requires indirectly_copyable, _Out> + && indirect_binary_predicate, _Pj>, const _Ty*> + constexpr remove_copy_result, _Out> operator()( + _Rng&& _Range, _Out _Result, const _Ty& _Val, _Pj _Proj = {}) const { + auto _First = _RANGES begin(_Range); + auto _UResult = _Remove_copy_unchecked(_Get_unwrapped(_STD move(_First)), _Uend(_Range), + _Get_unwrapped_unverified(_STD move(_Result)), _Val, _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult.in)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First), _STD move(_Result)}; + } + // clang-format on + private: + template + _NODISCARD static constexpr remove_copy_result<_It, _Out> _Remove_copy_unchecked( + _It _First, const _Se _Last, _Out _Result, const _Ty& _Val, _Pj _Proj) { + // Copy [_First, _Last) to _Result except projected values equal to _Val + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_copyable<_It, _Out>); + _STL_INTERNAL_STATIC_ASSERT(indirect_binary_predicate, const _Ty*>); + + for (; _First != _Last; ++_First) { + if (_STD invoke(_Proj, *_First) != _Val) { + *_Result = *_First; + ++_Result; + } + } + + return {_STD move(_First), _STD move(_Result)}; + } + }; + + inline constexpr _Remove_copy_fn remove_copy{_Not_quite_object::_Construct_tag{}}; + + // ALIAS TEMPLATE remove_copy_if_result + template + using remove_copy_if_result = in_out_result<_In, _Out>; + + // VARIABLE ranges::remove_copy_if + class _Remove_copy_if_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, weakly_incrementable _Out, class _Pj = identity, + indirect_unary_predicate> _Pr> + requires indirectly_copyable<_It, _Out> + constexpr remove_copy_if_result<_It, _Out> operator()( + _It _First, _Se _Last, _Out _Result, _Pr _Pred, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UResult = + _Remove_copy_if_unchecked(_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), + _Get_unwrapped_unverified(_STD move(_Result)), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult.in)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First), _STD move(_Result)}; + } + + template , _Pj>> _Pr> + requires indirectly_copyable, _Out> + constexpr remove_copy_if_result, _Out> operator()( + _Rng&& _Range, _Out _Result, _Pr _Pred, _Pj _Proj = {}) const { + auto _First = _RANGES begin(_Range); + auto _UResult = _Remove_copy_if_unchecked(_Get_unwrapped(_STD move(_First)), _Uend(_Range), + _Get_unwrapped_unverified(_STD move(_Result)), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult.in)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First), _STD move(_Result)}; + } + // clang-format on + private: + template + _NODISCARD static constexpr remove_copy_if_result<_It, _Out> _Remove_copy_if_unchecked( + _It _First, const _Se _Last, _Out _Result, _Pr _Pred, _Pj _Proj) { + // Copy [_First, _Last) to _Result except projected values that satisfy _Pred + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_copyable<_It, _Out>); + _STL_INTERNAL_STATIC_ASSERT(indirect_unary_predicate<_Pr, projected<_It, _Pj>>); + + for (; _First != _Last; ++_First) { + if (!_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { + *_Result = *_First; + ++_Result; + } + } + + return {_STD move(_First), _STD move(_Result)}; + } + }; + + inline constexpr _Remove_copy_if_fn remove_copy_if{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE unique template _NODISCARD _CONSTEXPR20 _FwdIt unique(_FwdIt _First, _FwdIt _Last, _Pr _Pred) { diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index d5fa87ddd08..9727362aace 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -1008,6 +1008,11 @@ constexpr void test_in_write() { with_input_ranges, Element1>::call(); } +template +constexpr void test_fwd_write() { + with_forward_ranges, Element1>::call(); +} + template constexpr void test_read() { with_input_iterators::call(); diff --git a/tests/std/test.lst b/tests/std/test.lst index 4c9d6bf510c..ac1d9478867 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -266,6 +266,10 @@ tests\P0896R4_ranges_alg_none_of tests\P0896R4_ranges_alg_partition tests\P0896R4_ranges_alg_partition_copy tests\P0896R4_ranges_alg_partition_point +tests\P0896R4_ranges_alg_remove +tests\P0896R4_ranges_alg_remove_copy +tests\P0896R4_ranges_alg_remove_copy_if +tests\P0896R4_ranges_alg_remove_if tests\P0896R4_ranges_alg_replace tests\P0896R4_ranges_alg_replace_copy tests\P0896R4_ranges_alg_replace_copy_if diff --git a/tests/std/tests/P0896R4_ranges_alg_remove/env.lst b/tests/std/tests/P0896R4_ranges_alg_remove/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_remove/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp new file mode 100644 index 00000000000..3ddb3c33703 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_remove/test.cpp @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#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>); + +struct instantiator { + static constexpr P expected[3] = {{0, 99}, {2, 99}, {4, 99}}; + + template + static constexpr void call() { +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163 +#pragma warning(suppress : 4127) // conditional expression is constant + if (!ranges::contiguous_range || !is_constant_evaluated()) +#endif // TRANSITION, VSO-938163 + { + using ranges::remove, ranges::subrange, ranges::equal, ranges::iterator_t; + + size_t projectionCounter = 0; + auto projection = [&projectionCounter](const P& val) { + ++projectionCounter; + return val.second; + }; + + { // Validate iterator + sentinel overload + P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; + Read wrapped_input{input}; + + auto result = remove(wrapped_input.begin(), wrapped_input.end(), 47, projection); + STATIC_ASSERT(same_as>>); + assert(result.begin() == next(wrapped_input.begin(), 3)); + assert(result.end() == wrapped_input.end()); + assert(equal(expected, span{input}.first<3>())); + assert(projectionCounter == ranges::size(input)); + } + + projectionCounter = 0; + + { // Validate range overload + P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; + Read wrapped_input{input}; + + auto result = remove(wrapped_input, 47, projection); + STATIC_ASSERT(same_as>>); + assert(result.begin() == next(wrapped_input.begin(), 3)); + assert(result.end() == wrapped_input.end()); + assert(equal(expected, span{input}.first<3>())); + assert(projectionCounter == ranges::size(input)); + } + } + } +}; + +int main() { + STATIC_ASSERT((test_fwd(), true)); + test_fwd(); +} diff --git a/tests/std/tests/P0896R4_ranges_alg_remove_copy/env.lst b/tests/std/tests/P0896R4_ranges_alg_remove_copy/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_remove_copy/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_remove_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove_copy/test.cpp new file mode 100644 index 00000000000..e5228540ed6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_remove_copy/test.cpp @@ -0,0 +1,76 @@ +// 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 that remove_copy_result aliases in_out_result +STATIC_ASSERT(same_as, ranges::in_out_result>); + +// Validate dangling story +STATIC_ASSERT(same_as{}, static_cast(nullptr), 42)), + ranges::remove_copy_result>); +STATIC_ASSERT(same_as{}, static_cast(nullptr), 42)), + ranges::remove_copy_result>); + +struct instantiator { + static constexpr P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; + static constexpr P expected[3] = {{0, 99}, {2, 99}, {4, 99}}; + + template > Write> + static constexpr void call() { + // Fails checking the indirect_binary_predicate requirement in C1XX's permissive mode with proxy iterators + // (probably related to VSO-566808) + constexpr bool non_proxy = + is_reference_v> && is_reference_v>; + if constexpr (non_proxy || !is_permissive) { + using ranges::remove_copy, ranges::remove_copy_result, ranges::equal, ranges::iterator_t; + + size_t projectionCounter = 0; + auto projection = [&projectionCounter](const P& val) { + ++projectionCounter; + return val.second; + }; + + { // Validate iterator + sentinel overload + P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; + Read wrapped_input{input}; + + auto result = remove_copy(wrapped_input.begin(), wrapped_input.end(), Write{output}, 47, projection); + STATIC_ASSERT(same_as, Write>>); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == output + 3); + assert(equal(output, expected)); + assert(projectionCounter == ranges::size(input)); + } + + projectionCounter = 0; + + { // Validate range overload + P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; + Read wrapped_input{input}; + + auto result = remove_copy(wrapped_input, Write{output}, 47, projection); + STATIC_ASSERT(same_as, Write>>); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == output + 3); + assert(equal(output, expected)); + assert(projectionCounter == ranges::size(input)); + } + } + } +}; + +int main() { +#ifndef _PREFAST_ // TRANSITION, GH-1030 + STATIC_ASSERT((test_in_write(), true)); +#endif // TRANSITION, GH-1030 + test_in_write(); +} diff --git a/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/env.lst b/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/test.cpp new file mode 100644 index 00000000000..23453dccd76 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_remove_copy_if/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; + +constexpr auto matches = [](const int val) { return val == 47; }; + +// Validate that remove_copy_if_result aliases in_out_result +STATIC_ASSERT(same_as, ranges::in_out_result>); + +// Validate dangling story +STATIC_ASSERT(same_as{}, static_cast(nullptr), matches)), + ranges::remove_copy_if_result>); +STATIC_ASSERT(same_as{}, static_cast(nullptr), matches)), + ranges::remove_copy_if_result>); + +struct instantiator { + static constexpr P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; + static constexpr P expected[3] = {{0, 99}, {2, 99}, {4, 99}}; + + template > Write> + static constexpr void call() { + // Fails checking the indirect_binary_predicate requirement in C1XX's permissive mode with proxy iterators + // (probably related to VSO-566808) + constexpr bool non_proxy = + is_reference_v> && is_reference_v>; + if constexpr (non_proxy || !is_permissive) { + using ranges::remove_copy_if, ranges::remove_copy_if_result, ranges::equal, ranges::iterator_t; + + size_t projectionCounter = 0; + auto projection = [&projectionCounter](const P& val) { + ++projectionCounter; + return val.second; + }; + + { // Validate iterator + sentinel overload + P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; + Read wrapped_input{input}; + auto result = + remove_copy_if(wrapped_input.begin(), wrapped_input.end(), Write{output}, matches, projection); + STATIC_ASSERT(same_as, Write>>); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == output + 3); + assert(equal(output, expected)); + assert(projectionCounter == ranges::size(input)); + } + + projectionCounter = 0; + + { // Validate range overload + P output[3] = {{-1, -1}, {-1, -1}, {-1, -1}}; + Read wrapped_input{input}; + auto result = remove_copy_if(wrapped_input, Write{output}, matches, projection); + STATIC_ASSERT(same_as, Write>>); + assert(result.in == wrapped_input.end()); + assert(result.out.peek() == output + 3); + assert(equal(output, expected)); + assert(projectionCounter == ranges::size(input)); + } + } + } +}; + +int main() { +#ifndef _PREFAST_ // TRANSITION, GH-1030 + STATIC_ASSERT((test_in_write(), true)); +#endif // TRANSITION, GH-1030 + test_in_write(); +} diff --git a/tests/std/tests/P0896R4_ranges_alg_remove_if/env.lst b/tests/std/tests/P0896R4_ranges_alg_remove_if/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_remove_if/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp b/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp new file mode 100644 index 00000000000..863a0e28917 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_remove_if/test.cpp @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +#include +using namespace std; +using P = pair; + +constexpr auto matches = [](const int val) { return val == 47; }; + +// Validate dangling story +STATIC_ASSERT(same_as{}, matches)), ranges::dangling>); +STATIC_ASSERT(same_as{}, matches)), ranges::subrange>); + +struct instantiator { + static constexpr P expected[3] = {{0, 99}, {2, 99}, {4, 99}}; + + template + static constexpr void call() { +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163 +#pragma warning(suppress : 4127) // conditional expression is constant + if (!ranges::contiguous_range || !is_constant_evaluated()) +#endif // TRANSITION, VSO-938163 + { + using ranges::remove_if, ranges::subrange, ranges::equal, ranges::iterator_t; + + size_t projectionCounter = 0; + auto projection = [&projectionCounter](const P& val) { + ++projectionCounter; + return val.second; + }; + + { // Validate iterator + sentinel overload + P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; + Read wrapped_input{input}; + + auto result = remove_if(wrapped_input.begin(), wrapped_input.end(), matches, projection); + STATIC_ASSERT(same_as>>); + assert(result.begin() == next(wrapped_input.begin(), 3)); + assert(result.end() == wrapped_input.end()); + assert(equal(expected, span{input}.first<3>())); + assert(projectionCounter == ranges::size(input)); + } + + projectionCounter = 0; + + { // Validate range overload + P input[5] = {{0, 99}, {1, 47}, {2, 99}, {3, 47}, {4, 99}}; + Read wrapped_input{input}; + + auto result = remove_if(wrapped_input, matches, projection); + STATIC_ASSERT(same_as>>); + assert(result.begin() == next(wrapped_input.begin(), 3)); + assert(result.end() == wrapped_input.end()); + assert(equal(expected, span{input}.first<3>())); + assert(projectionCounter == ranges::size(input)); + } + } + } +}; + +int main() { + STATIC_ASSERT((test_fwd(), true)); + test_fwd(); +}