Skip to content
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
336 changes: 242 additions & 94 deletions stl/inc/algorithm

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions stl/inc/xutility
Original file line number Diff line number Diff line change
Expand Up @@ -1671,7 +1671,7 @@ _CONSTEXPR17 void advance(_InIt& _Where, _Diff _Off) { // increment iterator by
_STL_ASSERT(_Off >= 0, "negative advance of non-bidirectional iterator");
}

auto&& _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off);
decltype(auto) _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off);
constexpr bool _Need_rewrap = !is_reference_v<decltype(_Get_unwrapped_n(_STD move(_Where), _Off))>;

if constexpr (is_signed_v<_Diff> && _Is_bidi_iter_v<_InIt>) {
Expand All @@ -1695,7 +1695,7 @@ _CONSTEXPR17 void _Advance1(_InIt& _Where, _Diff _Off, input_iterator_tag) {
// increment iterator by offset, input iterators
_STL_ASSERT(_Off >= 0, "negative advance of non-bidirectional iterator");

auto&& _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off);
decltype(auto) _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off);
constexpr bool _Need_rewrap = !is_reference_v<decltype(_Get_unwrapped_n(_STD move(_Where), _Off))>;

for (; 0 < _Off; --_Off) {
Expand All @@ -1710,7 +1710,7 @@ _CONSTEXPR17 void _Advance1(_InIt& _Where, _Diff _Off, input_iterator_tag) {
template <class _BidIt, class _Diff>
_CONSTEXPR17 void _Advance1(_BidIt& _Where, _Diff _Off, bidirectional_iterator_tag) {
// increment iterator by offset, bidirectional iterators
auto&& _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off);
decltype(auto) _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off);
constexpr bool _Need_rewrap = !is_reference_v<decltype(_Get_unwrapped_n(_STD move(_Where), _Off))>;

for (; 0 < _Off; --_Off) {
Expand Down Expand Up @@ -3111,7 +3111,7 @@ namespace ranges {
_STL_ASSERT(_Off >= 0, "negative advance of non-bidirectional iterator");
}

auto&& _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off);
decltype(auto) _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off);
constexpr bool _Need_rewrap = !is_reference_v<decltype(_Get_unwrapped_n(_STD move(_Where), _Off))>;

if constexpr (bidirectional_iterator<_It>) {
Expand Down Expand Up @@ -3139,9 +3139,9 @@ namespace ranges {
} else {
_Adl_verify_range(_Where, _Last);

auto&& _UWhere = _Get_unwrapped(static_cast<_It&&>(_Where));
decltype(auto) _UWhere = _Get_unwrapped(static_cast<_It&&>(_Where));
constexpr bool _Need_rewrap = !is_reference_v<decltype(_Get_unwrapped(static_cast<_It&&>(_Where)))>;
auto&& _ULast = _Get_unwrapped(static_cast<_Se&&>(_Last));
decltype(auto) _ULast = _Get_unwrapped(static_cast<_Se&&>(_Last));

while (_UWhere != _ULast) {
++_UWhere;
Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ tests\P0896R4_ranges_alg_is_permutation
tests\P0896R4_ranges_alg_mismatch
tests\P0896R4_ranges_alg_move
tests\P0896R4_ranges_alg_none_of
tests\P0896R4_ranges_alg_search
tests\P0896R4_ranges_iterator_machinery
tests\P0896R4_ranges_range_machinery
tests\P0896R4_ranges_subrange
Expand Down
11 changes: 11 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_find_end/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <cassert>
#include <concepts>
#include <ranges>
#include <span>
#include <utility>
//
#include <range_algorithm_support.hpp>
Expand Down Expand Up @@ -58,6 +59,16 @@ constexpr void smoke_test() {
assert(result.begin() == pairs.end());
assert(result.end() == pairs.end());
}

{
// 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);
}
}

int main() {
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_search/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
175 changes: 175 additions & 0 deletions tests/std/tests/P0896R4_ranges_alg_search/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
// Copyright (c) Microsoft Corporation.
// 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>

using namespace std;

using P = pair<int, int>;

// Validate dangling story
STATIC_ASSERT(same_as<decltype(ranges::search(borrowed<false>{}, borrowed<true>{})), ranges::dangling>);
STATIC_ASSERT(same_as<decltype(ranges::search(borrowed<true>{}, borrowed<false>{})), ranges::subrange<int*>>);

struct instantiator {
static constexpr array<P, 8> pairs = {
P{0, 42}, P{1, 42}, P{2, 42}, P{3, 42}, P{4, 42}, P{5, 42}, P{6, 42}, P{7, 42}};
static constexpr array<int, 3> not_pairs = {2, 3, 4};
static constexpr array<int, 3> neg_not_pairs = {-2, -3, -4};

template <class Fwd1, class Fwd2>
static constexpr void call() {
const Fwd1 range1{pairs};
{
const Fwd2 range2{not_pairs};

// defaulted predicate and projections
{
auto result = ranges::search(range1, range1);
STATIC_ASSERT(same_as<decltype(result), ranges::subrange<ranges::iterator_t<Fwd1>>>);
assert(result.begin() == range1.begin());
assert(result.end() == range1.end());
}
{
auto result = ranges::search(
ranges::begin(range2), ranges::end(range2), ranges::begin(range2), ranges::end(range2));
STATIC_ASSERT(same_as<decltype(result), ranges::subrange<ranges::iterator_t<Fwd2>>>);
assert(result.begin() == range2.begin());
assert(result.end() == range2.end());
}
}

const Fwd2 range2{neg_not_pairs};
const auto pred = [](int x, int y) { return x == -y; };

// explicit predicate
{
auto result = ranges::search(range2, range2, ranges::equal_to{});
STATIC_ASSERT(same_as<decltype(result), ranges::subrange<ranges::iterator_t<Fwd2>>>);
assert(result.begin() == range2.begin());
assert(result.end() == range2.end());
}
{
auto result = ranges::search(ranges::begin(range1), ranges::end(range1), ranges::begin(range1),
ranges::end(range1), ranges::equal_to{});
STATIC_ASSERT(same_as<decltype(result), ranges::subrange<ranges::iterator_t<Fwd1>>>);
assert(result.begin() == range1.begin());
assert(result.end() == range1.end());
}

// explicit predicate and one projection
{
auto result = ranges::search(range1, range2, pred, get_first);
STATIC_ASSERT(same_as<decltype(result), ranges::subrange<ranges::iterator_t<Fwd1>>>);
assert(result.begin() == ranges::next(range1.begin(), 2));
assert(result.end() == ranges::next(range1.begin(), 5));
}
{
auto result = ranges::search(ranges::begin(range1), ranges::end(range1), ranges::begin(range2),
ranges::end(range2), pred, get_first);
STATIC_ASSERT(same_as<decltype(result), ranges::subrange<ranges::iterator_t<Fwd1>>>);
assert(result.begin() == ranges::next(range1.begin(), 2));
assert(result.end() == ranges::next(range1.begin(), 5));
}

// explicit predicate and two projections
constexpr auto minus1 = [](int x) { return x - 1; };
{
auto result = ranges::search(range1, range2, pred, get_first, minus1);
STATIC_ASSERT(same_as<decltype(result), ranges::subrange<ranges::iterator_t<Fwd1>>>);
assert(result.begin() == ranges::next(range1.begin(), 3));
assert(result.end() == ranges::next(range1.begin(), 6));
}
{
auto result = ranges::search(ranges::begin(range1), ranges::end(range1), ranges::begin(range2),
ranges::end(range2), pred, get_first, minus1);
STATIC_ASSERT(same_as<decltype(result), ranges::subrange<ranges::iterator_t<Fwd1>>>);
assert(result.begin() == ranges::next(range1.begin(), 3));
assert(result.end() == ranges::next(range1.begin(), 6));
}

// negative case
{
auto result = ranges::search(range2, range1, pred, minus1, get_first);
STATIC_ASSERT(same_as<decltype(result), ranges::subrange<ranges::iterator_t<Fwd2>>>);
assert(result.empty());
}
{
auto result = ranges::search(ranges::begin(range2), ranges::end(range2), ranges::begin(range1),
ranges::end(range1), pred, minus1, get_first);
STATIC_ASSERT(same_as<decltype(result), ranges::subrange<ranges::iterator_t<Fwd2>>>);
assert(result.empty());
}
}
};

using Elem1 = const P;
using Elem2 = const int;

#ifdef TEST_EVERYTHING
int main() {
// No constexpr test here; the test_fwd_fwd call exceeds the maximum number of steps in a constexpr computation.
test_fwd_fwd<instantiator, Elem1, Elem2>();
}
#else // ^^^ test all range combinations // test only interesting range combos vvv
template <class Elem, test::Sized IsSized>
using fwd_test_range = test::range<forward_iterator_tag, Elem, IsSized, test::CanDifference::no, test::Common::no,
test::CanCompare::yes, test::ProxyRef::yes>;
template <class Elem, test::Sized IsSized, test::Common IsCommon>
using random_test_range = test::range<random_access_iterator_tag, Elem, IsSized, test::CanDifference::no, IsCommon,
test::CanCompare::yes, test::ProxyRef::no>;

constexpr bool run_tests() {
// All (except contiguous) proxy reference types, since the algorithm doesn't really care. Cases with only 1 range
// sized are not interesting; common is interesting only in that it's necessary to trigger memcmp optimization.

using test::Common, test::Sized;

// both forward, non-common, and sized or unsized
instantiator::call<fwd_test_range<Elem1, Sized::no>, fwd_test_range<Elem2, Sized::no>>();
instantiator::call<fwd_test_range<Elem1, Sized::yes>, fwd_test_range<Elem2, Sized::yes>>();

// both random-access, and sized or unsized; all permutations of common
instantiator::call<random_test_range<Elem1, Sized::no, Common::no>,
random_test_range<Elem2, Sized::no, Common::no>>();
instantiator::call<random_test_range<Elem1, Sized::no, Common::no>,
random_test_range<Elem2, Sized::no, Common::yes>>();
instantiator::call<random_test_range<Elem1, Sized::no, Common::yes>,
random_test_range<Elem2, Sized::no, Common::no>>();
instantiator::call<random_test_range<Elem1, Sized::no, Common::yes>,
random_test_range<Elem2, Sized::no, Common::yes>>();
instantiator::call<random_test_range<Elem1, Sized::yes, Common::no>,
random_test_range<Elem2, Sized::yes, Common::no>>();
instantiator::call<random_test_range<Elem1, Sized::yes, Common::no>,
random_test_range<Elem2, Sized::yes, Common::yes>>();
instantiator::call<random_test_range<Elem1, Sized::yes, Common::yes>,
random_test_range<Elem2, Sized::yes, Common::no>>();
instantiator::call<random_test_range<Elem1, Sized::yes, Common::yes>,
random_test_range<Elem2, Sized::yes, Common::yes>>();

{
// 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 = ranges::search(span<const int>{haystack}, needle);
STATIC_ASSERT(same_as<decltype(result), const ranges::subrange<span<const int>::iterator>>);
assert(to_address(result.begin()) == haystack + 0);
assert(to_address(result.end()) == haystack + 3);
}

return true;
}

int main() {
STATIC_ASSERT(run_tests());
run_tests();
}
#endif // TEST_EVERYTHING