Skip to content
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
ed0af15
Implement ranges::starts_with and ranges::ends_with
miscco Jun 10, 2021
a4d81a2
Address review feedback
miscco Jun 11, 2021
08ac0ed
Accept any integral for _Equal_count
miscco Jun 11, 2021
c884756
Merge branch 'main' into P1659-ranges-starts_with
miscco Jun 12, 2021
75b4380
Add feature test macro
miscco Jun 12, 2021
39bd70d
Apply optimizations for infinite ranges
miscco Jun 22, 2021
51c859d
Merge branch 'main' into P1659-ranges-starts_with
StephanTLavavej Jun 30, 2021
3817faf
Fix failing tests
miscco Jul 1, 2021
e2ac556
Rework so that we pass unwrapped iterators around
miscco Jul 1, 2021
e0a9b89
Merge branch 'main' into P1659-ranges-starts_with
miscco Aug 4, 2021
063f583
Fix formatting
miscco Aug 4, 2021
81e49e5
Merge branch 'main' into P1659-ranges-starts_with
miscco Aug 6, 2021
1e26004
Merge branch 'main' into P1659-ranges-starts_with
miscco Aug 24, 2021
b929362
Merge branch 'main' into P1659-ranges-starts_with
StephanTLavavej Aug 31, 2021
37a18ba
Sort `tests/std/test.lst`.
StephanTLavavej Aug 31, 2021
69141f7
Merge branch 'main' into P1659-ranges-starts_with
miscco Sep 16, 2021
5849ddb
Address review comments
miscco Sep 16, 2021
624f273
Fix incorret feature test macro placement
miscco Sep 17, 2021
5f22bb6
Fix infinite ranges and prepare LWG issue
miscco Sep 17, 2021
9164868
Begone infinite ranges
miscco Oct 14, 2021
2bf6d93
Merge branch 'main' into P1659-ranges-starts_with
miscco Oct 14, 2021
8c9bf6a
We cannot handle infinite input ranges at all in starts_with, as `_Eq…
miscco Oct 14, 2021
507f85b
Reenable infinite sizes optimization
miscco Oct 15, 2021
58da2b3
optimize ends_with; fix borked tests
CaseyCarter Oct 18, 2021
cd7733d
Factor out large middle section of _Ends_with_impl
CaseyCarter Oct 18, 2021
2df4fa1
Review comments
CaseyCarter Oct 19, 2021
42c9471
[[nonodiscard]]
CaseyCarter Oct 19, 2021
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
309 changes: 293 additions & 16 deletions stl/inc/algorithm

Large diffs are not rendered by default.

107 changes: 63 additions & 44 deletions stl/inc/xutility
Original file line number Diff line number Diff line change
Expand Up @@ -4764,44 +4764,40 @@ namespace ranges {
template <class _In1, class _In2>
using mismatch_result = in_in_result<_In1, _In2>;

class _Mismatch_fn : private _Not_quite_object {
private:
template <class _It1, class _It2, class _Pr, class _Pj1, class _Pj2>
_NODISCARD static constexpr mismatch_result<_It1, _It2> _Mismatch_n(
_It1 _First1, _It2 _First2, iter_difference_t<_It1> _Count, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) {
auto _UFirst1 = _Get_unwrapped(_STD move(_First1));
auto _UFirst2 = _Get_unwrapped(_STD move(_First2));

for (; _Count != 0; ++_UFirst1, (void) ++_UFirst2, --_Count) {
if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_UFirst1), _STD invoke(_Proj2, *_UFirst2))) {
break;
}
// clang-format off
template <input_iterator _It1, input_iterator _It2, class _Pr, class _Pj1, class _Pj2>
requires indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2>
_NODISCARD constexpr mismatch_result<_It1, _It2> _Mismatch_n(
_It1 _First1, _It2 _First2, iter_difference_t<_It1> _Count, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) {
// clang-format on
_STL_INTERNAL_CHECK(_Count >= 0);
for (; _Count != 0; ++_First1, (void) ++_First2, --_Count) {
if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) {
break;
}

_Seek_wrapped(_First1, _STD move(_UFirst1));
_Seek_wrapped(_First2, _STD move(_UFirst2));
return {_STD move(_First1), _STD move(_First2)};
}

template <class _It1, class _Se1, class _It2, class _Se2, class _Pr, class _Pj1, class _Pj2>
_NODISCARD static constexpr mismatch_result<_It1, _It2> _Mismatch_4(
_It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) {
auto _UFirst1 = _Get_unwrapped(_STD move(_First1));
const auto _ULast1 = _Get_unwrapped(_STD move(_Last1));
auto _UFirst2 = _Get_unwrapped(_STD move(_First2));
const auto _ULast2 = _Get_unwrapped(_STD move(_Last2));

for (; _UFirst1 != _ULast1 && _UFirst2 != _ULast2; ++_UFirst1, (void) ++_UFirst2) {
if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_UFirst1), _STD invoke(_Proj2, *_UFirst2))) {
break;
}
}
return {_STD move(_First1), _STD move(_First2)};
}

// clang-format off
template <input_iterator _It1, sentinel_for<_It1> _Se1, input_iterator _It2, sentinel_for<_It2> _Se2, class _Pr,
class _Pj1, class _Pj2>
requires indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2>
_NODISCARD constexpr mismatch_result<_It1, _It2> _Mismatch_4(
_It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) {
// clang-format on

_Seek_wrapped(_First1, _STD move(_UFirst1));
_Seek_wrapped(_First2, _STD move(_UFirst2));
return {_STD move(_First1), _STD move(_First2)};
for (; _First1 != _Last1 && _First2 != _Last2; ++_First1, (void) ++_First2) {
if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) {
break;
}
}

return {_STD move(_First1), _STD move(_First2)};
}

class _Mismatch_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

Expand All @@ -4811,45 +4807,64 @@ namespace ranges {
requires indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2>
_NODISCARD constexpr mismatch_result<_It1, _It2> operator()(_It1 _First1, _Se1 _Last1,
_It2 _First2, _Se2 _Last2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const {
// clang-format on
_Adl_verify_range(_First1, _Last1);
_Adl_verify_range(_First2, _Last2);

if constexpr (sized_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2>) {
iter_difference_t<_It1> _Count1 = _Last1 - _First1;
iter_difference_t<_It1> _Count1 = _Last1 - _First1;
const iter_difference_t<_It2> _Count2 = _Last2 - _First2;
if (_Count1 > _Count2) {
_Count1 = static_cast<decltype(_Count1)>(_Count2);
}

return _Mismatch_n(_STD move(_First1), _STD move(_First2), _Count1,
_Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2));
auto _Result = _RANGES _Mismatch_n(_Get_unwrapped(_STD move(_First1)),
_Get_unwrapped(_STD move(_First2)), _Count1, _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2));
_Seek_wrapped(_First1, _STD move(_Result.in1));
_Seek_wrapped(_First2, _STD move(_Result.in2));
return {_STD move(_First1), _STD move(_First2)};
} else {
return _Mismatch_4(_STD move(_First1), _STD move(_Last1), _STD move(_First2), _STD move(_Last2),
_Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2));
auto _Result = _RANGES _Mismatch_4(_Get_unwrapped(_STD move(_First1)),
_Get_unwrapped(_STD move(_Last1)), _Get_unwrapped(_STD move(_First2)),
_Get_unwrapped(_STD move(_Last2)), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2));
_Seek_wrapped(_First1, _STD move(_Result.in1));
_Seek_wrapped(_First2, _STD move(_Result.in2));
return {_STD move(_First1), _STD move(_First2)};
}
}

// clang-format off
template <input_range _Rng1, input_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 mismatch_result<borrowed_iterator_t<_Rng1>, borrowed_iterator_t<_Rng2>> operator()(
_Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const {
// clang-format on
if constexpr (sized_range<_Rng1> && sized_range<_Rng2>) {
range_difference_t<_Rng1> _Count1 = _RANGES distance(_Range1);
range_difference_t<_Rng1> _Count1 = _RANGES distance(_Range1);
const range_difference_t<_Rng2> _Count2 = _RANGES distance(_Range2);
if (_Count1 > _Count2) {
_Count1 = static_cast<range_difference_t<_Rng1>>(_Count2);
}

return _Mismatch_n(_RANGES begin(_Range1), _RANGES begin(_Range2), _Count1,
_Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2));
auto _First1 = _RANGES begin(_Range1);
auto _First2 = _RANGES begin(_Range2);
auto _Result = _RANGES _Mismatch_n(_Get_unwrapped(_STD move(_First1)),
_Get_unwrapped(_STD move(_First2)), _Count1, _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2));
_Seek_wrapped(_First1, _STD move(_Result.in1));
_Seek_wrapped(_First2, _STD move(_Result.in2));
return {_STD move(_First1), _STD move(_First2)};
} else {
return _Mismatch_4(_RANGES begin(_Range1), _RANGES end(_Range1),
_RANGES begin(_Range2), _RANGES end(_Range2),
_Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2));
auto _First1 = _RANGES begin(_Range1);
auto _First2 = _RANGES begin(_Range2);
auto _Result = _RANGES _Mismatch_4(_Get_unwrapped(_STD move(_First1)), _Uend(_Range1),
_Get_unwrapped(_STD move(_First2)), _Uend(_Range2), _Pass_fn(_Pred), _Pass_fn(_Proj1),
_Pass_fn(_Proj2));
_Seek_wrapped(_First1, _STD move(_Result.in1));
_Seek_wrapped(_First2, _STD move(_Result.in2));
return {_STD move(_First1), _STD move(_First2)};
}
}
// clang-format on
};

inline constexpr _Mismatch_fn mismatch{_Not_quite_object::_Construct_tag{}};
Expand Down Expand Up @@ -5196,6 +5211,10 @@ namespace ranges {
concept _Sized_or_unreachable_sentinel_for = sized_sentinel_for<_Se, _It> || same_as<_Se, unreachable_sentinel_t>;

// clang-format off
template <class _Rng>
concept _Sized_or_infinite_range = range<_Rng>
&& (sized_range<_Rng> || same_as<sentinel_t<_Rng>, unreachable_sentinel_t>);

// concept-constrained for strict enforcement as it is used by several algorithms
template <input_iterator _It, sentinel_for<_It> _Se, class _Ty, class _Pj = identity>
requires indirect_binary_predicate<ranges::equal_to, projected<_It, _Pj>, const _Ty*>
Expand Down
4 changes: 3 additions & 1 deletion stl/inc/yvals_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@
// P1048R1 is_scoped_enum
// P1132R7 out_ptr(), inout_ptr()
// P1425R4 Iterator Pair Constructors For stack And queue
// P1659R3 ranges::starts_with, ranges::ends_with
// P1679R3 contains() For basic_string/basic_string_view
// P1682R3 to_underlying() For Enumerations
// P1951R1 Default Template Arguments For pair's Forwarding Constructor
Expand Down Expand Up @@ -1380,7 +1381,8 @@
#define __cpp_lib_is_scoped_enum 202011L

#ifdef __cpp_lib_concepts
#define __cpp_lib_out_ptr 202106L
#define __cpp_lib_out_ptr 202106L
#define __cpp_lib_ranges_starts_ends_with 202106L
#endif // __cpp_lib_concepts

#define __cpp_lib_spanstream 202106L
Expand Down
2 changes: 2 additions & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,8 @@ tests\P1502R1_standard_library_header_units
tests\P1518R2_stop_overconstraining_allocators
tests\P1614R2_spaceship
tests\P1645R1_constexpr_numeric
tests\P1659R3_ranges_alg_ends_with
tests\P1659R3_ranges_alg_starts_with
tests\P1682R3_to_underlying
tests\P1951R1_default_arguments_pair_forward_ctor
tests\P2136R3_invoke_r
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P1659R3_ranges_alg_ends_with/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_latest_matrix.lst
96 changes: 96 additions & 0 deletions tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <algorithm>
#include <cassert>
#include <functional>
#include <ranges>
#include <utility>

#include <range_algorithm_support.hpp>

using namespace std;

// clang-format off
template <class T>
concept testable_range = ranges::input_range<T> && (ranges::forward_range<T> || ranges::sized_range<T>);

template <class T>
concept testable_sentinel = ranges::input_range<T>
&& (ranges::forward_range<T> || sized_sentinel_for<ranges::sentinel_t<T>, ranges::iterator_t<T>>);
// clang-format on

struct instantiator {
static constexpr pair<int, int> haystack[] = {{0, 42}, {2, 42}, {4, 42}};
static constexpr pair<int, int> short_haystack[] = {{4, 42}};
static constexpr pair<long, long> needle[] = {{13, 2}, {13, 4}};
static constexpr pair<long, long> wrong_needle[] = {{13, 2}, {13, 3}};

template <testable_range In1, testable_range In2>
static constexpr void test_range() {
using ranges::ends_with, ranges::equal_to;

{
const same_as<bool> auto match = ends_with(In1{haystack}, In2{needle}, equal_to{}, get_first, get_second);
assert(match);
}
{
const same_as<bool> auto match =
ends_with(In1{haystack}, In2{wrong_needle}, equal_to{}, get_first, get_second);
assert(!match);
}
{
const same_as<bool> auto match =
ends_with(In1{short_haystack}, In2{needle}, equal_to{}, get_first, get_second);
assert(!match);
}
}

template <testable_sentinel In1, testable_sentinel In2>
static constexpr void test_iterator_sentinel() {
using ranges::begin, ranges::end, ranges::ends_with, ranges::equal_to;

{
In1 h{haystack};
In2 n{needle};
const same_as<bool> auto match =
ends_with(begin(h), end(h), begin(n), end(n), equal_to{}, get_first, get_second);
assert(match);
}

{
In1 h{haystack};
In2 n{wrong_needle};
const same_as<bool> auto match =
ends_with(begin(h), end(h), begin(n), end(n), equal_to{}, get_first, get_second);
assert(!match);
}

{
In1 h{short_haystack};
In2 n{needle};
const same_as<bool> auto match =
ends_with(begin(h), end(h), begin(n), end(n), equal_to{}, get_first, get_second);
assert(!match);
}
}

template <ranges::input_range In1, ranges::input_range In2>
static void call() {
if constexpr (testable_range<In1> && testable_range<In2>) {
test_range<In1, In2>();
STATIC_ASSERT((test_range<In1, In2>(), true));
}

if constexpr (testable_sentinel<In1> && testable_sentinel<In2>) {
test_iterator_sentinel<In1, In2>();
STATIC_ASSERT((test_iterator_sentinel<In1, In2>(), true));
}
}
};

int main() {
#ifndef _PREFAST_ // TRANSITION, GH-1030
test_in_in<instantiator, const pair<int, int>, const pair<long, long>>();
#endif // TRANSITION, GH-1030
}
4 changes: 4 additions & 0 deletions tests/std/tests/P1659R3_ranges_alg_starts_with/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_latest_matrix.lst
84 changes: 84 additions & 0 deletions tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <algorithm>
#include <cassert>
#include <functional>
#include <ranges>
#include <utility>

#include <range_algorithm_support.hpp>

using namespace std;

struct instantiator {
static constexpr pair<int, int> haystack[] = {{0, 42}, {2, 42}, {4, 42}};
static constexpr pair<int, int> short_haystack[] = {{0, 42}};
static constexpr pair<long, long> needle[] = {{13, 0}, {13, 2}};
static constexpr pair<long, long> wrong_needle[] = {{13, 0}, {13, 3}};

template <ranges::input_range In1, ranges::input_range In2>
static constexpr void test() {
using ranges::begin, ranges::end, ranges::starts_with;

// Validate range overload
{
const same_as<bool> auto match = starts_with(In1{haystack}, In2{needle}, equal_to{}, get_first, get_second);
assert(match);
}
{
const same_as<bool> auto match =
starts_with(In1{haystack}, In2{wrong_needle}, equal_to{}, get_first, get_second);
assert(!match);
}
{
const same_as<bool> auto match =
starts_with(In1{short_haystack}, In2{needle}, equal_to{}, get_first, get_second);
assert(!match);
}

// Validate iterator + sentinel overload
{
In1 h{haystack};
In2 n{needle};
const same_as<bool> auto match =
starts_with(begin(h), end(h), begin(n), end(n), equal_to{}, get_first, get_second);
assert(match);
}
{
In1 h{haystack};
In2 n{wrong_needle};
const same_as<bool> auto match =
starts_with(begin(h), end(h), begin(n), end(n), equal_to{}, get_first, get_second);
assert(!match);
}
{
In1 h{short_haystack};
In2 n{needle};
const same_as<bool> auto match =
starts_with(begin(h), end(h), begin(n), end(n), equal_to{}, get_first, get_second);
assert(!match);
}
}

template <ranges::input_range In1, ranges::input_range In2>
static void call() {
test<In1, In2>();
STATIC_ASSERT((test<In1, In2>(), true));
}
};

int main() {
{ // infinite haystack
const same_as<bool> auto match = ranges::starts_with(views::iota(0), views::iota(0, 5));
assert(match);
}
{ // infinite needle
const same_as<bool> auto match = ranges::starts_with(views::iota(0, 5), views::iota(0));
assert(!match);
}

#ifndef _PREFAST_ // TRANSITION, GH-1030
test_in_in<instantiator, const pair<int, int>, const pair<long, long>>();
#endif // TRANSITION, GH-1030
}
Loading