Skip to content

Commit

Permalink
Implement ranges::find_first_of (#821)
Browse files Browse the repository at this point in the history
  • Loading branch information
CaseyCarter authored May 14, 2020
1 parent 48f79a3 commit 79c6021
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 1 deletion.
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));
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 @@ -241,6 +241,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>();

0 comments on commit 79c6021

Please sign in to comment.