Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
157 changes: 141 additions & 16 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -714,27 +714,29 @@ _NODISCARD pair<_FwdIt1, _FwdIt2> mismatch(
#ifdef __cpp_lib_concepts
namespace ranges {
// VARIABLE ranges::equal
class _Equal_fn : private _Not_quite_object {
private:
template <class _It1, class _It2, class _Size, class _Pr, class _Pj1, class _Pj2>
_NODISCARD static constexpr bool _Equal_count(
_It1 _First1, _It2 _First2, _Size _Count, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) {
if constexpr (_Equal_memcmp_is_safe<_It1, _It2,
_Pr> && same_as<_Pj1, identity> && same_as<_Pj2, identity>) {
if (!_STD is_constant_evaluated()) {
return _Memcmp_count(_First1, _First2, static_cast<size_t>(_Count)) == 0;
}
// clang-format off
template <input_iterator _It1, input_iterator _It2, integral _Size, class _Pr, class _Pj1, class _Pj2>
requires indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2>
_NODISCARD static constexpr bool _Equal_count(
Comment thread
CaseyCarter marked this conversation as resolved.
Resolved
_It1 _First1, _It2 _First2, _Size _Count, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) {
// clang-format on
Comment thread
miscco marked this conversation as resolved.
Resolved
if constexpr (_Equal_memcmp_is_safe<_It1, _It2, _Pr> && same_as<_Pj1, identity> && same_as<_Pj2, identity>) {
if (!_STD is_constant_evaluated()) {
return _Memcmp_count(_First1, _First2, static_cast<size_t>(_Count)) == 0;
}
}

for (; _Count != 0; ++_First1, (void) ++_First2, --_Count) {
if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) {
return false;
}
for (; _Count != 0; ++_First1, (void) ++_First2, --_Count) {
if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) {
return false;
}

return true;
}

return true;
}

class _Equal_fn : private _Not_quite_object {
private:
template <class _It1, class _Se1, class _It2, class _Se2, class _Pr, class _Pj1, class _Pj2>
_NODISCARD static constexpr bool _Equal_4(
_It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) {
Expand Down Expand Up @@ -2422,6 +2424,129 @@ namespace ranges {
};

inline constexpr _Search_n_fn search_n{_Not_quite_object::_Construct_tag{}};

#if _HAS_CXX23
// VARIABLE ranges::starts_with
Comment thread
miscco marked this conversation as resolved.
Resolved
class _Starts_with_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

// clang-format off
template <input_iterator _It1, sentinel_for<_It1> _Se1, input_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 bool 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>) {
Comment thread
CaseyCarter marked this conversation as resolved.
Resolved
const iter_difference_t<_It1> _Count1 = _Last1 - _First1;
const iter_difference_t<_It2> _Count2 = _Last2 - _First2;
if (_Count2 > _Count1) {
return false;
}
Comment thread
miscco marked this conversation as resolved.
Resolved

return _RANGES _Equal_count(_STD move(_First1), _STD move(_First2),
static_cast<iter_difference_t<_It1>>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2));
} else {
const auto _Result = _RANGES _Mismatch_4(_STD move(_First1), _STD move(_Last1), _STD move(_First2),
_STD move(_Last2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2));
return _Result.in2 == _Last2;
}
}

// 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 bool operator()(
_Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const {
// clang-format on
if constexpr (sized_range<_Rng1> && sized_range<_Rng2>) {
const range_difference_t<_Rng1> _Count1 = _RANGES distance(_Range1);
const range_difference_t<_Rng2> _Count2 = _RANGES distance(_Range2);
if (_Count2 > _Count1) {
return false;
}
Comment thread
miscco marked this conversation as resolved.
Resolved

return _RANGES _Equal_count(_RANGES begin(_Range1), _RANGES begin(_Range2),
static_cast<range_difference_t<_Rng1>>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1),
_Pass_fn(_Proj2));
} else {
const auto _Result = _RANGES _Mismatch_4(_RANGES begin(_Range1), _RANGES end(_Range1),
_RANGES begin(_Range2), _RANGES end(_Range2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2));
return _Result.in2 == _RANGES end(_Range2);
}
}
};

inline constexpr _Starts_with_fn starts_with{_Not_quite_object::_Construct_tag{}};

// VARIABLE ranges::ends_with
Comment thread
miscco marked this conversation as resolved.
Resolved
class _Ends_with_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;

// clang-format off
template <input_iterator _It1, sentinel_for<_It1> _Se1, input_iterator _It2, sentinel_for<_It2> _Se2,
class _Pr = ranges::equal_to, class _Pj1 = identity, class _Pj2 = identity>
requires (forward_iterator<_It1> || sized_sentinel_for<_Se1, _It1>)
&& (forward_iterator<_It2> || sized_sentinel_for<_Se2, _It2>)
&& indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2>
_NODISCARD constexpr bool 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);

iter_difference_t<_It1> _Count1;
if constexpr (sized_sentinel_for<_Se1, _It1>) {
_Count1 = _Last1 - _First1;
} else {
_Count1 = _RANGES distance(_First1, _Last1);
}
Comment thread
miscco marked this conversation as resolved.
Resolved

iter_difference_t<_It2> _Count2;
if constexpr (sized_sentinel_for<_Se2, _It2>) {
_Count2 = _Last2 - _First2;
} else {
_Count2 = _RANGES distance(_First2, _Last2);
}
Comment thread
miscco marked this conversation as resolved.
Resolved

if (_Count2 > _Count1) {
return false;
}

_RANGES advance(_First1, (_Count1 - _Count2));
return _RANGES _Equal_count(_STD move(_First1), _STD move(_First2),
static_cast<iter_difference_t<_It1>>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2));
}

// clang-format off
template <input_range _Rng1, input_range _Rng2, class _Pr = ranges::equal_to, class _Pj1 = identity,
class _Pj2 = identity>
requires (forward_range<_Rng1> || sized_range<_Rng1>) && (forward_range<_Rng2> || sized_range<_Rng2>)
&& indirectly_comparable<iterator_t<_Rng1>, iterator_t<_Rng2>, _Pr, _Pj1, _Pj2>
_NODISCARD constexpr bool operator()(
_Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const {
// clang-format on
const range_difference_t<_Rng1> _Count1 = _RANGES distance(_Range1);
const range_difference_t<_Rng2> _Count2 = _RANGES distance(_Range2);
if (_Count2 > _Count1) {
return false;
Comment thread
miscco marked this conversation as resolved.
Resolved
}

auto _First1 = _RANGES begin(_Range1);
_RANGES advance(_First1, (_Count1 - _Count2));
return _RANGES _Equal_count(_STD move(_First1), _RANGES begin(_Range2),
static_cast<range_difference_t<_Rng1>>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2));
}
};

inline constexpr _Ends_with_fn ends_with{_Not_quite_object::_Construct_tag{}};
#endif // _HAS_CXX23
} // namespace ranges
#endif // __cpp_lib_concepts

Expand Down
91 changes: 49 additions & 42 deletions stl/inc/xutility
Original file line number Diff line number Diff line change
Expand Up @@ -4933,44 +4933,50 @@ namespace ranges {
using mismatch_result = in_in_result<_In1, _In2>;

// VARIABLE ranges::mismatch
Comment thread
miscco marked this conversation as resolved.
Resolved
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
Comment thread
miscco marked this conversation as resolved.
Resolved
auto _UFirst1 = _Get_unwrapped(_STD move(_First1));
auto _UFirst2 = _Get_unwrapped(_STD move(_First2));

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

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));
_Seek_wrapped(_First1, _STD move(_UFirst1));
_Seek_wrapped(_First2, _STD move(_UFirst2));
return {_STD move(_First1), _STD move(_First2)};
}

for (; _UFirst1 != _ULast1 && _UFirst2 != _ULast2; ++_UFirst1, (void) ++_UFirst2) {
if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_UFirst1), _STD invoke(_Proj2, *_UFirst2))) {
break;
}
}
// 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
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));

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

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

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

Expand All @@ -4980,45 +4986,46 @@ 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));
return _RANGES _Mismatch_n(_STD move(_First1), _STD move(_First2), _Count1, _Pass_fn(_Pred),
_Pass_fn(_Proj1), _Pass_fn(_Proj2));
} else {
return _Mismatch_4(_STD move(_First1), _STD move(_Last1), _STD move(_First2), _STD move(_Last2),
return _RANGES _Mismatch_4(_STD move(_First1), _STD move(_Last1), _STD move(_First2), _STD move(_Last2),
_Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2));
}
}

// 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));
return _RANGES _Mismatch_n(_RANGES begin(_Range1), _RANGES begin(_Range2), _Count1, _Pass_fn(_Pred),
_Pass_fn(_Proj1), _Pass_fn(_Proj2));
} 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));
return _RANGES _Mismatch_4(_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 _Mismatch_fn mismatch{_Not_quite_object::_Construct_tag{}};
Expand Down
3 changes: 3 additions & 0 deletions stl/inc/yvals_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@
// P1831R1 Deprecating volatile In The Standard Library
// Other C++20 deprecation warnings

// _HAS_CXX23 directly controls
// P1659R3 ranges::starts_with, ranges::ends_with

// Parallel Algorithms Notes
// C++ allows an implementation to implement parallel algorithms as calls to the serial algorithms.
// This implementation parallelizes several common algorithm calls, but not all.
Expand Down
2 changes: 2 additions & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,8 @@ tests\P1208R6_source_location
tests\P1423R3_char8_t_remediation
tests\P1502R1_standard_library_header_units
tests\P1614R2_spaceship
tests\P1659R3_ranges_alg_ends_with
tests\P1659R3_ranges_alg_starts_with
tests\P1645R1_constexpr_numeric
tests\VSO_0000000_allocator_propagation
tests\VSO_0000000_any_calling_conventions
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
Loading