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

Implement ranges::find_first_of #821

Merged
merged 3 commits into from
May 14, 2020
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
47 changes: 47 additions & 0 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -2096,6 +2096,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 <input_iterator _It1, sentinel_for<_It1> _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));
CaseyCarter marked this conversation as resolved.
Show resolved Hide resolved
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 <input_range _Rng1, forward_range _Rng2, class _Pr = ranges::equal_to, class _Pj1 = identity,
class _Pj2 = identity>
requires indirectly_comparable<iterator_t<_Rng1>, 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 <class _FwdIt1, class _FwdIt2>
_CONSTEXPR20 _FwdIt2 swap_ranges(const _FwdIt1 _First1, const _FwdIt1 _Last1, _FwdIt2 _First2) {
Expand Down
5 changes: 5 additions & 0 deletions tests/std/include/range_algorithm_support.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,11 @@ void test_in_in() {
with_input_ranges<with_input_ranges<Instantiator>>::call();
}

template <class Instantiator>
void test_in_fwd() {
with_input_ranges<with_forward_ranges<Instantiator>>::call();
}

template <class Instantiator>
void test_fwd_fwd() {
with_forward_ranges<with_forward_ranges<Instantiator>>::call();
Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ tests\P0896R4_ranges_alg_count_if
tests\P0896R4_ranges_alg_equal
tests\P0896R4_ranges_alg_find
tests\P0896R4_ranges_alg_find_end
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
Expand Down
2 changes: 1 addition & 1 deletion tests/std/tests/P0896R4_ranges_alg_find/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ struct instantiator {
};
using Projection = type (*)(std::iter_common_reference_t<ranges::iterator_t<In>>);
(void) ranges::find(in, type{}, Projection{});
};
}
};

template void test_in<instantiator>();
4 changes: 4 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_find_first_of/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_matrix.lst
93 changes: 93 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_find_first_of/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <algorithm>
#include <array>
#include <cassert>
#include <concepts>
#include <ranges>
#include <utility>
//
#include <range_algorithm_support.hpp>

constexpr void smoke_test() {
using ranges::find_first_of, ranges::iterator_t, std::array, std::same_as, std::to_address;
using P = std::pair<int, int>;

// Validate dangling story
STATIC_ASSERT(same_as<decltype(find_first_of(borrowed<false>{}, array<int, 42>{})), ranges::dangling>);
STATIC_ASSERT(same_as<decltype(find_first_of(borrowed<true>{}, array<int, 42>{})), 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<decltype(result), const iterator_t<move_only_range<const P>>>);
assert(to_address(result.base()) == pairs.data() + 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<decltype(result), const iterator_t<move_only_range<const P>>>);
assert(to_address(result.base()) == pairs.data() + 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<decltype(result), const iterator_t<move_only_range<const P>>>);
assert(to_address(result.base()) == pairs.data() + pairs.size());
}
{
// 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<decltype(result), const iterator_t<move_only_range<const P>>>);
assert(to_address(result.base()) == pairs.data() + pairs.size());
}
}

int main() {
STATIC_ASSERT((smoke_test(), true));
smoke_test();
}

struct instantiator {
template <class In1, class Fwd2>
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<In1>, iterator_t<Fwd2>> 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<iterator_t<Fwd2>> halfpred{};
ProjectionFor<iterator_t<In1>> 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<iterator_t<In1>, 0> proj1{};
ProjectionFor<iterator_t<Fwd2>, 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<instantiator>();