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

Fix ranges::find_end return for bidi-common case #2270

Merged
merged 2 commits into from Nov 13, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion stl/inc/algorithm
Expand Up @@ -2363,7 +2363,7 @@ namespace ranges {
auto _Next2 = _Last2;
for (;;) { // test if [_First2, _Last2) is a suffix of [_First1, _Candidate)
if (_First2 == _Next2) { // match found
return {_STD move(_First1), _STD move(_Last1)};
return {_STD move(_Next1), _STD move(_Candidate)};
}

if (_First1 == _Next1) {
Expand Down
169 changes: 79 additions & 90 deletions tests/std/tests/P0896R4_ranges_alg_find_end/test.cpp
Expand Up @@ -2,113 +2,102 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <algorithm>
#include <array>
#include <cassert>
#include <concepts>
#include <ranges>
#include <span>
#include <utility>

#include <range_algorithm_support.hpp>

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

// Validate dangling story
STATIC_ASSERT(same_as<decltype(find_end(borrowed<false>{}, array<int, 42>{})), ranges::dangling>);
STATIC_ASSERT(same_as<decltype(find_end(borrowed<true>{}, array<int, 42>{})), subrange<int*>>);
// Validate dangling story
STATIC_ASSERT(same_as<decltype(ranges::find_end(borrowed<false>{}, span<const int>{})), ranges::dangling>);
STATIC_ASSERT(same_as<decltype(ranges::find_end(borrowed<true>{}, span<const int>{})), ranges::subrange<int*>>);

const array pairs = {P{0, 42}, P{1, 42}, P{0, 42}, P{1, 42}, P{0, 42}, P{1, 42}, P{0, 42}};
struct instantiator {
static constexpr pair<int, int> pairs[] = {{0, 42}, {1, 42}, {0, 42}, {1, 42}, {0, 42}};
static constexpr auto pred = [](const int x, const int y) { return x == y + 1; };

static constexpr int good_needle[] = {-1, 0};
static constexpr int bad_needle[] = {0, 0};

template <ranges::forward_range Fwd1, ranges::forward_range Fwd2>
static constexpr void test() {
using ranges::find_end, ranges::begin, ranges::end, ranges::iterator_t, ranges::next, ranges::subrange;

constexpr bool sized_result = sized_sentinel_for<iterator_t<Fwd1>, iterator_t<Fwd1>>;

{
// Validate range overload [found case]
Fwd1 haystack{pairs};
Fwd2 needle{good_needle};
const same_as<subrange<iterator_t<Fwd1>>> auto result = find_end(haystack, needle, pred, get_first);
STATIC_ASSERT(CanMemberSize<subrange<iterator_t<Fwd1>>> == sized_result);
if constexpr (sized_result) {
assert(result.size() == 2);
}
assert(result.begin() == next(begin(haystack), 2));
assert(result.end() == next(begin(haystack), 4));
}

const auto pred = [](const int x, const int y) { return x == y + 1; };
{
// Validate range overload [not found case]
Fwd1 haystack{pairs};
Fwd2 needle{bad_needle};
const same_as<subrange<iterator_t<Fwd1>>> auto result = find_end(haystack, needle, pred, get_first);
assert(result.empty());
assert(result.begin() == end(haystack));
assert(result.end() == end(haystack));
}

const array good_needle = {-1, 0};
{
// Validate range overload [found case]
const auto result = find_end(pairs, good_needle, pred, get_first);
STATIC_ASSERT(same_as<decltype(result), const subrange<iterator_t<decltype(pairs)>>>);
assert(result.size() == 2);
assert(result.begin() == pairs.begin() + 4);
assert(result.end() == pairs.begin() + 6);
}
{
// Validate iterator + sentinel overload [found case]
const auto result =
find_end(pairs.begin(), pairs.end(), good_needle.begin(), good_needle.end(), pred, get_first);
STATIC_ASSERT(same_as<decltype(result), const subrange<iterator_t<decltype(pairs)>>>);
assert(result.size() == 2);
assert(result.begin() == pairs.begin() + 4);
assert(result.end() == pairs.begin() + 6);
}
{
// Validate iterator + sentinel overload [found case]
Fwd1 haystack{pairs};
Fwd2 needle{good_needle};
const same_as<subrange<iterator_t<Fwd1>>> auto result =
find_end(begin(haystack), end(haystack), begin(needle), end(needle), pred, get_first);
if constexpr (sized_result) {
assert(result.size() == 2);
}
assert(result.begin() == next(begin(haystack), 2));
assert(result.end() == next(begin(haystack), 4));
}

const array bad_needle = {0, 0};
{
// Validate range overload [not found case]
const auto result = find_end(pairs, bad_needle, pred, get_first);
STATIC_ASSERT(same_as<decltype(result), const subrange<iterator_t<decltype(pairs)>>>);
assert(result.empty());
assert(result.begin() == pairs.end());
assert(result.end() == pairs.end());
}
{
// Validate range overload [not found case]
const auto result = find_end(pairs.begin(), pairs.end(), bad_needle.begin(), bad_needle.end(), pred, get_first);
STATIC_ASSERT(same_as<decltype(result), const subrange<iterator_t<decltype(pairs)>>>);
assert(result.empty());
assert(result.begin() == pairs.end());
assert(result.end() == pairs.end());
{
// Validate range overload [not found case]
Fwd1 haystack{pairs};
Fwd2 needle{bad_needle};
const same_as<subrange<iterator_t<Fwd1>>> auto result =
find_end(begin(haystack), end(haystack), begin(needle), end(needle), pred, get_first);
assert(result.empty());
assert(result.begin() == end(haystack));
assert(result.end() == end(haystack));
}
}

{
// Validate the memcmp optimization
const int haystack[] = {1, 2, 3, 1, 2, 3, 1, 2, 3};
const int needle[] = {1, 2, 3};
const auto result = find_end(haystack, std::span<const int>{needle});
STATIC_ASSERT(same_as<decltype(result), const subrange<const int*>>);
assert(result.begin() == haystack + 6);
assert(result.end() == haystack + 9);
template <ranges::forward_range Fwd1, ranges::forward_range Fwd2>
static void call() {
test<Fwd1, Fwd2>();
STATIC_ASSERT((test<Fwd1, Fwd2>(), true));
}
}
};

int main() {
STATIC_ASSERT((smoke_test(), true));
smoke_test();
constexpr bool memcmp_test() {
// Validate the memcmp optimization
const int haystack[] = {1, 2, 3, 1, 2, 3, 1, 2, 3};
const int needle[] = {2, 3, 1};
const same_as<ranges::subrange<const int*>> auto result = ranges::find_end(haystack, span<const int>{needle});
assert(result.begin() == haystack + 4);
assert(result.end() == haystack + 7);
return true;
}

int main() {
#ifndef _PREFAST_ // TRANSITION, GH-1030
CaseyCarter marked this conversation as resolved.
Show resolved Hide resolved
struct instantiator {
template <class Fwd1, class Fwd2>
static void call() {
if constexpr (!is_permissive) { // These fail to compile in C1XX's permissive mode due to VSO-566808
using ranges::iterator_t;

Fwd1 fwd1{std::span<const int, 0>{}};
Fwd2 fwd2{std::span<const int, 0>{}};

(void) ranges::find_end(fwd1, fwd2);
(void) ranges::find_end(ranges::begin(fwd1), ranges::end(fwd1), ranges::begin(fwd2), ranges::end(fwd2));

BinaryPredicateFor<iterator_t<Fwd1>, iterator_t<Fwd2>> pred{};
(void) ranges::find_end(fwd1, fwd2, pred);
(void) ranges::find_end(
ranges::begin(fwd1), ranges::end(fwd1), ranges::begin(fwd2), ranges::end(fwd2), pred);

HalfProjectedBinaryPredicateFor<iterator_t<Fwd2>> halfpred{};
ProjectionFor<iterator_t<Fwd1>> halfproj{};
(void) ranges::find_end(fwd1, fwd2, halfpred, halfproj);
(void) ranges::find_end(
ranges::begin(fwd1), ranges::end(fwd1), ranges::begin(fwd2), ranges::end(fwd2), halfpred, halfproj);

ProjectedBinaryPredicate<0, 1> projpred{};
ProjectionFor<iterator_t<Fwd1>, 0> proj1{};
ProjectionFor<iterator_t<Fwd2>, 1> proj2{};
(void) ranges::find_end(fwd1, fwd2, projpred, proj1, proj2);
(void) ranges::find_end(
ranges::begin(fwd1), ranges::end(fwd1), ranges::begin(fwd2), ranges::end(fwd2), projpred, proj1, proj2);
}
}
};
test_fwd_fwd<instantiator, const pair<int, int>, const int>();
#endif // TRANSITION

template void test_fwd_fwd<instantiator, const int, const int>();
#endif // TRANSITION, GH-1030
STATIC_ASSERT(memcmp_test());
memcmp_test();
}