From ed0af15be969665ecac33372b88fc51db22c8328 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Thu, 10 Jun 2021 13:54:40 +0200 Subject: [PATCH 01/19] Implement ranges::starts_with and ranges::ends_with Fixes #1975 --- stl/inc/algorithm | 127 ++++++++++++++++++ stl/inc/xutility | 61 +++++---- stl/inc/yvals_core.h | 3 + tests/std/test.lst | 2 + .../P1659R3_ranges_alg_ends_with/env.lst | 4 + .../P1659R3_ranges_alg_ends_with/test.cpp | 99 ++++++++++++++ .../P1659R3_ranges_alg_starts_with/env.lst | 4 + .../P1659R3_ranges_alg_starts_with/test.cpp | 113 ++++++++++++++++ 8 files changed, 382 insertions(+), 31 deletions(-) create mode 100644 tests/std/tests/P1659R3_ranges_alg_ends_with/env.lst create mode 100644 tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp create mode 100644 tests/std/tests/P1659R3_ranges_alg_starts_with/env.lst create mode 100644 tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 9fcee8193aa..c844b498006 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -2422,6 +2422,133 @@ namespace ranges { }; inline constexpr _Search_n_fn search_n{_Not_quite_object::_Construct_tag{}}; + +#if _HAS_CXX23 + // VARIABLE ranges::starts_with + class _Starts_with_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _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>) { + const iter_difference_t<_It1> _Count1 = _Last1 - _First1; + const iter_difference_t<_It2> _Count2 = _Last2 - _First2; + if (_Count2 > _Count1) { + return false; + } + + const auto _Result = _Mismatch_n(_STD move(_First1), _STD move(_First2), + static_cast>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Result.in2 == _Last2; + } else { + const auto _Result = _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 + requires indirectly_comparable, 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; + } + + const auto _Result = _Mismatch_n(_RANGES begin(_Range1), _RANGES begin(_Range2), + static_cast>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), + _Pass_fn(_Proj2)); + return _Result.in2 == _RANGES end(_Range2); + } else { + const auto _Result = _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 + class _Ends_with_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _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); + } + + iter_difference_t<_It2> _Count2; + if constexpr (sized_sentinel_for<_Se2, _It2>) { + _Count2 = _Last2 - _First2; + } else { + _Count2 = _RANGES distance(_First2, _Last2); + } + + if (_Count2 > _Count1) { + return false; + } + + _RANGES advance(_First1, (_Count1 - _Count2)); + const auto _Result = _Mismatch_n(_STD move(_First1), _STD move(_First2), + static_cast>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Result.in2 == _Last2; + } + + // clang-format off + template + requires (forward_range<_Rng1> || sized_range<_Rng1>) && (forward_range<_Rng2> || sized_range<_Rng2>) + && indirectly_comparable, 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; + } + + auto _First1 = _RANGES begin(_Range1); + _RANGES advance(_First1, (_Count1 - _Count2)); + const auto _Result = _Mismatch_n(_STD move(_First1), _RANGES begin(_Range2), + static_cast>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Result.in2 == _RANGES end(_Range2); + } + }; + + inline constexpr _Ends_with_fn ends_with{_Not_quite_object::_Construct_tag{}}; +#endif // _HAS_CXX23 } // namespace ranges #endif // __cpp_lib_concepts diff --git a/stl/inc/xutility b/stl/inc/xutility index 68fd05a7463..987f72d4852 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -4933,44 +4933,43 @@ namespace ranges { using mismatch_result = in_in_result<_In1, _In2>; // VARIABLE ranges::mismatch - class _Mismatch_fn : private _Not_quite_object { - private: - template - _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; - } + template + _NODISCARD 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; } - - _Seek_wrapped(_First1, _STD move(_UFirst1)); - _Seek_wrapped(_First2, _STD move(_UFirst2)); - return {_STD move(_First1), _STD move(_First2)}; } - template - _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; - } - } + template + _NODISCARD 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; + } } + _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; diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 3b2c62061be..8bc883f3bf6 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -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. diff --git a/tests/std/test.lst b/tests/std/test.lst index cba3615d387..76b7297f85c 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -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 diff --git a/tests/std/tests/P1659R3_ranges_alg_ends_with/env.lst b/tests/std/tests/P1659R3_ranges_alg_ends_with/env.lst new file mode 100644 index 00000000000..18e2d7c71ec --- /dev/null +++ b/tests/std/tests/P1659R3_ranges_alg_ends_with/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_latest_matrix.lst diff --git a/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp b/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp new file mode 100644 index 00000000000..a518468fdb0 --- /dev/null +++ b/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp @@ -0,0 +1,99 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +using namespace std; + +#include + +constexpr void smoke_test() { + using ranges::dangling, ranges::equal_to, ranges::iterator_t, ranges::ends_with; + + constexpr array, 3> haystack = {{{0, 42}, {2, 42}, {4, 42}}}; + constexpr array, 1> short_haystack = {{{4, 42}}}; + constexpr array, 2> needle = {{{13, 2}, {13, 4}}}; + constexpr array, 2> wrong_needle = {{{13, 2}, {13, 3}}}; + + { // Validate sized ranges + const same_as auto match = ends_with(haystack, needle, equal_to{}, get_first, get_second); + assert(match); + + const same_as auto no_match = ends_with(haystack, wrong_needle, equal_to{}, get_first, get_second); + assert(!no_match); + + const same_as auto no_match2 = ends_with(short_haystack, needle, equal_to{}, get_first, get_second); + assert(!no_match2); + } + { // Validate sized iterator + sentinel pairs + const same_as auto match = ends_with( + haystack.begin(), haystack.end(), needle.begin(), needle.end(), equal_to{}, get_first, get_second); + assert(match); + + const same_as auto no_match = ends_with(haystack.begin(), haystack.end(), wrong_needle.begin(), + wrong_needle.end(), equal_to{}, get_first, get_second); + assert(!no_match); + + const same_as auto no_match2 = ends_with(short_haystack.begin(), short_haystack.end(), needle.begin(), + needle.end(), equal_to{}, get_first, get_second); + assert(!no_match2); + } +} + +int main() { + STATIC_ASSERT((smoke_test(), true)); + smoke_test(); +} + +#ifndef _PREFAST_ // TRANSITION, GH-1030 +struct instantiator { + template + static void call(In1&& in1 = {}, In2&& in2 = {}) { + using ranges::begin, ranges::end, ranges::ends_with, ranges::iterator_t, ranges::forward_range, + ranges::sized_range, ranges::sentinel_t; + + if constexpr ((forward_range || sized_range) &&(forward_range || sized_range) ) { + if constexpr (!is_permissive) { + (void) ends_with(in1, in2); + } + + BinaryPredicateFor, iterator_t> pred{}; + (void) ends_with(in1, in2, pred); + + HalfProjectedBinaryPredicateFor> halfpred{}; + ProjectionFor> halfproj{}; + (void) ends_with(in1, in2, halfpred, halfproj); + + ProjectedBinaryPredicate<0, 1> projpred{}; + ProjectionFor, 0> proj1{}; + ProjectionFor, 1> proj2{}; + (void) ends_with(in1, in2, projpred, proj1, proj2); + } + + if constexpr ((forward_iterator> || sized_sentinel_for, iterator_t>) &&( + forward_iterator> || sized_sentinel_for, iterator_t>) ) { + if constexpr (!is_permissive) { + (void) ends_with(begin(in1), end(in1), begin(in2), end(in2)); + } + + BinaryPredicateFor, iterator_t> pred{}; + (void) ends_with(begin(in1), end(in1), begin(in2), end(in2), pred); + + HalfProjectedBinaryPredicateFor> halfpred{}; + ProjectionFor> halfproj{}; + (void) ends_with(begin(in1), end(in1), begin(in2), end(in2), halfpred, halfproj); + + ProjectedBinaryPredicate<0, 1> projpred{}; + ProjectionFor, 0> proj1{}; + ProjectionFor, 1> proj2{}; + (void) ends_with(begin(in1), end(in1), begin(in2), end(in2), projpred, proj1, proj2); + } + } +}; + +template void test_in_in(); +#endif // TRANSITION, GH-1030 diff --git a/tests/std/tests/P1659R3_ranges_alg_starts_with/env.lst b/tests/std/tests/P1659R3_ranges_alg_starts_with/env.lst new file mode 100644 index 00000000000..18e2d7c71ec --- /dev/null +++ b/tests/std/tests/P1659R3_ranges_alg_starts_with/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_latest_matrix.lst diff --git a/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp b/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp new file mode 100644 index 00000000000..5703204d361 --- /dev/null +++ b/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp @@ -0,0 +1,113 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +using namespace std; + +#include + +constexpr void smoke_test() { + using ranges::dangling, ranges::equal_to, ranges::iterator_t, ranges::starts_with; + + constexpr array, 3> haystack = {{{0, 42}, {2, 42}, {4, 42}}}; + constexpr array, 1> short_haystack = {{{0, 42}}}; + constexpr array, 2> needle = {{{13, 0}, {13, 2}}}; + constexpr array, 2> wrong_needle = {{{13, 0}, {13, 3}}}; + + { // Validate sized ranges + const same_as auto match = starts_with(haystack, needle, equal_to{}, get_first, get_second); + assert(match); + + const same_as auto no_match = starts_with(haystack, wrong_needle, equal_to{}, get_first, get_second); + assert(!no_match); + + const same_as auto no_match2 = starts_with(short_haystack, needle, equal_to{}, get_first, get_second); + assert(!no_match2); + } + { // Validate non-sized ranges + const same_as auto match = starts_with( + basic_borrowed_range{haystack}, basic_borrowed_range{needle}, equal_to{}, get_first, get_second); + assert(match); + + const same_as auto no_match = starts_with( + basic_borrowed_range{haystack}, basic_borrowed_range{wrong_needle}, equal_to{}, get_first, get_second); + assert(!no_match); + + const same_as auto no_match2 = starts_with( + basic_borrowed_range{short_haystack}, basic_borrowed_range{needle}, equal_to{}, get_first, get_second); + assert(!no_match2); + } + { // Validate sized iterator + sentinel pairs + const same_as auto match = starts_with( + haystack.begin(), haystack.end(), needle.begin(), needle.end(), equal_to{}, get_first, get_second); + assert(match); + + const same_as auto no_match = starts_with(haystack.begin(), haystack.end(), wrong_needle.begin(), + wrong_needle.end(), equal_to{}, get_first, get_second); + assert(!no_match); + + const same_as auto no_match2 = starts_with(short_haystack.begin(), short_haystack.end(), needle.begin(), + needle.end(), equal_to{}, get_first, get_second); + assert(!no_match2); + } + { // Validate non-sized iterator + sentinel pairs + basic_borrowed_range wrapped_haystack{haystack}; + basic_borrowed_range wrapped_needle{needle}; + const same_as auto match = starts_with(wrapped_haystack.begin(), wrapped_haystack.end(), + wrapped_needle.begin(), wrapped_needle.end(), equal_to{}, get_first, get_second); + assert(match); + + basic_borrowed_range wrapped_haystack2{haystack}; + basic_borrowed_range wrapped_wrong_needle{wrong_needle}; + const same_as auto no_match = starts_with(wrapped_haystack2.begin(), wrapped_haystack2.end(), + wrapped_wrong_needle.begin(), wrapped_wrong_needle.end(), equal_to{}, get_first, get_second); + assert(!no_match); + + basic_borrowed_range wrapped_short_haystack{short_haystack}; + basic_borrowed_range wrapped_needle2{needle}; + const same_as auto no_match2 = starts_with(wrapped_short_haystack.begin(), wrapped_short_haystack.end(), + wrapped_needle2.begin(), wrapped_needle2.end(), equal_to{}, get_first, get_second); + assert(!no_match2); + } +} + +int main() { + STATIC_ASSERT((smoke_test(), true)); + smoke_test(); +} + +#ifndef _PREFAST_ // TRANSITION, GH-1030 +struct instantiator { + template + static void call(In1&& in1 = {}, In2&& in2 = {}) { + using ranges::begin, ranges::end, ranges::starts_with, ranges::iterator_t; + + if constexpr (!is_permissive) { + (void) starts_with(in1, in2); + (void) starts_with(begin(in1), end(in1), begin(in2), end(in2)); + } + + BinaryPredicateFor, iterator_t> pred{}; + (void) starts_with(in1, in2, pred); + (void) starts_with(begin(in1), end(in1), begin(in2), end(in2), pred); + + HalfProjectedBinaryPredicateFor> halfpred{}; + ProjectionFor> halfproj{}; + (void) starts_with(in1, in2, halfpred, halfproj); + (void) starts_with(begin(in1), end(in1), begin(in2), end(in2), halfpred, halfproj); + + ProjectedBinaryPredicate<0, 1> projpred{}; + ProjectionFor, 0> proj1{}; + ProjectionFor, 1> proj2{}; + (void) starts_with(in1, in2, projpred, proj1, proj2); + (void) starts_with(begin(in1), end(in1), begin(in2), end(in2), projpred, proj1, proj2); + } +}; + +template void test_in_in(); +#endif // TRANSITION, GH-1030 From a4d81a23a67a3c416c3c73b11f374a99c30abc97 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 11 Jun 2021 08:31:34 +0200 Subject: [PATCH 02/19] Address review feedback --- stl/inc/algorithm | 52 +++++++++++++++++++++++------------------------ stl/inc/xutility | 34 +++++++++++++++++++------------ 2 files changed, 46 insertions(+), 40 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index c844b498006..c10f67305ff 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -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 - _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(_Count)) == 0; - } + // clang-format off + template + requires indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2> + _NODISCARD static constexpr bool _Equal_count( + _It1 _First1, _It2 _First2, iter_difference_t<_It1> _Count, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + // clang-format on + 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(_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 _NODISCARD static constexpr bool _Equal_4( _It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { @@ -2446,11 +2448,10 @@ namespace ranges { return false; } - const auto _Result = _Mismatch_n(_STD move(_First1), _STD move(_First2), + return _RANGES _Equal_count(_STD move(_First1), _STD move(_First2), static_cast>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); - return _Result.in2 == _Last2; } else { - const auto _Result = _Mismatch_4(_STD move(_First1), _STD move(_Last1), _STD move(_First2), + 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; } @@ -2470,13 +2471,12 @@ namespace ranges { return false; } - const auto _Result = _Mismatch_n(_RANGES begin(_Range1), _RANGES begin(_Range2), + return _RANGES _Equal_count(_RANGES begin(_Range1), _RANGES begin(_Range2), static_cast>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); - return _Result.in2 == _RANGES end(_Range2); } else { - const auto _Result = _Mismatch_4(_RANGES begin(_Range1), _RANGES end(_Range1), _RANGES begin(_Range2), - _RANGES end(_Range2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + 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); } } @@ -2520,9 +2520,8 @@ namespace ranges { } _RANGES advance(_First1, (_Count1 - _Count2)); - const auto _Result = _Mismatch_n(_STD move(_First1), _STD move(_First2), + return _RANGES _Equal_count(_STD move(_First1), _STD move(_First2), static_cast>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); - return _Result.in2 == _Last2; } // clang-format off @@ -2541,9 +2540,8 @@ namespace ranges { auto _First1 = _RANGES begin(_Range1); _RANGES advance(_First1, (_Count1 - _Count2)); - const auto _Result = _Mismatch_n(_STD move(_First1), _RANGES begin(_Range2), + return _RANGES _Equal_count(_STD move(_First1), _RANGES begin(_Range2), static_cast>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); - return _Result.in2 == _RANGES end(_Range2); } }; diff --git a/stl/inc/xutility b/stl/inc/xutility index 987f72d4852..7a221f47745 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -4933,9 +4933,12 @@ namespace ranges { using mismatch_result = in_in_result<_In1, _In2>; // VARIABLE ranges::mismatch - template + // clang-format off + template + 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 auto _UFirst1 = _Get_unwrapped(_STD move(_First1)); auto _UFirst2 = _Get_unwrapped(_STD move(_First2)); @@ -4950,9 +4953,13 @@ namespace ranges { return {_STD move(_First1), _STD move(_First2)}; } - template + // clang-format off + template _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)); @@ -4979,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(_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 requires indirectly_comparable, iterator_t<_Rng2>, _Pr, _Pj1, _Pj2> _NODISCARD constexpr mismatch_result, 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>(_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{}}; From 08ac0ed202f9e6c12a8a4c4648e663190e496e41 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 11 Jun 2021 13:55:24 +0200 Subject: [PATCH 03/19] Accept any integral for _Equal_count --- stl/inc/algorithm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index c10f67305ff..4daec0d9de6 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -715,10 +715,10 @@ _NODISCARD pair<_FwdIt1, _FwdIt2> mismatch( namespace ranges { // VARIABLE ranges::equal // clang-format off - template + template requires indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2> _NODISCARD static constexpr bool _Equal_count( - _It1 _First1, _It2 _First2, iter_difference_t<_It1> _Count, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _It1 _First1, _It2 _First2, _Size _Count, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { // clang-format on if constexpr (_Equal_memcmp_is_safe<_It1, _It2, _Pr> && same_as<_Pj1, identity> && same_as<_Pj2, identity>) { if (!_STD is_constant_evaluated()) { From 75b4380b50d2cd63fb56f6b1a34f82be3c7f6386 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Sat, 12 Jun 2021 11:25:56 +0200 Subject: [PATCH 04/19] Add feature test macro --- stl/inc/yvals_core.h | 6 ++++++ .../test.compile.pass.cpp | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index d4f611ea8d8..1510fe36303 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -1307,6 +1307,12 @@ #define __cpp_lib_unwrap_ref 201811L #endif // _HAS_CXX20 +#if _HAS_CXX23 +#ifdef __cpp_lib_concepts // TRANSITION, GH-395 +#define __cpp_lib_ranges_starts_ends_with 202106L +#endif // __cpp_lib_concepts +#endif // _HAS_CX23 + #ifndef _M_CEE #if _HAS_CXX20 #define __cpp_lib_execution 201902L // P1001R2 execution::unseq diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 0da859a6bf9..1d709008067 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -1235,6 +1235,20 @@ STATIC_ASSERT(__cpp_lib_ranges == 201911L); #endif #endif +#if _HAS_CXX23 && !defined(__EDG__) // TRANSITION, EDG concepts support and GH-1814 +#ifndef __cpp_lib_ranges_starts_ends_with +#error __cpp_lib_ranges_starts_ends_with is not defined +#elif __cpp_lib_ranges_starts_ends_with != 202106L +#error __cpp_lib_ranges_starts_ends_with is not 202106L +#else +STATIC_ASSERT(__cpp_lib_ranges_starts_ends_with == 202106L); +#endif +#else +#ifdef __cpp_lib_ranges_starts_ends_with +#error __cpp_lib_ranges_starts_ends_with is defined +#endif +#endif + #if _HAS_CXX17 #ifndef __cpp_lib_raw_memory_algorithms #error __cpp_lib_raw_memory_algorithms is not defined From 39bd70dd6e5b8666f8b3dbd7e55c7804127e4d94 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Tue, 22 Jun 2021 21:58:30 +0200 Subject: [PATCH 05/19] Apply optimizations for infinite ranges Co-authored-by: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> --- stl/inc/algorithm | 82 +++++++++++-------- stl/inc/xutility | 6 +- .../P1659R3_ranges_alg_ends_with/test.cpp | 16 ++++ .../P1659R3_ranges_alg_starts_with/test.cpp | 21 +++++ 4 files changed, 91 insertions(+), 34 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index d08feb7e2b5..3bea17976e2 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -2441,11 +2441,14 @@ namespace ranges { _Adl_verify_range(_First1, _Last1); _Adl_verify_range(_First2, _Last2); - if constexpr (sized_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2>) { - const iter_difference_t<_It1> _Count1 = _Last1 - _First1; + if constexpr (same_as<_Se2, unreachable_sentinel_t>) { + return false; + } else if constexpr (_Sized_or_unreachable_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2>) { const iter_difference_t<_It2> _Count2 = _Last2 - _First2; - if (_Count2 > _Count1) { - return false; + if constexpr (sized_sentinel_for<_Se1, _It1>) { + if (_Count2 > _Last1 - _First1) { + return false; + } } return _RANGES _Equal_count(_STD move(_First1), _STD move(_First2), @@ -2464,11 +2467,14 @@ namespace ranges { _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); + if constexpr (same_as, unreachable_sentinel_t>) { + return false; + } else if constexpr (_Sized_or_infinite_range<_Rng1> && sized_range<_Rng2>) { const range_difference_t<_Rng2> _Count2 = _RANGES distance(_Range2); - if (_Count2 > _Count1) { - return false; + if constexpr (sized_range<_Rng1>) { + if (_Count2 > _RANGES distance(_Range1)) { + return false; + } } return _RANGES _Equal_count(_RANGES begin(_Range1), _RANGES begin(_Range2), @@ -2501,27 +2507,31 @@ namespace ranges { _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; + if constexpr (same_as<_Se1, unreachable_sentinel_t> || same_as<_Se2, unreachable_sentinel_t>) { + return false; } else { - _Count1 = _RANGES distance(_First1, _Last1); - } + iter_difference_t<_It1> _Count1; + if constexpr (sized_sentinel_for<_Se1, _It1>) { + _Count1 = _Last1 - _First1; + } else { + _Count1 = _RANGES distance(_First1, _Last1); + } - iter_difference_t<_It2> _Count2; - if constexpr (sized_sentinel_for<_Se2, _It2>) { - _Count2 = _Last2 - _First2; - } else { - _Count2 = _RANGES distance(_First2, _Last2); - } + iter_difference_t<_It2> _Count2; + if constexpr (sized_sentinel_for<_Se2, _It2>) { + _Count2 = _Last2 - _First2; + } else { + _Count2 = _RANGES distance(_First2, _Last2); + } - if (_Count2 > _Count1) { - return false; - } + if (_Count2 > _Count1) { + return false; + } - _RANGES advance(_First1, (_Count1 - _Count2)); - return _RANGES _Equal_count(_STD move(_First1), _STD move(_First2), - static_cast>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + _RANGES advance(_First1, (_Count1 - _Count2)); + return _RANGES _Equal_count(_STD move(_First1), _STD move(_First2), + static_cast>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } } // clang-format off @@ -2532,16 +2542,22 @@ namespace ranges { _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) { + if constexpr (same_as, unreachable_sentinel_t> // + || same_as, unreachable_sentinel_t>) { return false; - } + } else { + const range_difference_t<_Rng1> _Count1 = _RANGES distance(_Range1); + const range_difference_t<_Rng2> _Count2 = _RANGES distance(_Range2); + if (_Count2 > _Count1) { + return false; + } - auto _First1 = _RANGES begin(_Range1); - _RANGES advance(_First1, (_Count1 - _Count2)); - return _RANGES _Equal_count(_STD move(_First1), _RANGES begin(_Range2), - static_cast>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + auto _First1 = _RANGES begin(_Range1); + _RANGES advance(_First1, (_Count1 - _Count2)); + return _RANGES _Equal_count(_STD move(_First1), _RANGES begin(_Range2), + static_cast>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), + _Pass_fn(_Proj2)); + } } }; diff --git a/stl/inc/xutility b/stl/inc/xutility index 1386b779da1..8d4ea601563 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -5368,10 +5368,14 @@ _NODISCARD _FwdIt find(_ExPo&& _Exec, _FwdIt _First, const _FwdIt _Last, const _ #ifdef __cpp_lib_concepts namespace ranges { // VARIABLE ranges::find - // clang-format off template concept _Sized_or_unreachable_sentinel_for = sized_sentinel_for<_Se, _It> || same_as<_Se, unreachable_sentinel_t>; + template + concept _Sized_or_infinite_range = range<_Rng> // + && (sized_range<_Rng> || same_as, unreachable_sentinel_t>); + + // clang-format off // concept-constrained for strict enforcement as it is used by several algorithms template _Se, class _Ty, class _Pj = identity> requires indirect_binary_predicate, const _Ty*> diff --git a/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp b/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp index a518468fdb0..a5fb40e8cf7 100644 --- a/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp +++ b/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp @@ -29,6 +29,13 @@ constexpr void smoke_test() { const same_as auto no_match2 = ends_with(short_haystack, needle, equal_to{}, get_first, get_second); assert(!no_match2); } + { // Validate infinite ranges + const same_as auto infinite_haystack = ends_with(views::iota(0), views::iota(0, 5)); + assert(!infinite_haystack); + + const same_as auto infinite_needle = ends_with(views::iota(0, 5), views::iota(0)); + assert(!infinite_needle); + } { // Validate sized iterator + sentinel pairs const same_as auto match = ends_with( haystack.begin(), haystack.end(), needle.begin(), needle.end(), equal_to{}, get_first, get_second); @@ -42,6 +49,15 @@ constexpr void smoke_test() { needle.end(), equal_to{}, get_first, get_second); assert(!no_match2); } + { // Validate unreachable sentinel + const same_as auto unreachable_haystack = ends_with( + haystack.begin(), unreachable_sentinel, needle.begin(), needle.end(), equal_to{}, get_first, get_second); + assert(!unreachable_haystack); + + const same_as auto unreachable_needle = ends_with( + haystack.begin(), haystack.end(), needle.begin(), unreachable_sentinel, equal_to{}, get_first, get_second); + assert(!unreachable_needle); + } } int main() { diff --git a/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp b/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp index 5703204d361..9039f3e8d93 100644 --- a/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp +++ b/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp @@ -42,6 +42,13 @@ constexpr void smoke_test() { basic_borrowed_range{short_haystack}, basic_borrowed_range{needle}, equal_to{}, get_first, get_second); assert(!no_match2); } + { // Validate infinite ranges + const same_as auto infinite_haystack = starts_with(views::iota(0), views::iota(0, 5)); + assert(infinite_haystack); + + const same_as auto infinite_needle = starts_with(views::iota(0, 5), views::iota(0)); + assert(!infinite_needle); + } { // Validate sized iterator + sentinel pairs const same_as auto match = starts_with( haystack.begin(), haystack.end(), needle.begin(), needle.end(), equal_to{}, get_first, get_second); @@ -74,6 +81,20 @@ constexpr void smoke_test() { wrapped_needle2.begin(), wrapped_needle2.end(), equal_to{}, get_first, get_second); assert(!no_match2); } + { // Validate unreachable sentinels + basic_borrowed_range wrapped_haystack{haystack}; + basic_borrowed_range wrapped_needle{needle}; + const same_as auto unreachable_haystack = starts_with(wrapped_haystack.begin(), unreachable_sentinel, + wrapped_needle.begin(), wrapped_needle.end(), equal_to{}, get_first, get_second); + assert(unreachable_haystack); + + basic_borrowed_range wrapped_short_haystack{short_haystack}; + basic_borrowed_range wrapped_needle2{needle}; + const same_as auto unreachable_needle = + starts_with(wrapped_short_haystack.begin(), wrapped_short_haystack.end(), wrapped_needle2.begin(), + unreachable_sentinel, equal_to{}, get_first, get_second); + assert(!unreachable_needle); + } } int main() { From 3817faf8ab3e0247a653e59edc44ba242fce14fb Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Thu, 1 Jul 2021 12:00:56 +0200 Subject: [PATCH 06/19] Fix failing tests --- tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp | 6 +++++- tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp b/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp index a5fb40e8cf7..e56c8133c23 100644 --- a/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp +++ b/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include using namespace std; @@ -68,10 +69,13 @@ int main() { #ifndef _PREFAST_ // TRANSITION, GH-1030 struct instantiator { template - static void call(In1&& in1 = {}, In2&& in2 = {}) { + static void call() { using ranges::begin, ranges::end, ranges::ends_with, ranges::iterator_t, ranges::forward_range, ranges::sized_range, ranges::sentinel_t; + In1 in1{span{}}; + In2 in2{span{}}; + if constexpr ((forward_range || sized_range) &&(forward_range || sized_range) ) { if constexpr (!is_permissive) { (void) ends_with(in1, in2); diff --git a/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp b/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp index 9039f3e8d93..1a93abc9d5a 100644 --- a/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp +++ b/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include using namespace std; @@ -105,9 +106,12 @@ int main() { #ifndef _PREFAST_ // TRANSITION, GH-1030 struct instantiator { template - static void call(In1&& in1 = {}, In2&& in2 = {}) { + static void call() { using ranges::begin, ranges::end, ranges::starts_with, ranges::iterator_t; + In1 in1{span{}}; + In2 in2{span{}}; + if constexpr (!is_permissive) { (void) starts_with(in1, in2); (void) starts_with(begin(in1), end(in1), begin(in2), end(in2)); From e2ac55688b5dc88cb6b4a96290d34b5f5469b256 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Thu, 1 Jul 2021 12:58:53 +0200 Subject: [PATCH 07/19] Rework so that we pass unwrapped iterators around --- stl/inc/algorithm | 30 ++++++++++++++++------------ stl/inc/xutility | 50 ++++++++++++++++++++++++++--------------------- 2 files changed, 45 insertions(+), 35 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 3bea17976e2..ced0fc0fea8 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -2451,12 +2451,14 @@ namespace ranges { } } - return _RANGES _Equal_count(_STD move(_First1), _STD move(_First2), + return _RANGES _Equal_count(_Get_unwrapped(_STD move(_First1)), _Get_unwrapped(_STD move(_First2)), static_cast>(_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; + const auto _ULast2 = _Get_unwrapped(_STD move(_Last2)); + const auto _Result = _RANGES _Mismatch_4(_Get_unwrapped(_STD move(_First1)), + _Get_unwrapped(_STD move(_Last1)), _Get_unwrapped(_STD move(_First2)), _ULast2, _Pass_fn(_Pred), + _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Result.in2 == _ULast2; } } @@ -2477,13 +2479,14 @@ namespace ranges { } } - return _RANGES _Equal_count(_RANGES begin(_Range1), _RANGES begin(_Range2), + return _RANGES _Equal_count(_Ubegin(_Range1), _Ubegin(_Range2), static_cast>(_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); + const auto _ULast2 = _Uend(_Range2); + const auto _Result = _RANGES _Mismatch_4(_Ubegin(_Range1), _Uend(_Range1), _Ubegin(_Range2), _ULast2, + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Result.in2 == _ULast2; } } }; @@ -2528,8 +2531,9 @@ namespace ranges { return false; } - _RANGES advance(_First1, (_Count1 - _Count2)); - return _RANGES _Equal_count(_STD move(_First1), _STD move(_First2), + auto _UFirst1 = _Get_unwrapped(_STD move(_First1)); + _RANGES advance(_UFirst1, (_Count1 - _Count2)); + return _RANGES _Equal_count(_STD move(_UFirst1), _Get_unwrapped(_STD move(_First2)), static_cast>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); } } @@ -2552,9 +2556,9 @@ namespace ranges { return false; } - auto _First1 = _RANGES begin(_Range1); - _RANGES advance(_First1, (_Count1 - _Count2)); - return _RANGES _Equal_count(_STD move(_First1), _RANGES begin(_Range2), + auto _UFirst1 = _Ubegin(_Range1); + _RANGES advance(_UFirst1, (_Count1 - _Count2)); + return _RANGES _Equal_count(_STD move(_UFirst1), _Ubegin(_Range2), static_cast>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); } diff --git a/stl/inc/xutility b/stl/inc/xutility index 5df662cb45f..f880baf29c1 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -4943,17 +4943,13 @@ namespace ranges { _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 - 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))) { + 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)}; } @@ -4964,19 +4960,13 @@ namespace ranges { _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)); - for (; _UFirst1 != _ULast1 && _UFirst2 != _ULast2; ++_UFirst1, (void) ++_UFirst2) { - if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_UFirst1), _STD invoke(_Proj2, *_UFirst2))) { + for (; _First1 != _Last1 && _First2 != _Last2; ++_First1, (void) ++_First2) { + 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)}; } @@ -5001,11 +4991,18 @@ namespace ranges { _Count1 = static_cast(_Count2); } - return _RANGES _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 _RANGES _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)}; } } @@ -5016,6 +5013,8 @@ namespace ranges { _NODISCARD constexpr mismatch_result, borrowed_iterator_t<_Rng2>> operator()( _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { // clang-format on + auto _First1 = _RANGES begin(_Range1); + auto _First2 = _RANGES begin(_Range2); if constexpr (sized_range<_Rng1> && sized_range<_Rng2>) { range_difference_t<_Rng1> _Count1 = _RANGES distance(_Range1); const range_difference_t<_Rng2> _Count2 = _RANGES distance(_Range2); @@ -5023,11 +5022,18 @@ namespace ranges { _Count1 = static_cast>(_Count2); } - return _RANGES _Mismatch_n(_RANGES begin(_Range1), _RANGES begin(_Range2), _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 _RANGES _Mismatch_4(_RANGES begin(_Range1), _RANGES end(_Range1), _RANGES begin(_Range2), - _RANGES end(_Range2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + 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)}; } } }; From 063f583f9ca1b20ac5b33ccccda4f51f9e2450d4 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Wed, 4 Aug 2021 15:14:44 +0200 Subject: [PATCH 08/19] Fix formatting --- stl/inc/xutility | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 2f10f844179..181ff773eb6 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -5381,11 +5381,11 @@ namespace ranges { template concept _Sized_or_unreachable_sentinel_for = sized_sentinel_for<_Se, _It> || same_as<_Se, unreachable_sentinel_t>; + // clang-format off template - concept _Sized_or_infinite_range = range<_Rng> // + concept _Sized_or_infinite_range = range<_Rng> && (sized_range<_Rng> || same_as, unreachable_sentinel_t>); - // clang-format off // concept-constrained for strict enforcement as it is used by several algorithms template _Se, class _Ty, class _Pj = identity> requires indirect_binary_predicate, const _Ty*> From 37a18baeb96c01b45612e39a135449fa5a519b91 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 30 Aug 2021 18:26:56 -0700 Subject: [PATCH 09/19] Sort `tests/std/test.lst`. --- tests/std/test.lst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/test.lst b/tests/std/test.lst index d647fe34c19..b0b2b0b8b40 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -429,9 +429,9 @@ tests\P1425R4_queue_stack_constructors 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\P1645R1_constexpr_numeric tests\P1682R3_to_underlying tests\P1951R1_default_arguments_pair_forward_ctor tests\P2136R3_invoke_r From 5849ddbc13ad47fbb49920c1bec43f1b903afc14 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Thu, 16 Sep 2021 21:16:04 +0200 Subject: [PATCH 10/19] Address review comments --- stl/inc/algorithm | 61 +++++++++++++++++++++++++++----------------- stl/inc/xutility | 9 ++++--- stl/inc/yvals_core.h | 15 ++++------- 3 files changed, 48 insertions(+), 37 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index e4c4a879d94..95bee2b1eda 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -696,6 +696,7 @@ namespace ranges { _NODISCARD static constexpr bool _Equal_count( _It1 _First1, _It2 _First2, _Size _Count, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { // clang-format on + _STL_INTERNAL_CHECK(_Count >= 0); 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(_Count)) == 0; @@ -2218,7 +2219,6 @@ namespace ranges { inline constexpr _Search_n_fn search_n{_Not_quite_object::_Construct_tag{}}; #if _HAS_CXX23 - // VARIABLE ranges::starts_with class _Starts_with_fn : private _Not_quite_object { public: using _Not_quite_object::_Not_quite_object; @@ -2235,21 +2235,25 @@ namespace ranges { if constexpr (same_as<_Se2, unreachable_sentinel_t>) { return false; - } else if constexpr (_Sized_or_unreachable_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2>) { - const iter_difference_t<_It2> _Count2 = _Last2 - _First2; + } + + auto _UFirst1 = _Get_unwrapped(_STD move(_First1)); + auto _ULast1 = _Get_unwrapped(_STD move(_Last1)); + auto _UFirst2 = _Get_unwrapped(_STD move(_First2)); + const auto _ULast2 = _Get_unwrapped(_STD move(_Last2)); + if constexpr (_Sized_or_unreachable_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2>) { + const iter_difference_t<_It2> _Count2 = _ULast2 - _UFirst2; if constexpr (sized_sentinel_for<_Se1, _It1>) { - if (_Count2 > _Last1 - _First1) { + if (_Count2 > _ULast1 - _UFirst1) { return false; } } - return _RANGES _Equal_count(_Get_unwrapped(_STD move(_First1)), _Get_unwrapped(_STD move(_First2)), + return _RANGES _Equal_count(_STD move(_UFirst1), _STD move(_UFirst2), static_cast>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); } else { - const auto _ULast2 = _Get_unwrapped(_STD move(_Last2)); - const auto _Result = _RANGES _Mismatch_4(_Get_unwrapped(_STD move(_First1)), - _Get_unwrapped(_STD move(_Last1)), _Get_unwrapped(_STD move(_First2)), _ULast2, _Pass_fn(_Pred), - _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + const auto _Result = _RANGES _Mismatch_4(_STD move(_UFirst1), _STD move(_ULast1), _STD move(_UFirst2), + _ULast2, _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); return _Result.in2 == _ULast2; } } @@ -2263,7 +2267,9 @@ namespace ranges { // clang-format on if constexpr (same_as, unreachable_sentinel_t>) { return false; - } else if constexpr (_Sized_or_infinite_range<_Rng1> && sized_range<_Rng2>) { + } + + if constexpr (_Sized_or_infinite_range<_Rng1> && sized_range<_Rng2>) { const range_difference_t<_Rng2> _Count2 = _RANGES distance(_Range2); if constexpr (sized_range<_Rng1>) { if (_Count2 > _RANGES distance(_Range1)) { @@ -2285,7 +2291,6 @@ namespace ranges { inline constexpr _Starts_with_fn starts_with{_Not_quite_object::_Construct_tag{}}; - // VARIABLE ranges::ends_with class _Ends_with_fn : private _Not_quite_object { public: using _Not_quite_object::_Not_quite_object; @@ -2302,30 +2307,36 @@ namespace ranges { _Adl_verify_range(_First1, _Last1); _Adl_verify_range(_First2, _Last2); - if constexpr (same_as<_Se1, unreachable_sentinel_t> || same_as<_Se2, unreachable_sentinel_t>) { + auto _UFirst1 = _Get_unwrapped(_STD move(_First1)); + auto _ULast1 = _Get_unwrapped(_STD move(_Last1)); + auto _UFirst2 = _Get_unwrapped(_STD move(_First2)); + auto _ULast2 = _Get_unwrapped(_STD move(_Last2)); + if constexpr (same_as<_Se1, unreachable_sentinel_t> && same_as<_Se2, unreachable_sentinel_t>) { + return _Equal_4(_STD move(_UFirst1), _STD move(_ULast1), _STD move(_UFirst2), _STD move(_ULast2), + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } else if constexpr (same_as<_Se1, unreachable_sentinel_t> || same_as<_Se2, unreachable_sentinel_t>) { return false; } else { iter_difference_t<_It1> _Count1; - if constexpr (sized_sentinel_for<_Se1, _It1>) { - _Count1 = _Last1 - _First1; + if constexpr (sized_sentinel_for<_Se1, _It1>) { // TRANSITION, LWG-3392 + _Count1 = _ULast1 - _UFirst1; } else { - _Count1 = _RANGES distance(_First1, _Last1); + _Count1 = _RANGES distance(_UFirst1, _ULast1); } iter_difference_t<_It2> _Count2; - if constexpr (sized_sentinel_for<_Se2, _It2>) { - _Count2 = _Last2 - _First2; + if constexpr (sized_sentinel_for<_Se2, _It2>) { // TRANSITION, LWG-3392 + _Count2 = _ULast2 - _UFirst2; } else { - _Count2 = _RANGES distance(_First2, _Last2); + _Count2 = _RANGES distance(_UFirst2, _ULast2); } if (_Count2 > _Count1) { return false; } - auto _UFirst1 = _Get_unwrapped(_STD move(_First1)); - _RANGES advance(_UFirst1, (_Count1 - _Count2)); - return _RANGES _Equal_count(_STD move(_UFirst1), _Get_unwrapped(_STD move(_First2)), + _RANGES advance(_UFirst1, static_cast>(_Count1 - _Count2)); + return _RANGES _Equal_count(_STD move(_UFirst1), _STD move(_UFirst2), static_cast>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); } } @@ -2339,7 +2350,11 @@ namespace ranges { _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { // clang-format on if constexpr (same_as, unreachable_sentinel_t> // - || same_as, unreachable_sentinel_t>) { + && same_as, unreachable_sentinel_t>) + return _Equal_4(_Ubegin(_Range1), _Uend(_Range1), _Ubegin(_Range2), _Uend(_Range2), _Pass_fn(_Pred), + _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + else if constexpr (same_as, unreachable_sentinel_t> // + || same_as, unreachable_sentinel_t>) { return false; } else { const range_difference_t<_Rng1> _Count1 = _RANGES distance(_Range1); @@ -2349,7 +2364,7 @@ namespace ranges { } auto _UFirst1 = _Ubegin(_Range1); - _RANGES advance(_UFirst1, (_Count1 - _Count2)); + _RANGES advance(_UFirst1, static_cast>(_Count1 - _Count2)); return _RANGES _Equal_count(_STD move(_UFirst1), _Ubegin(_Range2), static_cast>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); diff --git a/stl/inc/xutility b/stl/inc/xutility index 7b02a6bf3d0..b3061a1bd40 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -4736,14 +4736,13 @@ namespace ranges { template using mismatch_result = in_in_result<_In1, _In2>; - // VARIABLE ranges::mismatch // clang-format off template 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; @@ -4813,8 +4812,6 @@ namespace ranges { _NODISCARD constexpr mismatch_result, borrowed_iterator_t<_Rng2>> operator()( _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { // clang-format on - auto _First1 = _RANGES begin(_Range1); - auto _First2 = _RANGES begin(_Range2); if constexpr (sized_range<_Rng1> && sized_range<_Rng2>) { range_difference_t<_Rng1> _Count1 = _RANGES distance(_Range1); const range_difference_t<_Rng2> _Count2 = _RANGES distance(_Range2); @@ -4822,12 +4819,16 @@ namespace ranges { _Count1 = static_cast>(_Count2); } + 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 { + 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)); diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index c133fcc202f..632be8b30b3 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -1328,12 +1328,6 @@ #define __cpp_lib_unwrap_ref 201811L #endif // _HAS_CXX20 -#if _HAS_CXX23 -#ifdef __cpp_lib_concepts // TRANSITION, GH-395 -#define __cpp_lib_ranges_starts_ends_with 202106L -#endif // __cpp_lib_concepts -#endif // _HAS_CX23 - #ifndef _M_CEE #if _HAS_CXX20 #define __cpp_lib_execution 201902L // P1001R2 execution::unseq @@ -1389,10 +1383,11 @@ #define __cpp_lib_out_ptr 202106L #endif // __cpp_lib_concepts -#define __cpp_lib_spanstream 202106L -#define __cpp_lib_stdatomic_h 202011L -#define __cpp_lib_string_contains 202011L -#define __cpp_lib_to_underlying 202102L +#define __cpp_lib_ranges_starts_ends_with 202106L +#define __cpp_lib_spanstream 202106L +#define __cpp_lib_stdatomic_h 202011L +#define __cpp_lib_string_contains 202011L +#define __cpp_lib_to_underlying 202102L #endif // _HAS_CXX23 #define __cpp_lib_experimental_erase_if 201411L From 624f273dce959e8d04854651912a5993cfc2e5d9 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 17 Sep 2021 08:58:39 +0200 Subject: [PATCH 11/19] Fix incorret feature test macro placement --- stl/inc/yvals_core.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 632be8b30b3..3d25a59b501 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -1380,14 +1380,14 @@ #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_ranges_starts_ends_with 202106L -#define __cpp_lib_spanstream 202106L -#define __cpp_lib_stdatomic_h 202011L -#define __cpp_lib_string_contains 202011L -#define __cpp_lib_to_underlying 202102L +#define __cpp_lib_spanstream 202106L +#define __cpp_lib_stdatomic_h 202011L +#define __cpp_lib_string_contains 202011L +#define __cpp_lib_to_underlying 202102L #endif // _HAS_CXX23 #define __cpp_lib_experimental_erase_if 201411L From 5f22bb6a4eff31da8c544bef813aa4b4bc3d0fa0 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 17 Sep 2021 10:41:26 +0200 Subject: [PATCH 12/19] Fix infinite ranges and prepare LWG issue --- stl/inc/algorithm | 83 +++++++++---------- .../P1659R3_ranges_alg_ends_with/test.cpp | 7 -- 2 files changed, 39 insertions(+), 51 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 95bee2b1eda..c9e1bac41ea 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -2226,13 +2226,13 @@ namespace ranges { // clang-format off template _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> + requires (indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2> // Per LWG-issue unnumbered as of 17.09.2021 + && !(same_as<_Se1, unreachable_sentinel_t> && same_as<_Se2, unreachable_sentinel_t>)) _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 (same_as<_Se2, unreachable_sentinel_t>) { return false; } @@ -2261,7 +2261,9 @@ namespace ranges { // clang-format off template - requires indirectly_comparable, iterator_t<_Rng2>, _Pr, _Pj1, _Pj2> + requires (indirectly_comparable, iterator_t<_Rng2>, _Pr, _Pj1, _Pj2> + && !(same_as, unreachable_sentinel_t> + && same_as, unreachable_sentinel_t>)) // Per LWG-issue unnumbered as of 17.09.2021 _NODISCARD constexpr bool operator()( _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { // clang-format on @@ -2301,44 +2303,42 @@ namespace ranges { requires (forward_iterator<_It1> || sized_sentinel_for<_Se1, _It1>) && (forward_iterator<_It2> || sized_sentinel_for<_Se2, _It2>) && indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2> + && (!same_as<_Se1, unreachable_sentinel_t>) // Per LWG-issue unnumbered as of 17.09.2021 _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 (same_as<_Se2, unreachable_sentinel_t>) { + return false; + } auto _UFirst1 = _Get_unwrapped(_STD move(_First1)); auto _ULast1 = _Get_unwrapped(_STD move(_Last1)); auto _UFirst2 = _Get_unwrapped(_STD move(_First2)); auto _ULast2 = _Get_unwrapped(_STD move(_Last2)); - if constexpr (same_as<_Se1, unreachable_sentinel_t> && same_as<_Se2, unreachable_sentinel_t>) { - return _Equal_4(_STD move(_UFirst1), _STD move(_ULast1), _STD move(_UFirst2), _STD move(_ULast2), - _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); - } else if constexpr (same_as<_Se1, unreachable_sentinel_t> || same_as<_Se2, unreachable_sentinel_t>) { - return false; - } else { - iter_difference_t<_It1> _Count1; - if constexpr (sized_sentinel_for<_Se1, _It1>) { // TRANSITION, LWG-3392 - _Count1 = _ULast1 - _UFirst1; - } else { - _Count1 = _RANGES distance(_UFirst1, _ULast1); - } - iter_difference_t<_It2> _Count2; - if constexpr (sized_sentinel_for<_Se2, _It2>) { // TRANSITION, LWG-3392 - _Count2 = _ULast2 - _UFirst2; - } else { - _Count2 = _RANGES distance(_UFirst2, _ULast2); - } + iter_difference_t<_It1> _Count1; + if constexpr (sized_sentinel_for<_Se1, _It1>) { // TRANSITION, LWG-3392 + _Count1 = _ULast1 - _UFirst1; + } else { + _Count1 = _RANGES distance(_UFirst1, _ULast1); + } - if (_Count2 > _Count1) { - return false; - } + iter_difference_t<_It2> _Count2; + if constexpr (sized_sentinel_for<_Se2, _It2>) { // TRANSITION, LWG-3392 + _Count2 = _ULast2 - _UFirst2; + } else { + _Count2 = _RANGES distance(_UFirst2, _ULast2); + } - _RANGES advance(_UFirst1, static_cast>(_Count1 - _Count2)); - return _RANGES _Equal_count(_STD move(_UFirst1), _STD move(_UFirst2), - static_cast>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + if (_Count2 > _Count1) { + return false; } + + _RANGES advance(_UFirst1, static_cast>(_Count1 - _Count2)); + return _RANGES _Equal_count(_STD move(_UFirst1), _STD move(_UFirst2), + static_cast>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); } // clang-format off @@ -2346,29 +2346,24 @@ namespace ranges { class _Pj2 = identity> requires (forward_range<_Rng1> || sized_range<_Rng1>) && (forward_range<_Rng2> || sized_range<_Rng2>) && indirectly_comparable, iterator_t<_Rng2>, _Pr, _Pj1, _Pj2> + && (!same_as, unreachable_sentinel_t>) // Per LWG-issue unnumbered as of 17.09.2021 _NODISCARD constexpr bool operator()( _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { // clang-format on - if constexpr (same_as, unreachable_sentinel_t> // - && same_as, unreachable_sentinel_t>) - return _Equal_4(_Ubegin(_Range1), _Uend(_Range1), _Ubegin(_Range2), _Uend(_Range2), _Pass_fn(_Pred), - _Pass_fn(_Proj1), _Pass_fn(_Proj2)); - else if constexpr (same_as, unreachable_sentinel_t> // - || same_as, unreachable_sentinel_t>) { + if constexpr (same_as, unreachable_sentinel_t>) { return false; - } else { - const range_difference_t<_Rng1> _Count1 = _RANGES distance(_Range1); - const range_difference_t<_Rng2> _Count2 = _RANGES distance(_Range2); - if (_Count2 > _Count1) { - return false; - } + } - auto _UFirst1 = _Ubegin(_Range1); - _RANGES advance(_UFirst1, static_cast>(_Count1 - _Count2)); - return _RANGES _Equal_count(_STD move(_UFirst1), _Ubegin(_Range2), - static_cast>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), - _Pass_fn(_Proj2)); + const range_difference_t<_Rng1> _Count1 = _RANGES distance(_Range1); + const range_difference_t<_Rng2> _Count2 = _RANGES distance(_Range2); + if (_Count2 > _Count1) { + return false; } + + auto _UFirst1 = _Ubegin(_Range1); + _RANGES advance(_UFirst1, static_cast>(_Count1 - _Count2)); + return _RANGES _Equal_count(_STD move(_UFirst1), _Ubegin(_Range2), + static_cast>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); } }; diff --git a/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp b/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp index e56c8133c23..bb358d12519 100644 --- a/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp +++ b/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp @@ -31,9 +31,6 @@ constexpr void smoke_test() { assert(!no_match2); } { // Validate infinite ranges - const same_as auto infinite_haystack = ends_with(views::iota(0), views::iota(0, 5)); - assert(!infinite_haystack); - const same_as auto infinite_needle = ends_with(views::iota(0, 5), views::iota(0)); assert(!infinite_needle); } @@ -51,10 +48,6 @@ constexpr void smoke_test() { assert(!no_match2); } { // Validate unreachable sentinel - const same_as auto unreachable_haystack = ends_with( - haystack.begin(), unreachable_sentinel, needle.begin(), needle.end(), equal_to{}, get_first, get_second); - assert(!unreachable_haystack); - const same_as auto unreachable_needle = ends_with( haystack.begin(), haystack.end(), needle.begin(), unreachable_sentinel, equal_to{}, get_first, get_second); assert(!unreachable_needle); From 91648681da2f03e0457cc9854823df18c832812b Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Thu, 14 Oct 2021 15:30:11 +0200 Subject: [PATCH 13/19] Begone infinite ranges --- stl/inc/algorithm | 24 +-- .../P1659R3_ranges_alg_ends_with/test.cpp | 127 ++++------- .../P1659R3_ranges_alg_starts_with/test.cpp | 202 ++++++++---------- 3 files changed, 129 insertions(+), 224 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index c9e1bac41ea..8fc23e305a5 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -2226,16 +2226,12 @@ namespace ranges { // clang-format off template _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> // Per LWG-issue unnumbered as of 17.09.2021 - && !(same_as<_Se1, unreachable_sentinel_t> && same_as<_Se2, unreachable_sentinel_t>)) + 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 (same_as<_Se2, unreachable_sentinel_t>) { - return false; - } auto _UFirst1 = _Get_unwrapped(_STD move(_First1)); auto _ULast1 = _Get_unwrapped(_STD move(_Last1)); @@ -2261,16 +2257,10 @@ namespace ranges { // clang-format off template - requires (indirectly_comparable, iterator_t<_Rng2>, _Pr, _Pj1, _Pj2> - && !(same_as, unreachable_sentinel_t> - && same_as, unreachable_sentinel_t>)) // Per LWG-issue unnumbered as of 17.09.2021 + requires indirectly_comparable, 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 (same_as, unreachable_sentinel_t>) { - return false; - } - if constexpr (_Sized_or_infinite_range<_Rng1> && sized_range<_Rng2>) { const range_difference_t<_Rng2> _Count2 = _RANGES distance(_Range2); if constexpr (sized_range<_Rng1>) { @@ -2303,16 +2293,11 @@ namespace ranges { requires (forward_iterator<_It1> || sized_sentinel_for<_Se1, _It1>) && (forward_iterator<_It2> || sized_sentinel_for<_Se2, _It2>) && indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2> - && (!same_as<_Se1, unreachable_sentinel_t>) // Per LWG-issue unnumbered as of 17.09.2021 _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 (same_as<_Se2, unreachable_sentinel_t>) { - return false; - } - auto _UFirst1 = _Get_unwrapped(_STD move(_First1)); auto _ULast1 = _Get_unwrapped(_STD move(_Last1)); auto _UFirst2 = _Get_unwrapped(_STD move(_First2)); @@ -2346,14 +2331,9 @@ namespace ranges { class _Pj2 = identity> requires (forward_range<_Rng1> || sized_range<_Rng1>) && (forward_range<_Rng2> || sized_range<_Rng2>) && indirectly_comparable, iterator_t<_Rng2>, _Pr, _Pj1, _Pj2> - && (!same_as, unreachable_sentinel_t>) // Per LWG-issue unnumbered as of 17.09.2021 _NODISCARD constexpr bool operator()( _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { // clang-format on - if constexpr (same_as, unreachable_sentinel_t>) { - return false; - } - const range_difference_t<_Rng1> _Count1 = _RANGES distance(_Range1); const range_difference_t<_Rng2> _Count2 = _RANGES distance(_Range2); if (_Count2 > _Count1) { diff --git a/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp b/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp index bb358d12519..6e810d8faba 100644 --- a/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp +++ b/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp @@ -2,111 +2,62 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include -#include #include #include #include -#include #include using namespace std; #include -constexpr void smoke_test() { - using ranges::dangling, ranges::equal_to, ranges::iterator_t, ranges::ends_with; - - constexpr array, 3> haystack = {{{0, 42}, {2, 42}, {4, 42}}}; - constexpr array, 1> short_haystack = {{{4, 42}}}; - constexpr array, 2> needle = {{{13, 2}, {13, 4}}}; - constexpr array, 2> wrong_needle = {{{13, 2}, {13, 3}}}; - - { // Validate sized ranges - const same_as auto match = ends_with(haystack, needle, equal_to{}, get_first, get_second); - assert(match); - - const same_as auto no_match = ends_with(haystack, wrong_needle, equal_to{}, get_first, get_second); - assert(!no_match); - - const same_as auto no_match2 = ends_with(short_haystack, needle, equal_to{}, get_first, get_second); - assert(!no_match2); - } - { // Validate infinite ranges - const same_as auto infinite_needle = ends_with(views::iota(0, 5), views::iota(0)); - assert(!infinite_needle); - } - { // Validate sized iterator + sentinel pairs - const same_as auto match = ends_with( - haystack.begin(), haystack.end(), needle.begin(), needle.end(), equal_to{}, get_first, get_second); - assert(match); - - const same_as auto no_match = ends_with(haystack.begin(), haystack.end(), wrong_needle.begin(), - wrong_needle.end(), equal_to{}, get_first, get_second); - assert(!no_match); - - const same_as auto no_match2 = ends_with(short_haystack.begin(), short_haystack.end(), needle.begin(), - needle.end(), equal_to{}, get_first, get_second); - assert(!no_match2); - } - { // Validate unreachable sentinel - const same_as auto unreachable_needle = ends_with( - haystack.begin(), haystack.end(), needle.begin(), unreachable_sentinel, equal_to{}, get_first, get_second); - assert(!unreachable_needle); - } -} - -int main() { - STATIC_ASSERT((smoke_test(), true)); - smoke_test(); -} - -#ifndef _PREFAST_ // TRANSITION, GH-1030 struct instantiator { - template - static void call() { - using ranges::begin, ranges::end, ranges::ends_with, ranges::iterator_t, ranges::forward_range, - ranges::sized_range, ranges::sentinel_t; + static constexpr pair haystack[] = {{0, 42}, {2, 42}, {4, 42}}; + static constexpr pair short_haystack[] = {{4, 42}}; + static constexpr pair needle[] = {{13, 2}, {13, 4}}; + static constexpr pair wrong_needle[] = {{13, 2}, {13, 3}}; - In1 in1{span{}}; - In2 in2{span{}}; + template + static constexpr void test() { + using ranges::begin, ranges::end, ranges::ends_with, ranges::forward_range, ranges::sized_range; if constexpr ((forward_range || sized_range) &&(forward_range || sized_range) ) { - if constexpr (!is_permissive) { - (void) ends_with(in1, in2); - } - - BinaryPredicateFor, iterator_t> pred{}; - (void) ends_with(in1, in2, pred); - - HalfProjectedBinaryPredicateFor> halfpred{}; - ProjectionFor> halfproj{}; - (void) ends_with(in1, in2, halfpred, halfproj); + { // Validate ranges + const same_as auto match = ends_with(haystack, needle, equal_to{}, get_first, get_second); + assert(match); - ProjectedBinaryPredicate<0, 1> projpred{}; - ProjectionFor, 0> proj1{}; - ProjectionFor, 1> proj2{}; - (void) ends_with(in1, in2, projpred, proj1, proj2); - } + const same_as auto no_match = + ends_with(haystack, wrong_needle, equal_to{}, get_first, get_second); + assert(!no_match); - if constexpr ((forward_iterator> || sized_sentinel_for, iterator_t>) &&( - forward_iterator> || sized_sentinel_for, iterator_t>) ) { - if constexpr (!is_permissive) { - (void) ends_with(begin(in1), end(in1), begin(in2), end(in2)); + const same_as auto no_match2 = + ends_with(short_haystack, needle, equal_to{}, get_first, get_second); + assert(!no_match2); + } + { // Validate iterator + sentinel pairs + const same_as auto match = ends_with( + begin(haystack), end(haystack), begin(needle), end(needle), equal_to{}, get_first, get_second); + assert(match); + + const same_as auto no_match = ends_with(begin(haystack), end(haystack), begin(wrong_needle), + end(wrong_needle), equal_to{}, get_first, get_second); + assert(!no_match); + + const same_as auto no_match2 = ends_with(begin(short_haystack), end(short_haystack), + begin(needle), end(needle), equal_to{}, get_first, get_second); + assert(!no_match2); } - - BinaryPredicateFor, iterator_t> pred{}; - (void) ends_with(begin(in1), end(in1), begin(in2), end(in2), pred); - - HalfProjectedBinaryPredicateFor> halfpred{}; - ProjectionFor> halfproj{}; - (void) ends_with(begin(in1), end(in1), begin(in2), end(in2), halfpred, halfproj); - - ProjectedBinaryPredicate<0, 1> projpred{}; - ProjectionFor, 0> proj1{}; - ProjectionFor, 1> proj2{}; - (void) ends_with(begin(in1), end(in1), begin(in2), end(in2), projpred, proj1, proj2); } } + + template + static void call() { + test(); + STATIC_ASSERT((test(), true)); + } }; -template void test_in_in(); +int main() { +#ifndef _PREFAST_ // TRANSITION, GH-1030 + test_in_in, const pair>(); #endif // TRANSITION, GH-1030 +} diff --git a/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp b/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp index 1a93abc9d5a..df063732dd8 100644 --- a/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp +++ b/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp @@ -2,137 +2,111 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include -#include #include #include #include -#include #include using namespace std; #include -constexpr void smoke_test() { - using ranges::dangling, ranges::equal_to, ranges::iterator_t, ranges::starts_with; +struct instantiator { + static constexpr pair haystack[] = {{0, 42}, {2, 42}, {4, 42}}; + static constexpr pair short_haystack[] = {{0, 42}}; + static constexpr pair needle[] = {{13, 0}, {13, 2}}; + static constexpr pair wrong_needle[] = {{13, 0}, {13, 3}}; - constexpr array, 3> haystack = {{{0, 42}, {2, 42}, {4, 42}}}; - constexpr array, 1> short_haystack = {{{0, 42}}}; - constexpr array, 2> needle = {{{13, 0}, {13, 2}}}; - constexpr array, 2> wrong_needle = {{{13, 0}, {13, 3}}}; + template + static constexpr void test() { + using ranges::begin, ranges::end, ranges::starts_with; - { // Validate sized ranges - const same_as auto match = starts_with(haystack, needle, equal_to{}, get_first, get_second); - assert(match); + { // Validate sized ranges + const same_as auto match = starts_with(haystack, needle, equal_to{}, get_first, get_second); + assert(match); - const same_as auto no_match = starts_with(haystack, wrong_needle, equal_to{}, get_first, get_second); - assert(!no_match); + const same_as auto no_match = starts_with(haystack, wrong_needle, equal_to{}, get_first, get_second); + assert(!no_match); - const same_as auto no_match2 = starts_with(short_haystack, needle, equal_to{}, get_first, get_second); - assert(!no_match2); - } - { // Validate non-sized ranges - const same_as auto match = starts_with( - basic_borrowed_range{haystack}, basic_borrowed_range{needle}, equal_to{}, get_first, get_second); - assert(match); - - const same_as auto no_match = starts_with( - basic_borrowed_range{haystack}, basic_borrowed_range{wrong_needle}, equal_to{}, get_first, get_second); - assert(!no_match); - - const same_as auto no_match2 = starts_with( - basic_borrowed_range{short_haystack}, basic_borrowed_range{needle}, equal_to{}, get_first, get_second); - assert(!no_match2); - } - { // Validate infinite ranges - const same_as auto infinite_haystack = starts_with(views::iota(0), views::iota(0, 5)); - assert(infinite_haystack); + const same_as auto no_match2 = starts_with(short_haystack, needle, equal_to{}, get_first, get_second); + assert(!no_match2); + } + { // Validate non-sized ranges + const same_as auto match = starts_with( + basic_borrowed_range{haystack}, basic_borrowed_range{needle}, equal_to{}, get_first, get_second); + assert(match); + + const same_as auto no_match = starts_with( + basic_borrowed_range{haystack}, basic_borrowed_range{wrong_needle}, equal_to{}, get_first, get_second); + assert(!no_match); + + const same_as auto no_match2 = starts_with( + basic_borrowed_range{short_haystack}, basic_borrowed_range{needle}, equal_to{}, get_first, get_second); + assert(!no_match2); + } + { // Validate infinite ranges + const same_as auto infinite_haystack = starts_with(views::iota(0), views::iota(0, 5)); + assert(infinite_haystack); - const same_as auto infinite_needle = starts_with(views::iota(0, 5), views::iota(0)); - assert(!infinite_needle); - } - { // Validate sized iterator + sentinel pairs - const same_as auto match = starts_with( - haystack.begin(), haystack.end(), needle.begin(), needle.end(), equal_to{}, get_first, get_second); - assert(match); - - const same_as auto no_match = starts_with(haystack.begin(), haystack.end(), wrong_needle.begin(), - wrong_needle.end(), equal_to{}, get_first, get_second); - assert(!no_match); - - const same_as auto no_match2 = starts_with(short_haystack.begin(), short_haystack.end(), needle.begin(), - needle.end(), equal_to{}, get_first, get_second); - assert(!no_match2); - } - { // Validate non-sized iterator + sentinel pairs - basic_borrowed_range wrapped_haystack{haystack}; - basic_borrowed_range wrapped_needle{needle}; - const same_as auto match = starts_with(wrapped_haystack.begin(), wrapped_haystack.end(), - wrapped_needle.begin(), wrapped_needle.end(), equal_to{}, get_first, get_second); - assert(match); - - basic_borrowed_range wrapped_haystack2{haystack}; - basic_borrowed_range wrapped_wrong_needle{wrong_needle}; - const same_as auto no_match = starts_with(wrapped_haystack2.begin(), wrapped_haystack2.end(), - wrapped_wrong_needle.begin(), wrapped_wrong_needle.end(), equal_to{}, get_first, get_second); - assert(!no_match); - - basic_borrowed_range wrapped_short_haystack{short_haystack}; - basic_borrowed_range wrapped_needle2{needle}; - const same_as auto no_match2 = starts_with(wrapped_short_haystack.begin(), wrapped_short_haystack.end(), - wrapped_needle2.begin(), wrapped_needle2.end(), equal_to{}, get_first, get_second); - assert(!no_match2); - } - { // Validate unreachable sentinels - basic_borrowed_range wrapped_haystack{haystack}; - basic_borrowed_range wrapped_needle{needle}; - const same_as auto unreachable_haystack = starts_with(wrapped_haystack.begin(), unreachable_sentinel, - wrapped_needle.begin(), wrapped_needle.end(), equal_to{}, get_first, get_second); - assert(unreachable_haystack); - - basic_borrowed_range wrapped_short_haystack{short_haystack}; - basic_borrowed_range wrapped_needle2{needle}; - const same_as auto unreachable_needle = - starts_with(wrapped_short_haystack.begin(), wrapped_short_haystack.end(), wrapped_needle2.begin(), - unreachable_sentinel, equal_to{}, get_first, get_second); - assert(!unreachable_needle); + const same_as auto infinite_needle = starts_with(views::iota(0, 5), views::iota(0)); + assert(!infinite_needle); + } + { // Validate sized iterator + sentinel pairs + const same_as auto match = starts_with( + begin(haystack), end(haystack), begin(needle), end(needle), equal_to{}, get_first, get_second); + assert(match); + + const same_as auto no_match = starts_with(begin(haystack), end(haystack), begin(wrong_needle), + end(wrong_needle), equal_to{}, get_first, get_second); + assert(!no_match); + + const same_as auto no_match2 = starts_with(begin(short_haystack), end(short_haystack), begin(needle), + end(needle), equal_to{}, get_first, get_second); + assert(!no_match2); + } + { // Validate non-sized iterator + sentinel pairs + basic_borrowed_range wrapped_haystack{haystack}; + basic_borrowed_range wrapped_needle{needle}; + const same_as auto match = starts_with(begin(wrapped_haystack), end(wrapped_haystack), + begin(wrapped_needle), end(wrapped_needle), equal_to{}, get_first, get_second); + assert(match); + + basic_borrowed_range wrapped_haystack2{haystack}; + basic_borrowed_range wrapped_wrong_needle{wrong_needle}; + const same_as auto no_match = starts_with(begin(wrapped_haystack), end(wrapped_haystack), + begin(wrapped_wrong_needle), end(wrapped_wrong_needle), equal_to{}, get_first, get_second); + assert(!no_match); + + basic_borrowed_range wrapped_short_haystack{short_haystack}; + basic_borrowed_range wrapped_needle2{needle}; + const same_as auto no_match2 = starts_with(begin(wrapped_short_haystack), end(wrapped_short_haystack), + begin(wrapped_needle2), end(wrapped_needle2), equal_to{}, get_first, get_second); + assert(!no_match2); + } + { // Validate unreachable sentinels + basic_borrowed_range wrapped_haystack{haystack}; + basic_borrowed_range wrapped_needle{needle}; + const same_as auto unreachable_haystack = starts_with(begin(wrapped_haystack), unreachable_sentinel, + begin(wrapped_needle), end(wrapped_needle), equal_to{}, get_first, get_second); + assert(unreachable_haystack); + + basic_borrowed_range wrapped_short_haystack{short_haystack}; + basic_borrowed_range wrapped_needle2{needle}; + const same_as auto unreachable_needle = + starts_with(begin(wrapped_short_haystack), end(wrapped_short_haystack), begin(wrapped_needle), + unreachable_sentinel, equal_to{}, get_first, get_second); + assert(!unreachable_needle); + } } -} -int main() { - STATIC_ASSERT((smoke_test(), true)); - smoke_test(); -} - -#ifndef _PREFAST_ // TRANSITION, GH-1030 -struct instantiator { - template + template static void call() { - using ranges::begin, ranges::end, ranges::starts_with, ranges::iterator_t; - - In1 in1{span{}}; - In2 in2{span{}}; - - if constexpr (!is_permissive) { - (void) starts_with(in1, in2); - (void) starts_with(begin(in1), end(in1), begin(in2), end(in2)); - } - - BinaryPredicateFor, iterator_t> pred{}; - (void) starts_with(in1, in2, pred); - (void) starts_with(begin(in1), end(in1), begin(in2), end(in2), pred); - - HalfProjectedBinaryPredicateFor> halfpred{}; - ProjectionFor> halfproj{}; - (void) starts_with(in1, in2, halfpred, halfproj); - (void) starts_with(begin(in1), end(in1), begin(in2), end(in2), halfpred, halfproj); - - ProjectedBinaryPredicate<0, 1> projpred{}; - ProjectionFor, 0> proj1{}; - ProjectionFor, 1> proj2{}; - (void) starts_with(in1, in2, projpred, proj1, proj2); - (void) starts_with(begin(in1), end(in1), begin(in2), end(in2), projpred, proj1, proj2); + test(); + STATIC_ASSERT((test(), true)); } }; -template void test_in_in(); +int main() { +#ifndef _PREFAST_ // TRANSITION, GH-1030 + test_in_in, const pair>(); #endif // TRANSITION, GH-1030 +} From 8c9bf6a4a37ee78a262254dc94a7f66ef118de63 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Thu, 14 Oct 2021 17:30:02 +0200 Subject: [PATCH 14/19] We cannot handle infinite input ranges at all in starts_with, as `_Equal_count` performs no bounds checking --- stl/inc/algorithm | 16 ++++++---------- .../P1659R3_ranges_alg_starts_with/test.cpp | 4 ++-- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 1d031042916..0d558338bd7 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -2237,12 +2237,10 @@ namespace ranges { auto _ULast1 = _Get_unwrapped(_STD move(_Last1)); auto _UFirst2 = _Get_unwrapped(_STD move(_First2)); const auto _ULast2 = _Get_unwrapped(_STD move(_Last2)); - if constexpr (_Sized_or_unreachable_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2>) { + if constexpr (sized_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2>) { const iter_difference_t<_It2> _Count2 = _ULast2 - _UFirst2; - if constexpr (sized_sentinel_for<_Se1, _It1>) { - if (_Count2 > _ULast1 - _UFirst1) { - return false; - } + if (_Count2 > _ULast1 - _UFirst1) { + return false; } return _RANGES _Equal_count(_STD move(_UFirst1), _STD move(_UFirst2), @@ -2261,12 +2259,10 @@ namespace ranges { _NODISCARD constexpr bool operator()( _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { // clang-format on - if constexpr (_Sized_or_infinite_range<_Rng1> && sized_range<_Rng2>) { + if constexpr (sized_range<_Rng1> && sized_range<_Rng2>) { const range_difference_t<_Rng2> _Count2 = _RANGES distance(_Range2); - if constexpr (sized_range<_Rng1>) { - if (_Count2 > _RANGES distance(_Range1)) { - return false; - } + if (_Count2 > _RANGES distance(_Range1)) { + return false; } return _RANGES _Equal_count(_Ubegin(_Range1), _Ubegin(_Range2), diff --git a/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp b/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp index df063732dd8..29334f39933 100644 --- a/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp +++ b/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp @@ -72,7 +72,7 @@ struct instantiator { basic_borrowed_range wrapped_haystack2{haystack}; basic_borrowed_range wrapped_wrong_needle{wrong_needle}; - const same_as auto no_match = starts_with(begin(wrapped_haystack), end(wrapped_haystack), + const same_as auto no_match = starts_with(begin(wrapped_haystack2), end(wrapped_haystack2), begin(wrapped_wrong_needle), end(wrapped_wrong_needle), equal_to{}, get_first, get_second); assert(!no_match); @@ -92,7 +92,7 @@ struct instantiator { basic_borrowed_range wrapped_short_haystack{short_haystack}; basic_borrowed_range wrapped_needle2{needle}; const same_as auto unreachable_needle = - starts_with(begin(wrapped_short_haystack), end(wrapped_short_haystack), begin(wrapped_needle), + starts_with(begin(wrapped_short_haystack), end(wrapped_short_haystack), begin(wrapped_needle2), unreachable_sentinel, equal_to{}, get_first, get_second); assert(!unreachable_needle); } From 507f85b73aba4c48c1d1314c34d17a3083128784 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 15 Oct 2021 08:52:32 +0200 Subject: [PATCH 15/19] Reenable infinite sizes optimization --- stl/inc/algorithm | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 0d558338bd7..1d031042916 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -2237,10 +2237,12 @@ namespace ranges { auto _ULast1 = _Get_unwrapped(_STD move(_Last1)); auto _UFirst2 = _Get_unwrapped(_STD move(_First2)); const auto _ULast2 = _Get_unwrapped(_STD move(_Last2)); - if constexpr (sized_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2>) { + if constexpr (_Sized_or_unreachable_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2>) { const iter_difference_t<_It2> _Count2 = _ULast2 - _UFirst2; - if (_Count2 > _ULast1 - _UFirst1) { - return false; + if constexpr (sized_sentinel_for<_Se1, _It1>) { + if (_Count2 > _ULast1 - _UFirst1) { + return false; + } } return _RANGES _Equal_count(_STD move(_UFirst1), _STD move(_UFirst2), @@ -2259,10 +2261,12 @@ namespace ranges { _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>) { + if constexpr (_Sized_or_infinite_range<_Rng1> && sized_range<_Rng2>) { const range_difference_t<_Rng2> _Count2 = _RANGES distance(_Range2); - if (_Count2 > _RANGES distance(_Range1)) { - return false; + if constexpr (sized_range<_Rng1>) { + if (_Count2 > _RANGES distance(_Range1)) { + return false; + } } return _RANGES _Equal_count(_Ubegin(_Range1), _Ubegin(_Range2), From 58da2b3c88081df4df0f81875c8898c8e6d22bdb Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Sun, 17 Oct 2021 20:37:58 -0700 Subject: [PATCH 16/19] optimize ends_with; fix borked tests --- stl/inc/algorithm | 200 +++++++++++++++--- .../P1659R3_ranges_alg_ends_with/test.cpp | 103 ++++++--- .../P1659R3_ranges_alg_starts_with/test.cpp | 114 ++++------ 3 files changed, 282 insertions(+), 135 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 1d031042916..8ae09af8a34 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -717,6 +717,10 @@ namespace ranges { template _NODISCARD static constexpr bool _Equal_4( _It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2>); + for (;;) { if (_First1 == _Last1) { return _First2 == _Last2; @@ -2303,47 +2307,185 @@ namespace ranges { auto _UFirst2 = _Get_unwrapped(_STD move(_First2)); auto _ULast2 = _Get_unwrapped(_STD move(_Last2)); - iter_difference_t<_It1> _Count1; - if constexpr (sized_sentinel_for<_Se1, _It1>) { // TRANSITION, LWG-3392 - _Count1 = _ULast1 - _UFirst1; - } else { - _Count1 = _RANGES distance(_UFirst1, _ULast1); - } + auto _Count1 = _Distance_helper(_UFirst1, _ULast1); + auto _Count2 = _Distance_helper(_UFirst2, _ULast2); - iter_difference_t<_It2> _Count2; - if constexpr (sized_sentinel_for<_Se2, _It2>) { // TRANSITION, LWG-3392 - _Count2 = _ULast2 - _UFirst2; - } else { - _Count2 = _RANGES distance(_UFirst2, _ULast2); - } - - if (_Count2 > _Count1) { - return false; - } - - _RANGES advance(_UFirst1, static_cast>(_Count1 - _Count2)); - return _RANGES _Equal_count(_STD move(_UFirst1), _STD move(_UFirst2), - static_cast>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Ends_with_impl(_STD move(_UFirst1), _STD move(_ULast1), _STD move(_Count1), _STD move(_UFirst2), + _STD move(_ULast2), _STD move(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); } // clang-format off template - requires (forward_range<_Rng1> || sized_range<_Rng1>) && (forward_range<_Rng2> || sized_range<_Rng2>) + requires (forward_range<_Rng1> || sized_range<_Rng1>) + && (forward_range<_Rng2> || sized_range<_Rng2>) && indirectly_comparable, 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; + auto _Count1 = _Distance_helper(_Range1); + auto _Count2 = _Distance_helper(_Range2); + + return _Ends_with_impl(_Ubegin(_Range1), _Uend(_Range1), _STD move(_Count1), _Ubegin(_Range2), + _Uend(_Range2), _STD move(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } + + private: + struct _Not_a_difference {}; + + template + [[nodiscard]] static constexpr auto _Distance_helper(const _It& _First, const _Se& _Last) { + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + if constexpr (sized_sentinel_for<_Se, _It>) { + return _Last - _First; + } else { + return _Not_a_difference{}; + } + } + + template + [[nodiscard]] static constexpr auto _Distance_helper(_Rng&& _Range) { + _STL_INTERNAL_STATIC_ASSERT(range<_Rng>); + if constexpr (sized_range<_Rng>) { + return _RANGES distance(_Range); + } else { + return _Not_a_difference{}; + } + } + + template + [[nodiscard]] static constexpr bool _Match_backwards( + const _Ty _First1, _It1 _Last1, const _It2 _First2, _It2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(same_as<_Ty, _It1> || same_as<_Ty, unreachable_sentinel_t>); + _STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2>); + + for (;;) { + if (_First2 == _Last2) { // needle exhausted - match + return true; + } + + if (_First1 == _Last1) { // haystack exhausted - no match + return false; + } + + --_Last1; + --_Last2; + if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_Last1), _STD invoke(_Proj2, *_Last2))) { + return false; // non-equal elements - no match + } } + } + + template + [[nodiscard]] static constexpr bool _Ends_with_impl(_It1 _First1, _Se1 _Last1, _Diff1 _Count1, _It2 _First2, + _Se2 _Last2, _Diff2 _Count2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(_Is_any_of_v<_Diff1, iter_difference_t<_It1>, _Not_a_difference>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(_Is_any_of_v<_Diff2, iter_difference_t<_It2>, _Not_a_difference>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2>); + + constexpr bool _Sized1 = !same_as<_Diff1, _Not_a_difference>; + constexpr bool _Sized2 = !same_as<_Diff2, _Not_a_difference>; + constexpr bool _Both_sized = _Sized1 && _Sized2; + constexpr bool _Bi_common1 = _Bidi_common<_It1, _Se1>; + constexpr bool _Bi_common2 = _Bidi_common<_It2, _Se2>; + constexpr bool _Both_bi_common = _Bi_common1 && _Bi_common2; - auto _UFirst1 = _Ubegin(_Range1); - _RANGES advance(_UFirst1, static_cast>(_Count1 - _Count2)); - return _RANGES _Equal_count(_STD move(_UFirst1), _Ubegin(_Range2), - static_cast>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + if constexpr (_Both_sized) { + if (_Count2 > _Count1) { + return false; + } + } + + if constexpr (_Sized2) { + if (_Count2 == 0) { + return true; + } + } + + if constexpr (_Both_bi_common && !(random_access_iterator<_It1> && _Both_sized)) { + if constexpr (_Both_sized) { + return _Match_backwards(unreachable_sentinel, _STD move(_Last1), _STD move(_First2), + _STD move(_Last2), _Pred, _Proj1, _Proj2); + } else { + return _Match_backwards(_STD move(_First1), _STD move(_Last1), _STD move(_First2), + _STD move(_Last2), _Pred, _Proj1, _Proj2); + } + } else if constexpr (_Sized2) { + if constexpr (random_access_iterator<_It1> && _Sized1) { + _First1 += static_cast>(_Count1 - _Count2); + } else if constexpr (_Bi_common1) { + if constexpr (_Sized1) { + if ((_Count1 >> 1) >= _Count2) { // beginning of potential match is closer to _Last1 + _RANGES advance(_Last1, static_cast>(-_Count2)); + _First1 = _STD move(_Last1); + } else { // beginning of potential match is closer to _First1 + _RANGES advance(_First1, static_cast>(_Count1 - _Count2)); + } + } else { // Only second range is sized + if (_RANGES advance(_Last1, static_cast>(-_Count2), _First1) != 0) { + // distance(_First1, _Last1) < _Count2 + return false; + } + _First1 = _STD move(_Last1); + } + } else if constexpr (forward_iterator<_It1>) { + auto _Mid1 = _First1; + auto _Count = _Count2; + do { + if (_Mid1 == _Last1) { // distance(_First1, _Last1) < distance(_First2, _Last2) + return false; + } + + ++_Mid1; + } while (--_Count != 0); + + // At this point, distance(_First1, _Mid1) == distance(_First2, _Last2) + while (_Mid1 != _Last1) { + ++_First1; + ++_Mid1; + } + } else { + _STL_INTERNAL_STATIC_ASSERT(_Both_sized); + _RANGES advance(_First1, static_cast>(_Count1 - _Count2)); + } + + return _RANGES _Equal_count(_STD move(_First1), _STD move(_First2), + static_cast>(_Count2), _Pred, _Proj1, _Proj2); + } else { + iter_difference_t<_It2> _Count; + + if constexpr (forward_iterator<_It1>) { + auto _Mid1 = _First1; + auto _Mid2 = _First2; + _Count = 0; + for (; _Mid2 != _Last2; ++_Mid1, (void) ++_Mid2, ++_Count) { + if (_Mid1 == _Last1) { // distance(_First1, _Last1) < distance(_First2, _Last2) + return false; + } + } + + // distance(_First1, _Mid1) == distance(_First2, _Last2) == _Count + while (_Mid1 != _Last1) { + ++_First1; + ++_Mid1; + } + } else { + _STL_INTERNAL_STATIC_ASSERT(_Sized1 && !_Sized2); + _Count = _RANGES distance(_First2, _Last2); + if (_Count > _Count1) { + return false; // distance(_First1, _Last1) < distance(_First2, _Last2) + } + _RANGES advance(_First1, static_cast>(_Count1 - _Count)); + } + + return _RANGES _Equal_count(_STD move(_First1), _STD move(_First2), + static_cast>(_Count), _Pred, _Proj1, _Proj2); + } } }; diff --git a/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp b/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp index 6e810d8faba..15f1276d4bf 100644 --- a/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp +++ b/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp @@ -3,61 +3,94 @@ #include #include -#include +#include #include #include -using namespace std; #include +using namespace std; + +// clang-format off +template +concept testable_range = ranges::input_range && (ranges::forward_range || ranges::sized_range); + +template +concept testable_sentinel = ranges::input_range + && (ranges::forward_range || sized_sentinel_for, ranges::iterator_t>); +// clang-format on + struct instantiator { static constexpr pair haystack[] = {{0, 42}, {2, 42}, {4, 42}}; static constexpr pair short_haystack[] = {{4, 42}}; static constexpr pair needle[] = {{13, 2}, {13, 4}}; static constexpr pair wrong_needle[] = {{13, 2}, {13, 3}}; - template - static constexpr void test() { - using ranges::begin, ranges::end, ranges::ends_with, ranges::forward_range, ranges::sized_range; - - if constexpr ((forward_range || sized_range) &&(forward_range || sized_range) ) { - { // Validate ranges - const same_as auto match = ends_with(haystack, needle, equal_to{}, get_first, get_second); - assert(match); - - const same_as auto no_match = - ends_with(haystack, wrong_needle, equal_to{}, get_first, get_second); - assert(!no_match); - - const same_as auto no_match2 = - ends_with(short_haystack, needle, equal_to{}, get_first, get_second); - assert(!no_match2); - } - { // Validate iterator + sentinel pairs - const same_as auto match = ends_with( - begin(haystack), end(haystack), begin(needle), end(needle), equal_to{}, get_first, get_second); - assert(match); - - const same_as auto no_match = ends_with(begin(haystack), end(haystack), begin(wrong_needle), - end(wrong_needle), equal_to{}, get_first, get_second); - assert(!no_match); - - const same_as auto no_match2 = ends_with(begin(short_haystack), end(short_haystack), - begin(needle), end(needle), equal_to{}, get_first, get_second); - assert(!no_match2); - } + template + static constexpr void test_range() { + using ranges::ends_with, ranges::equal_to; + + { + const same_as auto match = ends_with(In1{haystack}, In2{needle}, equal_to{}, get_first, get_second); + assert(match); + } + { + const same_as auto match = + ends_with(In1{haystack}, In2{wrong_needle}, equal_to{}, get_first, get_second); + assert(!match); + } + { + const same_as auto match = + ends_with(In1{short_haystack}, In2{needle}, equal_to{}, get_first, get_second); + assert(!match); + } + } + + template + 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 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 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 auto match = + ends_with(begin(h), end(h), begin(n), end(n), equal_to{}, get_first, get_second); + assert(!match); } } template static void call() { - test(); - STATIC_ASSERT((test(), true)); + if constexpr (testable_range && testable_range) { + test_range(); + STATIC_ASSERT((test_range(), true)); + } + + if constexpr (testable_sentinel && testable_sentinel) { + test_iterator_sentinel(); + STATIC_ASSERT((test_iterator_sentinel(), true)); + } } }; int main() { #ifndef _PREFAST_ // TRANSITION, GH-1030 - test_in_in, const pair>(); + test_in_in, const pair>(); #endif // TRANSITION, GH-1030 } diff --git a/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp b/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp index 29334f39933..094d2df0b1b 100644 --- a/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp +++ b/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp @@ -3,13 +3,14 @@ #include #include -#include +#include #include #include -using namespace std; #include +using namespace std; + struct instantiator { static constexpr pair haystack[] = {{0, 42}, {2, 42}, {4, 42}}; static constexpr pair short_haystack[] = {{0, 42}}; @@ -20,81 +21,43 @@ struct instantiator { static constexpr void test() { using ranges::begin, ranges::end, ranges::starts_with; - { // Validate sized ranges - const same_as auto match = starts_with(haystack, needle, equal_to{}, get_first, get_second); + // Validate range overload + { + const same_as auto match = starts_with(In1{haystack}, In2{needle}, equal_to{}, get_first, get_second); assert(match); - - const same_as auto no_match = starts_with(haystack, wrong_needle, equal_to{}, get_first, get_second); - assert(!no_match); - - const same_as auto no_match2 = starts_with(short_haystack, needle, equal_to{}, get_first, get_second); - assert(!no_match2); } - { // Validate non-sized ranges - const same_as auto match = starts_with( - basic_borrowed_range{haystack}, basic_borrowed_range{needle}, equal_to{}, get_first, get_second); - assert(match); - - const same_as auto no_match = starts_with( - basic_borrowed_range{haystack}, basic_borrowed_range{wrong_needle}, equal_to{}, get_first, get_second); - assert(!no_match); - - const same_as auto no_match2 = starts_with( - basic_borrowed_range{short_haystack}, basic_borrowed_range{needle}, equal_to{}, get_first, get_second); - assert(!no_match2); + { + const same_as auto match = + starts_with(In1{haystack}, In2{wrong_needle}, equal_to{}, get_first, get_second); + assert(!match); } - { // Validate infinite ranges - const same_as auto infinite_haystack = starts_with(views::iota(0), views::iota(0, 5)); - assert(infinite_haystack); - - const same_as auto infinite_needle = starts_with(views::iota(0, 5), views::iota(0)); - assert(!infinite_needle); + { + const same_as auto match = + starts_with(In1{short_haystack}, In2{needle}, equal_to{}, get_first, get_second); + assert(!match); } - { // Validate sized iterator + sentinel pairs - const same_as auto match = starts_with( - begin(haystack), end(haystack), begin(needle), end(needle), equal_to{}, get_first, get_second); - assert(match); - const same_as auto no_match = starts_with(begin(haystack), end(haystack), begin(wrong_needle), - end(wrong_needle), equal_to{}, get_first, get_second); - assert(!no_match); - - const same_as auto no_match2 = starts_with(begin(short_haystack), end(short_haystack), begin(needle), - end(needle), equal_to{}, get_first, get_second); - assert(!no_match2); - } - { // Validate non-sized iterator + sentinel pairs - basic_borrowed_range wrapped_haystack{haystack}; - basic_borrowed_range wrapped_needle{needle}; - const same_as auto match = starts_with(begin(wrapped_haystack), end(wrapped_haystack), - begin(wrapped_needle), end(wrapped_needle), equal_to{}, get_first, get_second); + // Validate iterator + sentinel overload + { + In1 h{haystack}; + In2 n{needle}; + const same_as auto match = + starts_with(begin(h), end(h), begin(n), end(n), equal_to{}, get_first, get_second); assert(match); - - basic_borrowed_range wrapped_haystack2{haystack}; - basic_borrowed_range wrapped_wrong_needle{wrong_needle}; - const same_as auto no_match = starts_with(begin(wrapped_haystack2), end(wrapped_haystack2), - begin(wrapped_wrong_needle), end(wrapped_wrong_needle), equal_to{}, get_first, get_second); - assert(!no_match); - - basic_borrowed_range wrapped_short_haystack{short_haystack}; - basic_borrowed_range wrapped_needle2{needle}; - const same_as auto no_match2 = starts_with(begin(wrapped_short_haystack), end(wrapped_short_haystack), - begin(wrapped_needle2), end(wrapped_needle2), equal_to{}, get_first, get_second); - assert(!no_match2); } - { // Validate unreachable sentinels - basic_borrowed_range wrapped_haystack{haystack}; - basic_borrowed_range wrapped_needle{needle}; - const same_as auto unreachable_haystack = starts_with(begin(wrapped_haystack), unreachable_sentinel, - begin(wrapped_needle), end(wrapped_needle), equal_to{}, get_first, get_second); - assert(unreachable_haystack); - - basic_borrowed_range wrapped_short_haystack{short_haystack}; - basic_borrowed_range wrapped_needle2{needle}; - const same_as auto unreachable_needle = - starts_with(begin(wrapped_short_haystack), end(wrapped_short_haystack), begin(wrapped_needle2), - unreachable_sentinel, equal_to{}, get_first, get_second); - assert(!unreachable_needle); + { + In1 h{haystack}; + In2 n{wrong_needle}; + const same_as 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 auto match = + starts_with(begin(h), end(h), begin(n), end(n), equal_to{}, get_first, get_second); + assert(!match); } } @@ -106,7 +69,16 @@ struct instantiator { }; int main() { + { // infinite haystack + const same_as auto match = ranges::starts_with(views::iota(0), views::iota(0, 5)); + assert(match); + } + { // infinite needle + const same_as auto match = ranges::starts_with(views::iota(0, 5), views::iota(0)); + assert(!match); + } + #ifndef _PREFAST_ // TRANSITION, GH-1030 - test_in_in, const pair>(); + test_in_in, const pair>(); #endif // TRANSITION, GH-1030 } From cd7733d7c0cc7f43af0b55fb145bfa099b17dc92 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Sun, 17 Oct 2021 23:13:42 -0700 Subject: [PATCH 17/19] Factor out large middle section of _Ends_with_impl --- stl/inc/algorithm | 101 +++++++++++++++++++++++++--------------------- 1 file changed, 55 insertions(+), 46 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 8ae09af8a34..68d5724ace0 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -2378,6 +2378,57 @@ namespace ranges { } } + template + [[nodiscard]] static constexpr bool _Ends_with_sized_needle(_It1 _First1, _Se1 _Last1, _Diff1 _Count1, + _It2 _First2, iter_difference_t<_It2> _Count2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(_Is_any_of_v<_Diff1, iter_difference_t<_It1>, _Not_a_difference>); + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2>); + + constexpr bool _Sized1 = !same_as<_Diff1, _Not_a_difference>; + + if constexpr (random_access_iterator<_It1> && _Sized1) { + _First1 += static_cast>(_Count1 - _Count2); + } else if constexpr (_Bidi_common<_It1, _Se1>) { + if constexpr (_Sized1) { + if ((_Count1 >> 1) >= _Count2) { // beginning of potential match is closer to _Last1 + _RANGES advance(_Last1, static_cast>(-_Count2)); + _First1 = _STD move(_Last1); + } else { // beginning of potential match is closer to _First1 + _RANGES advance(_First1, static_cast>(_Count1 - _Count2)); + } + } else { // Only second range is sized + if (_RANGES advance(_Last1, static_cast>(-_Count2), _First1) != 0) { + // distance(_First1, _Last1) < _Count2 + return false; + } + _First1 = _STD move(_Last1); + } + } else if constexpr (forward_iterator<_It1>) { + auto _Mid1 = _First1; + auto _Count = _Count2; + do { + if (_Mid1 == _Last1) { // distance(_First1, _Last1) < _Count2 + return false; + } + + ++_Mid1; + } while (--_Count != 0); + + // At this point, distance(_First1, _Mid1) == _Count2 + while (_Mid1 != _Last1) { + ++_First1; + ++_Mid1; + } + } else { + _RANGES advance(_First1, static_cast>(_Count1 - _Count2)); + } + + return _RANGES _Equal_count(_STD move(_First1), _STD move(_First2), + static_cast>(_Count2), _Pred, _Proj1, _Proj2); + } + template [[nodiscard]] static constexpr bool _Ends_with_impl(_It1 _First1, _Se1 _Last1, _Diff1 _Count1, _It2 _First2, @@ -2388,12 +2439,9 @@ namespace ranges { _STL_INTERNAL_STATIC_ASSERT(_Is_any_of_v<_Diff2, iter_difference_t<_It2>, _Not_a_difference>); _STL_INTERNAL_STATIC_ASSERT(indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2>); - constexpr bool _Sized1 = !same_as<_Diff1, _Not_a_difference>; constexpr bool _Sized2 = !same_as<_Diff2, _Not_a_difference>; - constexpr bool _Both_sized = _Sized1 && _Sized2; - constexpr bool _Bi_common1 = _Bidi_common<_It1, _Se1>; - constexpr bool _Bi_common2 = _Bidi_common<_It2, _Se2>; - constexpr bool _Both_bi_common = _Bi_common1 && _Bi_common2; + constexpr bool _Both_sized = !same_as<_Diff1, _Not_a_difference> && _Sized2; + constexpr bool _Both_bi_common = _Bidi_common<_It1, _Se1> && _Bidi_common<_It2, _Se2>; if constexpr (_Both_sized) { if (_Count2 > _Count1) { @@ -2416,46 +2464,8 @@ namespace ranges { _STD move(_Last2), _Pred, _Proj1, _Proj2); } } else if constexpr (_Sized2) { - if constexpr (random_access_iterator<_It1> && _Sized1) { - _First1 += static_cast>(_Count1 - _Count2); - } else if constexpr (_Bi_common1) { - if constexpr (_Sized1) { - if ((_Count1 >> 1) >= _Count2) { // beginning of potential match is closer to _Last1 - _RANGES advance(_Last1, static_cast>(-_Count2)); - _First1 = _STD move(_Last1); - } else { // beginning of potential match is closer to _First1 - _RANGES advance(_First1, static_cast>(_Count1 - _Count2)); - } - } else { // Only second range is sized - if (_RANGES advance(_Last1, static_cast>(-_Count2), _First1) != 0) { - // distance(_First1, _Last1) < _Count2 - return false; - } - _First1 = _STD move(_Last1); - } - } else if constexpr (forward_iterator<_It1>) { - auto _Mid1 = _First1; - auto _Count = _Count2; - do { - if (_Mid1 == _Last1) { // distance(_First1, _Last1) < distance(_First2, _Last2) - return false; - } - - ++_Mid1; - } while (--_Count != 0); - - // At this point, distance(_First1, _Mid1) == distance(_First2, _Last2) - while (_Mid1 != _Last1) { - ++_First1; - ++_Mid1; - } - } else { - _STL_INTERNAL_STATIC_ASSERT(_Both_sized); - _RANGES advance(_First1, static_cast>(_Count1 - _Count2)); - } - - return _RANGES _Equal_count(_STD move(_First1), _STD move(_First2), - static_cast>(_Count2), _Pred, _Proj1, _Proj2); + return _Ends_with_sized_needle(_STD move(_First1), _STD move(_Last1), _STD move(_Count1), + _STD move(_First2), _STD move(_Count2), _Pred, _Proj1, _Proj2); } else { iter_difference_t<_It2> _Count; @@ -2475,7 +2485,6 @@ namespace ranges { ++_Mid1; } } else { - _STL_INTERNAL_STATIC_ASSERT(_Sized1 && !_Sized2); _Count = _RANGES distance(_First2, _Last2); if (_Count > _Count1) { return false; // distance(_First1, _Last1) < distance(_First2, _Last2) From 2df4fa1972cc9358338c699513f31725dcdbeee5 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 18 Oct 2021 23:35:46 -0700 Subject: [PATCH 18/19] Review comments --- stl/inc/algorithm | 103 ++++++++++-------- .../P1659R3_ranges_alg_ends_with/test.cpp | 41 +++++-- .../P1659R3_ranges_alg_starts_with/test.cpp | 2 +- 3 files changed, 93 insertions(+), 53 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 68d5724ace0..87cab901404 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -693,7 +693,7 @@ namespace ranges { // clang-format off template requires indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2> - _NODISCARD static constexpr bool _Equal_count( + _NODISCARD constexpr bool _Equal_count( _It1 _First1, _It2 _First2, _Size _Count, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { // clang-format on _STL_INTERNAL_CHECK(_Count >= 0); @@ -2307,11 +2307,11 @@ namespace ranges { auto _UFirst2 = _Get_unwrapped(_STD move(_First2)); auto _ULast2 = _Get_unwrapped(_STD move(_Last2)); - auto _Count1 = _Distance_helper(_UFirst1, _ULast1); - auto _Count2 = _Distance_helper(_UFirst2, _ULast2); + const auto _Count1 = _Distance_helper(_UFirst1, _ULast1); + const auto _Count2 = _Distance_helper(_UFirst2, _ULast2); - return _Ends_with_impl(_STD move(_UFirst1), _STD move(_ULast1), _STD move(_Count1), _STD move(_UFirst2), - _STD move(_ULast2), _STD move(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Ends_with_impl(_STD move(_UFirst1), _STD move(_ULast1), _Count1, _STD move(_UFirst2), + _STD move(_ULast2), _Count2, _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); } // clang-format off @@ -2323,11 +2323,11 @@ namespace ranges { _NODISCARD constexpr bool operator()( _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { // clang-format on - auto _Count1 = _Distance_helper(_Range1); - auto _Count2 = _Distance_helper(_Range2); + const auto _Count1 = _Distance_helper(_Range1); + const auto _Count2 = _Distance_helper(_Range2); - return _Ends_with_impl(_Ubegin(_Range1), _Uend(_Range1), _STD move(_Count1), _Ubegin(_Range2), - _Uend(_Range2), _STD move(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Ends_with_impl(_Ubegin(_Range1), _Uend(_Range1), _Count1, _Ubegin(_Range2), _Uend(_Range2), _Count2, + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); } private: @@ -2381,8 +2381,8 @@ namespace ranges { template [[nodiscard]] static constexpr bool _Ends_with_sized_needle(_It1 _First1, _Se1 _Last1, _Diff1 _Count1, _It2 _First2, iter_difference_t<_It2> _Count2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { - _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); - _STL_INTERNAL_STATIC_ASSERT(_Is_any_of_v<_Diff1, iter_difference_t<_It1>, _Not_a_difference>); + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1> && sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It1> || same_as<_Diff1, iter_difference_t<_It1>>); _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It2>); _STL_INTERNAL_STATIC_ASSERT(indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2>); @@ -2429,6 +2429,45 @@ namespace ranges { static_cast>(_Count2), _Pred, _Proj1, _Proj2); } + template + [[nodiscard]] static constexpr bool _Ends_with_unsized_needle( + _It1 _First1, _Se1 _Last1, _Diff1 _Count1, _It2 _First2, _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1> && sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It1> || same_as<_Diff1, iter_difference_t<_It1>>); + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It2> && sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2>); + + constexpr bool _Sized1 = !same_as<_Diff1, _Not_a_difference>; + + iter_difference_t<_It2> _Count2 = 0; + + if constexpr (_Sized1) { + for (auto _Mid2 = _First2; _Mid2 != _Last2; ++_Mid2, (void) ++_Count2) { + if (_Count2 == _Count1) { // distance(_First1, _Last1) < distance(_First2, _Last2) + return false; + } + } + + _RANGES advance(_First1, static_cast>(_Count1 - _Count2)); + } else { // first range isn't sized, so must be forward + auto _Mid1 = _First1; + for (auto _Mid2 = _First2; _Mid2 != _Last2; ++_Mid1, (void) ++_Mid2, ++_Count2) { + if (_Mid1 == _Last1) { // distance(_First1, _Last1) < distance(_First2, _Last2) + return false; + } + } + + // distance(_First1, _Mid1) == distance(_First2, _Last2) == _Count2 + while (_Mid1 != _Last1) { + ++_First1; + ++_Mid1; + } + } + + return _RANGES _Equal_count(_STD move(_First1), _STD move(_First2), + static_cast>(_Count2), _Pred, _Proj1, _Proj2); + } + template [[nodiscard]] static constexpr bool _Ends_with_impl(_It1 _First1, _Se1 _Last1, _Diff1 _Count1, _It2 _First2, @@ -2439,9 +2478,10 @@ namespace ranges { _STL_INTERNAL_STATIC_ASSERT(_Is_any_of_v<_Diff2, iter_difference_t<_It2>, _Not_a_difference>); _STL_INTERNAL_STATIC_ASSERT(indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2>); - constexpr bool _Sized2 = !same_as<_Diff2, _Not_a_difference>; - constexpr bool _Both_sized = !same_as<_Diff1, _Not_a_difference> && _Sized2; - constexpr bool _Both_bi_common = _Bidi_common<_It1, _Se1> && _Bidi_common<_It2, _Se2>; + constexpr bool _Sized1 = !same_as<_Diff1, _Not_a_difference>; + constexpr bool _Sized2 = !same_as<_Diff2, _Not_a_difference>; + constexpr bool _Both_sized = _Sized1 && _Sized2; + constexpr bool _Both_bidi_common = _Bidi_common<_It1, _Se1> && _Bidi_common<_It2, _Se2>; if constexpr (_Both_sized) { if (_Count2 > _Count1) { @@ -2455,7 +2495,7 @@ namespace ranges { } } - if constexpr (_Both_bi_common && !(random_access_iterator<_It1> && _Both_sized)) { + if constexpr (_Both_bidi_common && !(random_access_iterator<_It1> && _Both_sized)) { if constexpr (_Both_sized) { return _Match_backwards(unreachable_sentinel, _STD move(_Last1), _STD move(_First2), _STD move(_Last2), _Pred, _Proj1, _Proj2); @@ -2464,36 +2504,11 @@ namespace ranges { _STD move(_Last2), _Pred, _Proj1, _Proj2); } } else if constexpr (_Sized2) { - return _Ends_with_sized_needle(_STD move(_First1), _STD move(_Last1), _STD move(_Count1), - _STD move(_First2), _STD move(_Count2), _Pred, _Proj1, _Proj2); + return _Ends_with_sized_needle( + _STD move(_First1), _STD move(_Last1), _Count1, _STD move(_First2), _Count2, _Pred, _Proj1, _Proj2); } else { - iter_difference_t<_It2> _Count; - - if constexpr (forward_iterator<_It1>) { - auto _Mid1 = _First1; - auto _Mid2 = _First2; - _Count = 0; - for (; _Mid2 != _Last2; ++_Mid1, (void) ++_Mid2, ++_Count) { - if (_Mid1 == _Last1) { // distance(_First1, _Last1) < distance(_First2, _Last2) - return false; - } - } - - // distance(_First1, _Mid1) == distance(_First2, _Last2) == _Count - while (_Mid1 != _Last1) { - ++_First1; - ++_Mid1; - } - } else { - _Count = _RANGES distance(_First2, _Last2); - if (_Count > _Count1) { - return false; // distance(_First1, _Last1) < distance(_First2, _Last2) - } - _RANGES advance(_First1, static_cast>(_Count1 - _Count)); - } - - return _RANGES _Equal_count(_STD move(_First1), _STD move(_First2), - static_cast>(_Count), _Pred, _Proj1, _Proj2); + return _Ends_with_unsized_needle(_STD move(_First1), _STD move(_Last1), _Count1, _STD move(_First2), + _STD move(_Last2), _Pred, _Proj1, _Proj2); } } }; diff --git a/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp b/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp index 15f1276d4bf..ca553e46e11 100644 --- a/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp +++ b/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -21,9 +22,10 @@ concept testable_sentinel = ranges::input_range // clang-format on struct instantiator { - static constexpr pair haystack[] = {{0, 42}, {2, 42}, {4, 42}}; + static constexpr pair haystack[] = {{0, 42}, {1, 42}, {2, 42}, {4, 42}}; static constexpr pair short_haystack[] = {{4, 42}}; - static constexpr pair needle[] = {{13, 2}, {13, 4}}; + static constexpr pair long_needle[] = {{13, 1}, {13, 2}, {13, 4}}; + static constexpr pair short_needle[] = {{13, 2}, {13, 4}}; static constexpr pair wrong_needle[] = {{13, 2}, {13, 3}}; template @@ -31,7 +33,13 @@ struct instantiator { using ranges::ends_with, ranges::equal_to; { - const same_as auto match = ends_with(In1{haystack}, In2{needle}, equal_to{}, get_first, get_second); + const same_as auto match = + ends_with(In1{haystack}, In2{long_needle}, equal_to{}, get_first, get_second); + assert(match); + } + { + const same_as auto match = + ends_with(In1{haystack}, In2{short_needle}, equal_to{}, get_first, get_second); assert(match); } { @@ -41,9 +49,14 @@ struct instantiator { } { const same_as auto match = - ends_with(In1{short_haystack}, In2{needle}, equal_to{}, get_first, get_second); + ends_with(In1{short_haystack}, In2{short_needle}, equal_to{}, get_first, get_second); assert(!match); } + { + const same_as auto match = + ends_with(In1{haystack}, In2{span, 0>{}}, equal_to{}, get_first, get_second); + assert(match); + } } template @@ -52,12 +65,18 @@ struct instantiator { { In1 h{haystack}; - In2 n{needle}; + In2 n{long_needle}; + const same_as 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{short_needle}; const same_as 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}; @@ -65,14 +84,20 @@ struct instantiator { 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}; + In2 n{short_needle}; const same_as 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{span, 0>{}}; + const same_as auto match = + ends_with(begin(h), end(h), begin(n), end(n), equal_to{}, get_first, get_second); + assert(match); + } } template diff --git a/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp b/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp index 094d2df0b1b..b8f545ed522 100644 --- a/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp +++ b/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp @@ -19,7 +19,7 @@ struct instantiator { template static constexpr void test() { - using ranges::begin, ranges::end, ranges::starts_with; + using ranges::begin, ranges::end, ranges::equal_to, ranges::starts_with; // Validate range overload { From 42c9471b6b04592b09098ebe357f042a0f202084 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 19 Oct 2021 00:00:49 -0700 Subject: [PATCH 19/19] [[nonodiscard]] --- stl/inc/algorithm | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 87cab901404..12077672aa6 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -2334,7 +2334,7 @@ namespace ranges { struct _Not_a_difference {}; template - [[nodiscard]] static constexpr auto _Distance_helper(const _It& _First, const _Se& _Last) { + _NODISCARD static constexpr auto _Distance_helper(const _It& _First, const _Se& _Last) { _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); if constexpr (sized_sentinel_for<_Se, _It>) { return _Last - _First; @@ -2344,7 +2344,7 @@ namespace ranges { } template - [[nodiscard]] static constexpr auto _Distance_helper(_Rng&& _Range) { + _NODISCARD static constexpr auto _Distance_helper(_Rng&& _Range) { _STL_INTERNAL_STATIC_ASSERT(range<_Rng>); if constexpr (sized_range<_Rng>) { return _RANGES distance(_Range); @@ -2354,7 +2354,7 @@ namespace ranges { } template - [[nodiscard]] static constexpr bool _Match_backwards( + _NODISCARD static constexpr bool _Match_backwards( const _Ty _First1, _It1 _Last1, const _It2 _First2, _It2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { _STL_INTERNAL_STATIC_ASSERT(same_as<_Ty, _It1> || same_as<_Ty, unreachable_sentinel_t>); _STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It1>); @@ -2379,7 +2379,7 @@ namespace ranges { } template - [[nodiscard]] static constexpr bool _Ends_with_sized_needle(_It1 _First1, _Se1 _Last1, _Diff1 _Count1, + _NODISCARD static constexpr bool _Ends_with_sized_needle(_It1 _First1, _Se1 _Last1, _Diff1 _Count1, _It2 _First2, iter_difference_t<_It2> _Count2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1> && sentinel_for<_Se1, _It1>); _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It1> || same_as<_Diff1, iter_difference_t<_It1>>); @@ -2430,7 +2430,7 @@ namespace ranges { } template - [[nodiscard]] static constexpr bool _Ends_with_unsized_needle( + _NODISCARD static constexpr bool _Ends_with_unsized_needle( _It1 _First1, _Se1 _Last1, _Diff1 _Count1, _It2 _First2, _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1> && sentinel_for<_Se1, _It1>); _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It1> || same_as<_Diff1, iter_difference_t<_It1>>); @@ -2470,7 +2470,7 @@ namespace ranges { template - [[nodiscard]] static constexpr bool _Ends_with_impl(_It1 _First1, _Se1 _Last1, _Diff1 _Count1, _It2 _First2, + _NODISCARD static constexpr bool _Ends_with_impl(_It1 _First1, _Se1 _Last1, _Diff1 _Count1, _It2 _First2, _Se2 _Last2, _Diff2 _Count2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); _STL_INTERNAL_STATIC_ASSERT(_Is_any_of_v<_Diff1, iter_difference_t<_It1>, _Not_a_difference>);