From 715415620932316270d87b65624e03df4e9d1420 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Fri, 8 May 2020 17:01:39 -0700 Subject: [PATCH 1/2] Implement ranges::find_first_of --- stl/inc/algorithm | 47 ++++++++++ tests/std/include/range_algorithm_support.hpp | 27 ++++++ tests/std/test.lst | 1 + .../tests/P0896R4_ranges_alg_find/test.cpp | 2 +- .../P0896R4_ranges_alg_find_first_of/env.lst | 4 + .../P0896R4_ranges_alg_find_first_of/test.cpp | 93 +++++++++++++++++++ 6 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 tests/std/tests/P0896R4_ranges_alg_find_first_of/env.lst create mode 100644 tests/std/tests/P0896R4_ranges_alg_find_first_of/test.cpp diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 70f0425701..93f020c776 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -1907,6 +1907,53 @@ _NODISCARD _FwdIt1 find_first_of(_ExPo&& _Exec, const _FwdIt1 _First1, const _Fw } #endif // _HAS_CXX17 +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::find_first_of + class _Find_first_of_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se1, forward_iterator _It2, sentinel_for<_It2> _Se2, + class _Pr = ranges::equal_to, class _Pj1 = identity, class _Pj2 = identity> + requires indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2> + _NODISCARD constexpr _It1 operator()(_It1 _First1, const _Se1 _Last1, const _It2 _First2, const _Se2 _Last2, + _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + _Adl_verify_range(_First1, _Last1); + _Adl_verify_range(_First2, _Last2); + auto _UFirst1 = _Get_unwrapped(_STD move(_First1)); + const auto _ULast1 = _Get_unwrapped(_Last1); + const auto _UFirst2 = _Get_unwrapped(_First2); + const auto _ULast2 = _Get_unwrapped(_Last2); + for (; _UFirst1 != _ULast1; ++_UFirst1) { + for (auto _UMid2 = _UFirst2; _UMid2 != _ULast2; ++_UMid2) { + if (_STD invoke(_Pred, _STD invoke(_Proj1, *_UFirst1), _STD invoke(_Proj2, *_UMid2))) { + _Seek_wrapped(_First1, _STD move(_UFirst1)); + return _First1; + } + } + } + + _Seek_wrapped(_First1, _STD move(_UFirst1)); + return _First1; + } + + template + requires indirectly_comparable, iterator_t<_Rng2>, _Pr, _Pj1, _Pj2> + _NODISCARD constexpr borrowed_iterator_t<_Rng1> operator()( + _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + return (*this)(_RANGES begin(_Range1), _RANGES end(_Range1), _RANGES begin(_Range2), _RANGES end(_Range2), + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } + // clang-format on + }; + + inline constexpr _Find_first_of_fn find_first_of{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE swap_ranges template _CONSTEXPR20 _FwdIt2 swap_ranges(const _FwdIt1 _First1, const _FwdIt1 _Last1, _FwdIt2 _First2) { diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index 2120c6b454..f60c5194d1 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -487,6 +487,28 @@ struct with_input_ranges { } }; +template +struct with_forward_ranges { + template + static void call() { + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + + Continuation::template call>(); + Continuation::template call>(); + + Continuation::template call>(); + Continuation::template call>(); + } +}; + template struct with_input_iterators { template @@ -524,6 +546,11 @@ void test_in_in() { with_input_ranges>::call(); } +template +void test_in_fwd() { + with_input_ranges>::call(); +} + template void test_in_out() { with_input_ranges>::call(); diff --git a/tests/std/test.lst b/tests/std/test.lst index 9cdaa15d15..3de989fcbe 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -239,6 +239,7 @@ tests\P0896R4_ranges_alg_count tests\P0896R4_ranges_alg_count_if tests\P0896R4_ranges_alg_equal tests\P0896R4_ranges_alg_find +tests\P0896R4_ranges_alg_find_first_of tests\P0896R4_ranges_alg_find_if tests\P0896R4_ranges_alg_find_if_not tests\P0896R4_ranges_alg_for_each diff --git a/tests/std/tests/P0896R4_ranges_alg_find/test.cpp b/tests/std/tests/P0896R4_ranges_alg_find/test.cpp index d286f85d91..825c7d7132 100644 --- a/tests/std/tests/P0896R4_ranges_alg_find/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_find/test.cpp @@ -66,7 +66,7 @@ struct instantiator { }; using Projection = type (*)(std::iter_common_reference_t>); (void) ranges::find(in, type{}, Projection{}); - }; + } }; template void test_in(); diff --git a/tests/std/tests/P0896R4_ranges_alg_find_first_of/env.lst b/tests/std/tests/P0896R4_ranges_alg_find_first_of/env.lst new file mode 100644 index 0000000000..f3ccc8613c --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_find_first_of/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_find_first_of/test.cpp b/tests/std/tests/P0896R4_ranges_alg_find_first_of/test.cpp new file mode 100644 index 0000000000..fe8bfd3700 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_find_first_of/test.cpp @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +// +#include + +constexpr void smoke_test() { + using ranges::find_first_of, ranges::iterator_t, std::array, std::same_as, std::to_address; + using P = std::pair; + + // Validate dangling story + STATIC_ASSERT(same_as{}, array{})), ranges::dangling>); + STATIC_ASSERT(same_as{}, array{})), int*>); + + const array pairs = {P{0, 42}, P{1, 42}, P{2, 42}, P{3, 42}, P{4, 42}, P{5, 42}, P{6, 42}}; + + const auto pred = [](const int x, const int y) { return x == y + 1; }; + + const array good_needle = {29, 1}; + { + // Validate range overload [found case] + const auto result = find_first_of(move_only_range{pairs}, good_needle, pred, get_first); + STATIC_ASSERT(same_as>>); + assert(&*result.base() == &pairs.begin()[2]); + } + { + // Validate iterator + sentinel overload [found case] + move_only_range wrapped_pairs{pairs}; + const auto result = find_first_of( + wrapped_pairs.begin(), wrapped_pairs.end(), good_needle.begin(), good_needle.end(), pred, get_first); + STATIC_ASSERT(same_as>>); + assert(&*result.base() == &pairs.begin()[2]); + } + + const array bad_needle = {29, 17}; + { + // Validate range overload [not found case] + const auto result = find_first_of(move_only_range{pairs}, bad_needle, pred, get_first); + STATIC_ASSERT(same_as>>); + assert(to_address(result.base()) == to_address(pairs.end())); + } + { + // Validate iterator + sentinel overload [not found case] + move_only_range wrapped_pairs{pairs}; + const auto result = find_first_of( + wrapped_pairs.begin(), wrapped_pairs.end(), bad_needle.begin(), bad_needle.end(), pred, get_first); + STATIC_ASSERT(same_as>>); + assert(to_address(result.base()) == to_address(pairs.end())); + } +} + +int main() { + STATIC_ASSERT((smoke_test(), true)); + smoke_test(); +} + +struct instantiator { + template + static void call(In1&& in1 = {}, Fwd2&& fwd2 = {}) { + if constexpr (!is_permissive) { // These fail to compile in C1XX's permissive mode due to VSO-566808 + using ranges::iterator_t; + + (void) ranges::find_first_of(in1, fwd2); + (void) ranges::find_first_of(ranges::begin(in1), ranges::end(in1), ranges::begin(fwd2), ranges::end(fwd2)); + + BinaryPredicateFor, iterator_t> pred{}; + (void) ranges::find_first_of(in1, fwd2, pred); + (void) ranges::find_first_of( + ranges::begin(in1), ranges::end(in1), ranges::begin(fwd2), ranges::end(fwd2), pred); + + HalfProjectedBinaryPredicateFor> halfpred{}; + ProjectionFor> halfproj{}; + (void) ranges::find_first_of(in1, fwd2, halfpred, halfproj); + (void) ranges::find_first_of( + ranges::begin(in1), ranges::end(in1), ranges::begin(fwd2), ranges::end(fwd2), halfpred, halfproj); + + ProjectedBinaryPredicate<0, 1> projpred{}; + ProjectionFor, 0> proj1{}; + ProjectionFor, 1> proj2{}; + (void) ranges::find_first_of(in1, fwd2, projpred, proj1, proj2); + (void) ranges::find_first_of( + ranges::begin(in1), ranges::end(in1), ranges::begin(fwd2), ranges::end(fwd2), projpred, proj1, proj2); + } + } +}; + +template void test_in_fwd(); From b029997a4ac1081c38cff75c68be55d1048e1d0a Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 12 May 2020 17:38:24 -0700 Subject: [PATCH 2/2] Review comment --- tests/std/tests/P0896R4_ranges_alg_find_first_of/test.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/std/tests/P0896R4_ranges_alg_find_first_of/test.cpp b/tests/std/tests/P0896R4_ranges_alg_find_first_of/test.cpp index fe8bfd3700..0d2dcb235f 100644 --- a/tests/std/tests/P0896R4_ranges_alg_find_first_of/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_find_first_of/test.cpp @@ -27,7 +27,7 @@ constexpr void smoke_test() { // Validate range overload [found case] const auto result = find_first_of(move_only_range{pairs}, good_needle, pred, get_first); STATIC_ASSERT(same_as>>); - assert(&*result.base() == &pairs.begin()[2]); + assert(to_address(result.base()) == pairs.data() + 2); } { // Validate iterator + sentinel overload [found case] @@ -35,7 +35,7 @@ constexpr void smoke_test() { const auto result = find_first_of( wrapped_pairs.begin(), wrapped_pairs.end(), good_needle.begin(), good_needle.end(), pred, get_first); STATIC_ASSERT(same_as>>); - assert(&*result.base() == &pairs.begin()[2]); + assert(to_address(result.base()) == pairs.data() + 2); } const array bad_needle = {29, 17}; @@ -43,7 +43,7 @@ constexpr void smoke_test() { // Validate range overload [not found case] const auto result = find_first_of(move_only_range{pairs}, bad_needle, pred, get_first); STATIC_ASSERT(same_as>>); - assert(to_address(result.base()) == to_address(pairs.end())); + assert(to_address(result.base()) == pairs.data() + pairs.size()); } { // Validate iterator + sentinel overload [not found case] @@ -51,7 +51,7 @@ constexpr void smoke_test() { const auto result = find_first_of( wrapped_pairs.begin(), wrapped_pairs.end(), bad_needle.begin(), bad_needle.end(), pred, get_first); STATIC_ASSERT(same_as>>); - assert(to_address(result.base()) == to_address(pairs.end())); + assert(to_address(result.base()) == pairs.data() + pairs.size()); } }