From 6b2c1d70aafdc4f18ac5f49d46399d2137a2e92e Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 11 Jun 2020 15:46:19 -0700 Subject: [PATCH 1/9] Implement ranges::search Implement helper `ranges::_Rewrap_subrange` to use in `ranges::search` and `ranges::find_end`. Perma-workaround VSO-1141368, a failure in MSVC to properly handle lifetime extension when binding a temporary returned from a non-static member function to a reference in a constexpr context. (This seems to also affect structured bindings which look similar "under the covers".) Partially addresses #39. --- stl/inc/algorithm | 298 ++++++++++++------ stl/inc/xutility | 27 +- tests/std/test.lst | 1 + .../P0896R4_ranges_alg_find_end/test.cpp | 10 + .../tests/P0896R4_ranges_alg_search/env.lst | 4 + .../tests/P0896R4_ranges_alg_search/test.cpp | 170 ++++++++++ 6 files changed, 416 insertions(+), 94 deletions(-) create mode 100644 tests/std/tests/P0896R4_ranges_alg_search/env.lst create mode 100644 tests/std/tests/P0896R4_ranges_alg_search/test.cpp diff --git a/stl/inc/algorithm b/stl/inc/algorithm index b6a009c14d7..5b890ac5992 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -220,6 +220,23 @@ namespace ranges { // clang-format on }; + // FUNCTION TEMPLATE _Rewrap_subrange + template + constexpr _Result _Rewrap_subrange( + [[maybe_unused]] _Wrapped& _First, [[maybe_unused]] subrange<_Unwrapped>&& _UResult) { + // conditionally computes a wrapped subrange from a wrapped iterator and unwrapped subrange + _STL_INTERNAL_STATIC_ASSERT(is_same_v<_Unwrapped_t<_Wrapped>, _Unwrapped>); + if constexpr (is_same_v<_Result, dangling>) { + return dangling{}; + } else { + _STL_INTERNAL_STATIC_ASSERT(is_same_v<_Result, subrange<_Wrapped>>); + auto _Last = _First; + _Seek_wrapped(_First, _UResult.begin()); + _Seek_wrapped(_Last, _UResult.end()); + return _Result{_STD move(_First), _STD move(_Last)}; + } + } + #ifdef __clang__ #pragma clang diagnostic pop #endif // __clang__ @@ -1531,6 +1548,140 @@ _NODISCARD _CONSTEXPR20 _FwdItHaystack search( return _Search(_First, _Last).first; } +#ifdef __cpp_lib_concepts +namespace ranges { + // FUNCTION TEMPLATE _Equal_rev_pred + // clang-format off + template + concept _Equal_rev_pred_can_memcmp = is_same_v<_Pj1, identity> && is_same_v<_Pj2, identity> + && is_same_v<_Se2, _It2> && _Equal_memcmp_is_safe<_It1, _It2, _Pr>; + + template _Se2, class _Pr, class _Pj1, class _Pj2> + requires indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2> + _NODISCARD constexpr pair _Equal_rev_pred( + _It1 _First1, _It2 _First2, const _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + // compare [_First1, ...) to [_First2, _Last2) + constexpr bool _Optimize = _Equal_rev_pred_can_memcmp<_It1, _It2, _Se2, _Pr, _Pj1, _Pj2>; + if constexpr (_Optimize) { + if (!_STD is_constant_evaluated()) { + const auto _First1_ch = reinterpret_cast(_STD to_address(_First1)); + const auto _First2_ch = reinterpret_cast(_STD to_address(_First2)); + const auto _Count = + static_cast(reinterpret_cast(_STD to_address(_Last2)) - _First2_ch); + return {_CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0, _First1 + (_Last2 - _First2)}; + } + } + + for (; _First2 != _Last2; ++_First1, (void) ++_First2) { + if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) { + return {false, _It1{}}; + } + } + + return {true, _STD move(_First1)}; + } + // clang-format on + + // VARIABLE ranges::search + class _Search_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se1, forward_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 subrange<_It1> operator()(_It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, + _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + _Adl_verify_range(_First1, _Last1); + _Adl_verify_range(_First2, _Last2); + 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 (sized_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2>) { + const auto _Count1 = _ULast1 - _UFirst1; + const auto _Count2 = _ULast2 - _UFirst2; + auto _UResult = + _Sized_ranges(_STD move(_UFirst1), _STD move(_ULast1), _Count1, _STD move(_UFirst2), + _STD move(_ULast2), _Count2, _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Rewrap_subrange>(_First1, _STD move(_UResult)); + } else { + auto _UResult = _Unsized_ranges(_STD move(_UFirst1), _STD move(_ULast1), _STD move(_UFirst2), + _STD move(_ULast2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Rewrap_subrange>(_First1, _STD move(_UResult)); + } + } + + template + requires indirectly_comparable, iterator_t<_Rng2>, _Pr, _Pj1, _Pj2> + _NODISCARD constexpr borrowed_subrange_t<_Rng1> operator()( + _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + auto _First1 = _RANGES begin(_Range1); + + if constexpr (sized_range<_Rng1> && sized_range<_Rng2>) { + const auto _Count1 = _RANGES distance(_Range1); + const auto _Count2 = _RANGES distance(_Range2); + auto _UResult = _Sized_ranges(_Get_unwrapped(_First1), _Get_unwrapped(_RANGES end(_Range1)), _Count1, + _Get_unwrapped(_RANGES begin(_Range2)), _Get_unwrapped(_RANGES end(_Range2)), _Count2, + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Rewrap_subrange>(_First1, _STD move(_UResult)); + } else { + auto _UResult = _Unsized_ranges(_Get_unwrapped(_First1), _Get_unwrapped(_RANGES end(_Range1)), + _Get_unwrapped(_RANGES begin(_Range2)), _Get_unwrapped(_RANGES end(_Range2)), + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Rewrap_subrange>(_First1, _STD move(_UResult)); + } + } + // clang-format on + + private: + template + _NODISCARD static constexpr subrange<_It1> _Sized_ranges(_It1 _First1, const _Se1 _Last1, + iter_difference_t<_It1> _Count1, _It2 _First2, const _Se2 _Last2, const iter_difference_t<_It2> _Count2, + _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_CHECK(_RANGES distance(_First1, _Last1) == _Count1); + _STL_INTERNAL_CHECK(_RANGES distance(_First2, _Last2) == _Count2); + + for (; _Count1 >= _Count2; ++_First1, (void) --_Count1) { + auto [_Match, _Mid1] = _RANGES _Equal_rev_pred(_First1, _First2, _Last2, _Pred, _Proj1, _Proj2); + if (_Match) { + return {_STD move(_First1), _STD move(_Mid1)}; + } + } + + _First1 = _Find_last_iterator(_First1, _Last1, _Count1); + return {_First1, _First1}; + } + + template + _NODISCARD static constexpr subrange<_It1> _Unsized_ranges( + _It1 _First1, const _Se1 _Last1, _It2 _First2, const _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + for (;; ++_First1) { + auto _Mid1 = _First1; + for (auto _Mid2 = _First2;; ++_Mid1, (void) ++_Mid2) { + if (_Mid2 == _Last2) { // match! + return {_STD move(_First1), _STD move(_Mid1)}; + } + + if (_Mid1 == _Last1) { // not enough haystack left to find a match + return {_Mid1, _Mid1}; + } + + if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_Mid1), _STD invoke(_Proj2, *_Mid2))) { // mismatch + break; + } + } + } + } + }; // namespace ranges + + inline constexpr _Search_fn search{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE search_n #if _HAS_IF_CONSTEXPR template @@ -1904,47 +2055,21 @@ namespace ranges { // VARIABLE ranges::find_end class _Find_end_fn : private _Not_quite_object { private: - template - _NODISCARD static constexpr bool _Equal_rev_pred_unchecked( - _It1 _First1, _It2 _First2, const _It2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { - // compare [_First1, ...) to [_First2, _Last2) - constexpr bool _Both_identity = is_same_v<_Pj1, identity> && is_same_v<_Pj2, identity>; - // Performance note: teach _Equal_memcmp_is_safe about contiguous iterators and ranges::equal_to - if constexpr (_Both_identity && _Equal_memcmp_is_safe<_It1, _It2, _Pr>) { - if (!_STD is_constant_evaluated()) { - const auto _First1_ch = reinterpret_cast(_STD to_address(_First1)); - const auto _First2_ch = reinterpret_cast(_STD to_address(_First2)); - const auto _Count = - static_cast(reinterpret_cast(_STD to_address(_Last2)) - _First2_ch); - return _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; - } - } - - for (; _First2 != _Last2; ++_First1, (void) ++_First2) { - if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) { - return false; - } - } - - return true; - } - template _NODISCARD static constexpr subrange<_It1> _Random_access_sized_ranges(_It1 _First1, const iter_difference_t<_It1> _Count1, _It2 _First2, const iter_difference_t<_It2> _Count2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { if (_Count2 > 0 && _Count2 <= _Count1) { - const auto _UFirst1 = _Get_unwrapped(_First1); - const auto _UFirst2 = _Get_unwrapped(_First2); const auto _Count2_as1 = static_cast>(_Count2); - for (auto _UCandidate = _UFirst1 + (_Count1 - _Count2_as1);; --_UCandidate) { - if (_Equal_rev_pred_unchecked(_UCandidate, _UFirst2, _UFirst2 + _Count2, _Pred, _Proj1, _Proj2)) { - _Seek_wrapped(_First1, _UCandidate); - return {_First1, _First1 + _Count2_as1}; + for (auto _Candidate = _First1 + (_Count1 - _Count2_as1);; --_Candidate) { + auto [_Match, _Mid1] = + _Equal_rev_pred(_Candidate, _First2, _First2 + _Count2, _Pred, _Proj1, _Proj2); + if (_Match) { + return {_STD move(_Candidate), _STD move(_Mid1)}; } - if (_UCandidate == _UFirst1) { + if (_Candidate == _First1) { break; } } @@ -1957,28 +2082,22 @@ namespace ranges { template _NODISCARD static constexpr subrange<_It1> _Bidi_common_ranges( _It1 _First1, _It1 _Last1, _It2 _First2, _It2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { - auto _UFirst1 = _Get_unwrapped(_First1); - const auto _ULast1 = _Get_unwrapped(_Last1); - const auto _UFirst2 = _Get_unwrapped(_First2); - const auto _ULast2 = _Get_unwrapped(_Last2); - for (auto _UCandidate = _ULast1;; --_UCandidate) { // try a match at _UCandidate - auto _UNext1 = _UCandidate; - auto _UNext2 = _ULast2; - for (;;) { // test if [_UFirst2, _ULast2) is a suffix of [_UFirst1, _UCandidate) - if (_UFirst2 == _UNext2) { // match found - _Seek_wrapped(_First1, _UNext1); - _Seek_wrapped(_Last1, _UCandidate); + for (auto _Candidate = _Last1;; --_Candidate) { // try a match at _Candidate + auto _Next1 = _Candidate; + auto _Next2 = _Last2; + for (;;) { // test if [_First2, _Last2) is a suffix of [_First1, _Candidate) + if (_First2 == _Next2) { // match found return {_First1, _Last1}; } - if (_UFirst1 == _UNext1) { - // [_UFirst1, _UCandidate) is shorter than [_UFirst2, _ULast2), remaining candidates nonviable + if (_First1 == _Next1) { + // [_First1, _Candidate) is shorter than [_First2, _Last2); remaining candidates nonviable return {_Last1, _Last1}; } - --_UNext1; - --_UNext2; - if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_UNext1), _STD invoke(_Proj2, *_UNext2))) { + --_Next1; + --_Next2; + if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_Next1), _STD invoke(_Proj2, *_Next2))) { break; // mismatch } } @@ -1988,48 +2107,35 @@ namespace ranges { template _NODISCARD static constexpr subrange<_It1> _Forward_ranges( _It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { - auto _UFirst1 = _Get_unwrapped(_First1); - const auto _ULast1 = _Get_unwrapped(_Last1); - const auto _UFirst2 = _Get_unwrapped(_First2); - const auto _ULast2 = _Get_unwrapped(_Last2); - decltype(_UFirst1) _UMatch_first; - decltype(_UFirst1) _UMatch_last; + subrange<_It1> _Match{}; bool _Found = false; - for (;;) { // try a match at _UFirst1 - auto _UNext1 = _UFirst1; - auto _UNext2 = _UFirst2; - for (;;) { // test if [_UFirst2, _ULast2) is a prefix of [_UFirst1, _ULast1) - const bool _End_of_needle = _UNext2 == _ULast2; + for (;; ++_First1) { // try a match at _First1 + auto _Next1 = _First1; + auto _Next2 = _First2; + for (;; ++_Next1, (void) ++_Next2) { // test if [_First2, _Last2) is a prefix of [_First1, _Last1) + const bool _End_of_needle = _Next2 == _Last2; if (_End_of_needle) { // match candidate found - _UMatch_first = _UFirst1; - _UMatch_last = _UNext1; - _Found = true; + _Match = subrange{_First1, _Next1}; + _Found = true; } - if (_UNext1 == _ULast1) { // haystack exhausted - _Seek_wrapped(_First1, _Found ? _UMatch_first : _UFirst1); - auto _Mid1 = _First1; - if (_Found) { - _Seek_wrapped(_Mid1, _UMatch_last); + if (_Next1 == _Last1) { // haystack exhausted + if (!_Found) { + _Match = subrange{_Next1, _Next1}; } - return {_First1, _Mid1}; + return _Match; } if (_End_of_needle) { break; // end of match found, go to the next candidate } - if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_UNext1), _STD invoke(_Proj2, *_UNext2))) { + if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_Next1), _STD invoke(_Proj2, *_Next2))) { break; // mismatch, go to the next candidate } - - ++_UNext1; - ++_UNext2; } - - ++_UFirst1; } } @@ -2044,17 +2150,27 @@ namespace ranges { _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { _Adl_verify_range(_First1, _Last1); _Adl_verify_range(_First2, _Last2); + auto _UFirst1 = _Get_unwrapped(_First1); + auto _ULast1 = _Get_unwrapped(_Last1); + auto _UFirst2 = _Get_unwrapped(_First2); + auto _ULast2 = _Get_unwrapped(_Last2); + if constexpr (random_access_iterator<_It1> && sized_sentinel_for<_Se1, _It1> && random_access_iterator<_It2> && sized_sentinel_for<_Se2, _It2>) { - return _Random_access_sized_ranges(_First1, _Last1 - _First1, _First2, _Last2 - _First2, + const auto _Count1 = _ULast1 - _UFirst1; + const auto _Count2 = _ULast2 - _UFirst2; + auto _UResult = _Random_access_sized_ranges(_STD move(_UFirst1), _Count1, _STD move(_UFirst2), _Count2, _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Rewrap_subrange>(_First1, _STD move(_UResult)); } else if constexpr (bidirectional_iterator<_It1> && is_same_v<_It1, _Se1> && bidirectional_iterator<_It2> && is_same_v<_It2, _Se2>) { - return _Bidi_common_ranges(_First1, _Last1, _First2, _Last2, - _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + auto _UResult = _Bidi_common_ranges(_STD move(_UFirst1), _STD move(_ULast1), _STD move(_UFirst2), + _STD move(_ULast2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Rewrap_subrange>(_First1, _STD move(_UResult)); } else { - return _Forward_ranges(_First1, _Last1, _First2, _Last2, - _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + auto _UResult = _Forward_ranges(_STD move(_UFirst1), _STD move(_ULast1), _STD move(_UFirst2), + _STD move(_ULast2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Rewrap_subrange>(_First1, _STD move(_UResult)); } } @@ -2065,21 +2181,27 @@ namespace ranges { _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { if constexpr (random_access_range<_Rng1> && sized_range<_Rng1> && random_access_range<_Rng2> && sized_range<_Rng2>) { - return _Random_access_sized_ranges( - _RANGES begin(_Range1), _RANGES distance(_Range1), - _RANGES begin(_Range2), _RANGES distance(_Range2), + auto _First1 = _RANGES begin(_Range1); + auto _UResult = _Random_access_sized_ranges( + _Get_unwrapped(_First1), _RANGES distance(_Range1), + _Get_unwrapped(_RANGES begin(_Range2)), _RANGES distance(_Range2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Rewrap_subrange>(_First1, _STD move(_UResult)); } else if constexpr (bidirectional_range<_Rng1> && common_range<_Rng1> && bidirectional_range<_Rng2> && common_range<_Rng2>) { - return _Bidi_common_ranges( - _RANGES begin(_Range1), _RANGES end(_Range1), - _RANGES begin(_Range2), _RANGES end(_Range2), + auto _First1 = _RANGES begin(_Range1); + auto _UResult = _Bidi_common_ranges( + _Get_unwrapped(_First1), _Get_unwrapped(_RANGES end(_Range1)), + _Get_unwrapped(_RANGES begin(_Range2)), _Get_unwrapped(_RANGES end(_Range2)), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Rewrap_subrange>(_First1, _STD move(_UResult)); } else { - return _Forward_ranges( - _RANGES begin(_Range1), _RANGES end(_Range1), - _RANGES begin(_Range2), _RANGES end(_Range2), + auto _First1 = _RANGES begin(_Range1); + auto _UResult = _Forward_ranges( + _Get_unwrapped(_First1), _Get_unwrapped(_RANGES end(_Range1)), + _Get_unwrapped(_RANGES begin(_Range2)), _Get_unwrapped(_RANGES end(_Range2)), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Rewrap_subrange>(_First1, _STD move(_UResult)); } } // clang-format on diff --git a/stl/inc/xutility b/stl/inc/xutility index e3ad16acb42..91027b553cc 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -1645,7 +1645,7 @@ _CONSTEXPR17 void advance(_InIt& _Where, _Diff _Off) { // increment iterator by _STL_ASSERT(_Off >= 0, "negative advance of non-bidirectional iterator"); } - auto&& _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off); + decltype(auto) _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off); constexpr bool _Need_rewrap = !is_reference_v; if constexpr (is_signed_v<_Diff> && _Is_bidi_iter_v<_InIt>) { @@ -1669,7 +1669,7 @@ _CONSTEXPR17 void _Advance1(_InIt& _Where, _Diff _Off, input_iterator_tag) { // increment iterator by offset, input iterators _STL_ASSERT(_Off >= 0, "negative advance of non-bidirectional iterator"); - auto&& _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off); + decltype(auto) _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off); constexpr bool _Need_rewrap = !is_reference_v; for (; 0 < _Off; --_Off) { @@ -1684,7 +1684,7 @@ _CONSTEXPR17 void _Advance1(_InIt& _Where, _Diff _Off, input_iterator_tag) { template _CONSTEXPR17 void _Advance1(_BidIt& _Where, _Diff _Off, bidirectional_iterator_tag) { // increment iterator by offset, bidirectional iterators - auto&& _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off); + decltype(auto) _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off); constexpr bool _Need_rewrap = !is_reference_v; for (; 0 < _Off; --_Off) { @@ -2972,7 +2972,7 @@ namespace ranges { _STL_ASSERT(_Off >= 0, "negative advance of non-bidirectional iterator"); } - auto&& _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off); + decltype(auto) _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off); constexpr bool _Need_rewrap = !is_reference_v; if constexpr (bidirectional_iterator<_It>) { @@ -3000,9 +3000,9 @@ namespace ranges { } else { _Adl_verify_range(_Where, _Last); - auto&& _UWhere = _Get_unwrapped(static_cast<_It&&>(_Where)); + decltype(auto) _UWhere = _Get_unwrapped(static_cast<_It&&>(_Where)); constexpr bool _Need_rewrap = !is_reference_v(_Where)))>; - auto&& _ULast = _Get_unwrapped(static_cast<_Se&&>(_Last)); + decltype(auto) _ULast = _Get_unwrapped(static_cast<_Se&&>(_Last)); while (_UWhere != _ULast) { ++_UWhere; @@ -3148,6 +3148,21 @@ namespace ranges { // VARIABLE ranges::prev inline constexpr _Prev_fn prev{_Not_quite_object::_Construct_tag{}}; + // FUNCTION TEMPLATE _Find_last_iterator + template _Se> + _NODISCARD constexpr _It _Find_last_iterator( + const _It& _First, const _Se& _Last, const iter_difference_t<_It> _Count) { + // Find the iterator in [_First, _Last) (of length _Count) which equals _Last + _STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) == _Count); + auto _Result = _First; + if constexpr (assignable_from<_It&, _Se>) { + _RANGES advance(_Result, _Last); + } else { + _RANGES advance(_Result, _Count); + } + return _Result; + } + // STRUCT ranges::equal_to struct equal_to { // clang-format off diff --git a/tests/std/test.lst b/tests/std/test.lst index cced084e677..89587d18f8b 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -251,6 +251,7 @@ tests\P0896R4_ranges_alg_for_each tests\P0896R4_ranges_alg_for_each_n tests\P0896R4_ranges_alg_mismatch tests\P0896R4_ranges_alg_none_of +tests\P0896R4_ranges_alg_search tests\P0896R4_ranges_iterator_machinery tests\P0896R4_ranges_range_machinery tests\P0896R4_ranges_subrange diff --git a/tests/std/tests/P0896R4_ranges_alg_find_end/test.cpp b/tests/std/tests/P0896R4_ranges_alg_find_end/test.cpp index b0d2b1ff0ac..04226ce8420 100644 --- a/tests/std/tests/P0896R4_ranges_alg_find_end/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_find_end/test.cpp @@ -58,6 +58,16 @@ constexpr void smoke_test() { assert(result.begin() == pairs.end()); assert(result.end() == pairs.end()); } + + { + // Validate the memcmp optimization + const int haystack[] = {1, 2, 3, 1, 2, 3, 1, 2, 3}; + const int needle[] = {1, 2, 3}; + const auto result = find_end(haystack, std::span{needle}); + STATIC_ASSERT(same_as>); + assert(result.begin() == haystack + 6); + assert(result.end() == haystack + 9); + } } int main() { diff --git a/tests/std/tests/P0896R4_ranges_alg_search/env.lst b/tests/std/tests/P0896R4_ranges_alg_search/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_search/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_search/test.cpp b/tests/std/tests/P0896R4_ranges_alg_search/test.cpp new file mode 100644 index 00000000000..15daa423ab2 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_search/test.cpp @@ -0,0 +1,170 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +// +#include + +using namespace std; + +// Validate dangling story +STATIC_ASSERT(same_as{}, borrowed{})), ranges::dangling>); +STATIC_ASSERT(same_as{}, borrowed{})), ranges::subrange>); + +struct instantiator { + using P = pair; + + static constexpr array pairs = { + P{0, 42}, P{1, 42}, P{2, 42}, P{3, 42}, P{4, 42}, P{5, 42}, P{6, 42}, P{7, 42}}; + static constexpr array not_pairs = {2, 3, 4}; + + template + static constexpr void call() { + const Fwd1 range1{pairs}; + const Fwd2 range2{not_pairs}; + + // defaulted predicate and projections + { + auto result = ranges::search(range1, range1); + STATIC_ASSERT(same_as>>); + assert(result.begin() == range1.begin()); + assert(result.end() == range1.end()); + } + { + auto result = + ranges::search(ranges::begin(range2), ranges::end(range2), ranges::begin(range2), ranges::end(range2)); + STATIC_ASSERT(same_as>>); + assert(result.begin() == range2.begin()); + assert(result.end() == range2.end()); + } + + // explicit predicate + { + auto result = ranges::search(range2, range2, ranges::equal_to{}); + STATIC_ASSERT(same_as>>); + assert(result.begin() == range2.begin()); + assert(result.end() == range2.end()); + } + { + auto result = ranges::search(ranges::begin(range1), ranges::end(range1), ranges::begin(range1), + ranges::end(range1), ranges::equal_to{}); + STATIC_ASSERT(same_as>>); + assert(result.begin() == range1.begin()); + assert(result.end() == range1.end()); + } + + // explicit predicate and one projection + { + auto result = ranges::search(range1, range2, ranges::equal_to{}, get_first); + STATIC_ASSERT(same_as>>); + assert(result.begin() == ranges::next(range1.begin(), 2)); + assert(result.end() == ranges::next(range1.begin(), 5)); + } + { + auto result = ranges::search(ranges::begin(range1), ranges::end(range1), ranges::begin(range2), + ranges::end(range2), ranges::equal_to{}, get_first); + STATIC_ASSERT(same_as>>); + assert(result.begin() == ranges::next(range1.begin(), 2)); + assert(result.end() == ranges::next(range1.begin(), 5)); + } + + // explicit predicate and two projections + constexpr auto plus1 = [](int x) { return x + 1; }; + { + auto result = ranges::search(range1, range2, ranges::equal_to{}, get_first, plus1); + STATIC_ASSERT(same_as>>); + assert(result.begin() == ranges::next(range1.begin(), 3)); + assert(result.end() == ranges::next(range1.begin(), 6)); + } + { + auto result = ranges::search(ranges::begin(range1), ranges::end(range1), ranges::begin(range2), + ranges::end(range2), ranges::equal_to{}, get_first, plus1); + STATIC_ASSERT(same_as>>); + assert(result.begin() == ranges::next(range1.begin(), 3)); + assert(result.end() == ranges::next(range1.begin(), 6)); + } + + // negative case + { + auto result = ranges::search(range2, range1, ranges::equal_to{}, plus1, get_first); + STATIC_ASSERT(same_as>>); + assert(result.empty()); + } + { + auto result = ranges::search(ranges::begin(range2), ranges::end(range2), ranges::begin(range1), + ranges::end(range1), ranges::equal_to{}, plus1, get_first); + STATIC_ASSERT(same_as>>); + assert(result.empty()); + } + } +}; + +using Elem1 = const pair; +using Elem2 = const int; + +#ifdef TEST_EVERYTHING +int main() { + STATIC_ASSERT((test_fwd_fwd(), true)); + test_fwd_fwd(); +} +#else // ^^^ test all range combinations // test only interesting range combos vvv +template +using fwd_test_range = test::range; +template +using random_test_range = test::range; + +constexpr bool run_tests() { + // All (except contiguous) proxy reference types, since the algorithm doesn't really care. + // Cases with only 1 range sized are not interesting + // common is interesting only in that it's necessary to trigger memcmp optimization + + using test::Common, test::Sized; + + // both forward non-common and 0-/2-sized + instantiator::call, fwd_test_range>(); + instantiator::call, fwd_test_range>(); + + // both random-access, varying common, and 0-/2-sized + instantiator::call, + random_test_range>(); + instantiator::call, + random_test_range>(); + instantiator::call, + random_test_range>(); + instantiator::call, + random_test_range>(); + instantiator::call, + random_test_range>(); + instantiator::call, + random_test_range>(); + instantiator::call, + random_test_range>(); + instantiator::call, + random_test_range>(); + + { + // Validate the memcmp optimization + const int haystack[] = {1, 2, 3, 1, 2, 3, 1, 2, 3}; + const int needle[] = {1, 2, 3}; + const auto result = ranges::search(span{haystack}, needle); + STATIC_ASSERT(same_as::iterator>>); + assert(to_address(result.begin()) == haystack + 0); + assert(to_address(result.end()) == haystack + 3); + } + + return true; +} + +int main() { + STATIC_ASSERT(run_tests()); + run_tests(); +} +#endif // TEST_EVERYTHING From 3a003b0ee25e16d8503e3543f7f8c7b61ad7180c Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Sun, 14 Jun 2020 19:40:12 -0700 Subject: [PATCH 2/9] Silly typo --- stl/inc/algorithm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 5b890ac5992..1cadfa92379 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -1676,7 +1676,7 @@ namespace ranges { } } } - }; // namespace ranges + }; inline constexpr _Search_fn search{_Not_quite_object::_Construct_tag{}}; } // namespace ranges From 4a54963ae22aca0a0aef8917c7280961e8f23cd6 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 15 Jun 2020 07:56:24 -0700 Subject: [PATCH 3/9] Review comment --- stl/inc/algorithm | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 1cadfa92379..e6b980d0e7e 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -2179,9 +2179,9 @@ namespace ranges { requires indirectly_comparable, iterator_t<_Rng2>, _Pr, _Pj1, _Pj2> _NODISCARD constexpr borrowed_subrange_t<_Rng1> operator()( _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + auto _First1 = _RANGES begin(_Range1); if constexpr (random_access_range<_Rng1> && sized_range<_Rng1> && random_access_range<_Rng2> && sized_range<_Rng2>) { - auto _First1 = _RANGES begin(_Range1); auto _UResult = _Random_access_sized_ranges( _Get_unwrapped(_First1), _RANGES distance(_Range1), _Get_unwrapped(_RANGES begin(_Range2)), _RANGES distance(_Range2), @@ -2189,14 +2189,12 @@ namespace ranges { return _Rewrap_subrange>(_First1, _STD move(_UResult)); } else if constexpr (bidirectional_range<_Rng1> && common_range<_Rng1> && bidirectional_range<_Rng2> && common_range<_Rng2>) { - auto _First1 = _RANGES begin(_Range1); auto _UResult = _Bidi_common_ranges( _Get_unwrapped(_First1), _Get_unwrapped(_RANGES end(_Range1)), _Get_unwrapped(_RANGES begin(_Range2)), _Get_unwrapped(_RANGES end(_Range2)), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); return _Rewrap_subrange>(_First1, _STD move(_UResult)); } else { - auto _First1 = _RANGES begin(_Range1); auto _UResult = _Forward_ranges( _Get_unwrapped(_First1), _Get_unwrapped(_RANGES end(_Range1)), _Get_unwrapped(_RANGES begin(_Range2)), _Get_unwrapped(_RANGES end(_Range2)), From f2c61e639b7e2491a6c1508ff6aa87b9618fefa4 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 15 Jun 2020 21:02:19 -0700 Subject: [PATCH 4/9] Don't constexpr TEST_EVERYTHING --- tests/std/tests/P0896R4_ranges_alg_search/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P0896R4_ranges_alg_search/test.cpp b/tests/std/tests/P0896R4_ranges_alg_search/test.cpp index 15daa423ab2..4e9374e68ff 100644 --- a/tests/std/tests/P0896R4_ranges_alg_search/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_search/test.cpp @@ -110,7 +110,7 @@ using Elem2 = const int; #ifdef TEST_EVERYTHING int main() { - STATIC_ASSERT((test_fwd_fwd(), true)); + // No constepxr test here; this test_fwd_fwd call exceeds the maximum number of steps in a constexpr computation test_fwd_fwd(); } #else // ^^^ test all range combinations // test only interesting range combos vvv From c6508ef17228b9863dd3998f83ae0ddca388ca66 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 18 Jun 2020 13:53:40 -0700 Subject: [PATCH 5/9] Review comments --- stl/inc/algorithm | 33 +++++++++++-------- stl/inc/xutility | 12 ++++--- .../P0896R4_ranges_alg_find_end/test.cpp | 1 + .../tests/P0896R4_ranges_alg_search/test.cpp | 17 +++++----- 4 files changed, 36 insertions(+), 27 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index e6b980d0e7e..aa0802d338d 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -222,7 +222,7 @@ namespace ranges { // FUNCTION TEMPLATE _Rewrap_subrange template - constexpr _Result _Rewrap_subrange( + _NODISCARD constexpr _Result _Rewrap_subrange( [[maybe_unused]] _Wrapped& _First, [[maybe_unused]] subrange<_Unwrapped>&& _UResult) { // conditionally computes a wrapped subrange from a wrapped iterator and unwrapped subrange _STL_INTERNAL_STATIC_ASSERT(is_same_v<_Unwrapped_t<_Wrapped>, _Unwrapped>); @@ -1560,7 +1560,8 @@ namespace ranges { requires indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2> _NODISCARD constexpr pair _Equal_rev_pred( _It1 _First1, _It2 _First2, const _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { - // compare [_First1, ...) to [_First2, _Last2) + // Returns {true, _First1 + (_Last2 - _First2)} if [_First1, ...) equals [_First2, _Last2), and {false, {}} + // otherwise. constexpr bool _Optimize = _Equal_rev_pred_can_memcmp<_It1, _It2, _Se2, _Pr, _Pj1, _Pj2>; if constexpr (_Optimize) { if (!_STD is_constant_evaluated()) { @@ -1568,7 +1569,13 @@ namespace ranges { const auto _First2_ch = reinterpret_cast(_STD to_address(_First2)); const auto _Count = static_cast(reinterpret_cast(_STD to_address(_Last2)) - _First2_ch); - return {_CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0, _First1 + (_Last2 - _First2)}; + const bool _Eq = _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; + if (_Eq) { + _First1 += (_Last2 - _First2); + return {true, _STD move(_First1)}; + } else { + return {false, _It1{}}; + } } } @@ -1604,11 +1611,11 @@ namespace ranges { const auto _Count1 = _ULast1 - _UFirst1; const auto _Count2 = _ULast2 - _UFirst2; auto _UResult = - _Sized_ranges(_STD move(_UFirst1), _STD move(_ULast1), _Count1, _STD move(_UFirst2), + _Search_sized(_STD move(_UFirst1), _STD move(_ULast1), _Count1, _STD move(_UFirst2), _STD move(_ULast2), _Count2, _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); return _Rewrap_subrange>(_First1, _STD move(_UResult)); } else { - auto _UResult = _Unsized_ranges(_STD move(_UFirst1), _STD move(_ULast1), _STD move(_UFirst2), + auto _UResult = _Search_unsized(_STD move(_UFirst1), _STD move(_ULast1), _STD move(_UFirst2), _STD move(_ULast2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); return _Rewrap_subrange>(_First1, _STD move(_UResult)); } @@ -1624,12 +1631,12 @@ namespace ranges { if constexpr (sized_range<_Rng1> && sized_range<_Rng2>) { const auto _Count1 = _RANGES distance(_Range1); const auto _Count2 = _RANGES distance(_Range2); - auto _UResult = _Sized_ranges(_Get_unwrapped(_First1), _Get_unwrapped(_RANGES end(_Range1)), _Count1, + auto _UResult = _Search_sized(_Get_unwrapped(_First1), _Get_unwrapped(_RANGES end(_Range1)), _Count1, _Get_unwrapped(_RANGES begin(_Range2)), _Get_unwrapped(_RANGES end(_Range2)), _Count2, _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); return _Rewrap_subrange>(_First1, _STD move(_UResult)); } else { - auto _UResult = _Unsized_ranges(_Get_unwrapped(_First1), _Get_unwrapped(_RANGES end(_Range1)), + auto _UResult = _Search_unsized(_Get_unwrapped(_First1), _Get_unwrapped(_RANGES end(_Range1)), _Get_unwrapped(_RANGES begin(_Range2)), _Get_unwrapped(_RANGES end(_Range2)), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); return _Rewrap_subrange>(_First1, _STD move(_UResult)); @@ -1639,7 +1646,7 @@ namespace ranges { private: template - _NODISCARD static constexpr subrange<_It1> _Sized_ranges(_It1 _First1, const _Se1 _Last1, + _NODISCARD static constexpr subrange<_It1> _Search_sized(_It1 _First1, const _Se1 _Last1, iter_difference_t<_It1> _Count1, _It2 _First2, const _Se2 _Last2, const iter_difference_t<_It2> _Count2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { _STL_INTERNAL_CHECK(_RANGES distance(_First1, _Last1) == _Count1); @@ -1657,12 +1664,12 @@ namespace ranges { } template - _NODISCARD static constexpr subrange<_It1> _Unsized_ranges( + _NODISCARD static constexpr subrange<_It1> _Search_unsized( _It1 _First1, const _Se1 _Last1, _It2 _First2, const _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { for (;; ++_First1) { auto _Mid1 = _First1; for (auto _Mid2 = _First2;; ++_Mid1, (void) ++_Mid2) { - if (_Mid2 == _Last2) { // match! + if (_Mid2 == _Last2) { // match return {_STD move(_First1), _STD move(_Mid1)}; } @@ -2081,13 +2088,13 @@ namespace ranges { template _NODISCARD static constexpr subrange<_It1> _Bidi_common_ranges( - _It1 _First1, _It1 _Last1, _It2 _First2, _It2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _It1 _First1, _It1 _Last1, _It2 _First2, const _It2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { for (auto _Candidate = _Last1;; --_Candidate) { // try a match at _Candidate auto _Next1 = _Candidate; auto _Next2 = _Last2; for (;;) { // test if [_First2, _Last2) is a suffix of [_First1, _Candidate) if (_First2 == _Next2) { // match found - return {_First1, _Last1}; + return {_STD move(_First1), _STD move(_Last1)}; } if (_First1 == _Next1) { @@ -2106,7 +2113,7 @@ namespace ranges { template _NODISCARD static constexpr subrange<_It1> _Forward_ranges( - _It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _It1 _First1, const _Se1 _Last1, _It2 _First2, const _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { subrange<_It1> _Match{}; bool _Found = false; diff --git a/stl/inc/xutility b/stl/inc/xutility index 91027b553cc..224c9c26488 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -3154,13 +3154,15 @@ namespace ranges { const _It& _First, const _Se& _Last, const iter_difference_t<_It> _Count) { // Find the iterator in [_First, _Last) (of length _Count) which equals _Last _STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) == _Count); - auto _Result = _First; - if constexpr (assignable_from<_It&, _Se>) { - _RANGES advance(_Result, _Last); + (void) _First; + (void) _Last; + (void) _Count; + + if constexpr (is_same_v<_It, _Se>) { + return _Last; } else { - _RANGES advance(_Result, _Count); + return _RANGES next(_First, _Count); } - return _Result; } // STRUCT ranges::equal_to diff --git a/tests/std/tests/P0896R4_ranges_alg_find_end/test.cpp b/tests/std/tests/P0896R4_ranges_alg_find_end/test.cpp index 04226ce8420..a5e9b2716da 100644 --- a/tests/std/tests/P0896R4_ranges_alg_find_end/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_find_end/test.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include // #include diff --git a/tests/std/tests/P0896R4_ranges_alg_search/test.cpp b/tests/std/tests/P0896R4_ranges_alg_search/test.cpp index 4e9374e68ff..4c4fb63a091 100644 --- a/tests/std/tests/P0896R4_ranges_alg_search/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_search/test.cpp @@ -13,13 +13,13 @@ using namespace std; +using P = pair; + // Validate dangling story STATIC_ASSERT(same_as{}, borrowed{})), ranges::dangling>); STATIC_ASSERT(same_as{}, borrowed{})), ranges::subrange>); struct instantiator { - using P = pair; - static constexpr array pairs = { P{0, 42}, P{1, 42}, P{2, 42}, P{3, 42}, P{4, 42}, P{5, 42}, P{6, 42}, P{7, 42}}; static constexpr array not_pairs = {2, 3, 4}; @@ -105,12 +105,12 @@ struct instantiator { } }; -using Elem1 = const pair; +using Elem1 = const P; using Elem2 = const int; #ifdef TEST_EVERYTHING int main() { - // No constepxr test here; this test_fwd_fwd call exceeds the maximum number of steps in a constexpr computation + // No constexpr test here; the test_fwd_fwd call exceeds the maximum number of steps in a constexpr computation. test_fwd_fwd(); } #else // ^^^ test all range combinations // test only interesting range combos vvv @@ -122,17 +122,16 @@ using random_test_range = test::range; constexpr bool run_tests() { - // All (except contiguous) proxy reference types, since the algorithm doesn't really care. - // Cases with only 1 range sized are not interesting - // common is interesting only in that it's necessary to trigger memcmp optimization + // All (except contiguous) proxy reference types, since the algorithm doesn't really care. Cases with only 1 range + // sized are not interesting; common is interesting only in that it's necessary to trigger memcmp optimization. using test::Common, test::Sized; - // both forward non-common and 0-/2-sized + // both forward, non-common, and sized or unsized instantiator::call, fwd_test_range>(); instantiator::call, fwd_test_range>(); - // both random-access, varying common, and 0-/2-sized + // both random-access, and sized or unsized; all permutations of common instantiator::call, random_test_range>(); instantiator::call, From 5997b4661dfa4baf6cf345e67c3eea4d3cfb457c Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 18 Jun 2020 22:48:44 -0700 Subject: [PATCH 6/9] Document type requirements of internal functions ... with `_STL_INTERNAL_STATIC_ASSERT` when we don't want to pay the throughput cost of constraints. --- stl/inc/algorithm | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index aa0802d338d..a872b7e0b8a 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -1649,6 +1649,11 @@ namespace ranges { _NODISCARD static constexpr subrange<_It1> _Search_sized(_It1 _First1, const _Se1 _Last1, iter_difference_t<_It1> _Count1, _It2 _First2, const _Se2 _Last2, const iter_difference_t<_It2> _Count2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2>); _STL_INTERNAL_CHECK(_RANGES distance(_First1, _Last1) == _Count1); _STL_INTERNAL_CHECK(_RANGES distance(_First2, _Last2) == _Count2); @@ -1666,6 +1671,12 @@ namespace ranges { template _NODISCARD static constexpr subrange<_It1> _Search_unsized( _It1 _First1, const _Se1 _Last1, _It2 _First2, const _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2>); + for (;; ++_First1) { auto _Mid1 = _First1; for (auto _Mid2 = _First2;; ++_Mid1, (void) ++_Mid2) { @@ -2066,6 +2077,12 @@ namespace ranges { _NODISCARD static constexpr subrange<_It1> _Random_access_sized_ranges(_It1 _First1, const iter_difference_t<_It1> _Count1, _It2 _First2, const iter_difference_t<_It2> _Count2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2>); + // pre: _First1 + [0, _Count1) is a valid counted range + // pre: _First2 + [0, _Count2) is a valid counted range + if (_Count2 > 0 && _Count2 <= _Count1) { const auto _Count2_as1 = static_cast>(_Count2); @@ -2089,6 +2106,10 @@ namespace ranges { template _NODISCARD static constexpr subrange<_It1> _Bidi_common_ranges( _It1 _First1, _It1 _Last1, _It2 _First2, const _It2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _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 (auto _Candidate = _Last1;; --_Candidate) { // try a match at _Candidate auto _Next1 = _Candidate; auto _Next2 = _Last2; @@ -2114,6 +2135,12 @@ namespace ranges { template _NODISCARD static constexpr subrange<_It1> _Forward_ranges( _It1 _First1, const _Se1 _Last1, _It2 _First2, const _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2>); + subrange<_It1> _Match{}; bool _Found = false; From fce2cca1eed43a85adf65b6e7d1a6f5c40135df9 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Sat, 20 Jun 2020 09:29:46 -0700 Subject: [PATCH 7/9] Use _U(begin|end) --- stl/inc/algorithm | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index b3b4c1700c4..37e0e29b460 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -1665,13 +1665,13 @@ namespace ranges { if constexpr (sized_range<_Rng1> && sized_range<_Rng2>) { const auto _Count1 = _RANGES distance(_Range1); const auto _Count2 = _RANGES distance(_Range2); - auto _UResult = _Search_sized(_Get_unwrapped(_First1), _Get_unwrapped(_RANGES end(_Range1)), _Count1, - _Get_unwrapped(_RANGES begin(_Range2)), _Get_unwrapped(_RANGES end(_Range2)), _Count2, + auto _UResult = _Search_sized(_Get_unwrapped(_First1), _Uend(_Range1), _Count1, + _Ubegin(_Range2), _Uend(_Range2), _Count2, _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); return _Rewrap_subrange>(_First1, _STD move(_UResult)); } else { - auto _UResult = _Search_unsized(_Get_unwrapped(_First1), _Get_unwrapped(_RANGES end(_Range1)), - _Get_unwrapped(_RANGES begin(_Range2)), _Get_unwrapped(_RANGES end(_Range2)), + auto _UResult = _Search_unsized(_Get_unwrapped(_First1), _Uend(_Range1), + _Ubegin(_Range2), _Uend(_Range2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); return _Rewrap_subrange>(_First1, _STD move(_UResult)); } @@ -2250,23 +2250,17 @@ namespace ranges { auto _First1 = _RANGES begin(_Range1); if constexpr (random_access_range<_Rng1> && sized_range<_Rng1> && random_access_range<_Rng2> && sized_range<_Rng2>) { - auto _UResult = _Random_access_sized_ranges( - _Get_unwrapped(_First1), _RANGES distance(_Range1), - _Get_unwrapped(_RANGES begin(_Range2)), _RANGES distance(_Range2), - _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + auto _UResult = _Random_access_sized_ranges(_Get_unwrapped(_First1), _RANGES distance(_Range1), + _Ubegin(_Range2), _RANGES distance(_Range2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); return _Rewrap_subrange>(_First1, _STD move(_UResult)); } else if constexpr (bidirectional_range<_Rng1> && common_range<_Rng1> && bidirectional_range<_Rng2> && common_range<_Rng2>) { - auto _UResult = _Bidi_common_ranges( - _Get_unwrapped(_First1), _Get_unwrapped(_RANGES end(_Range1)), - _Get_unwrapped(_RANGES begin(_Range2)), _Get_unwrapped(_RANGES end(_Range2)), - _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + auto _UResult = _Bidi_common_ranges(_Get_unwrapped(_First1), _Uend(_Range1), + _Ubegin(_Range2), _Uend(_Range2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); return _Rewrap_subrange>(_First1, _STD move(_UResult)); } else { - auto _UResult = _Forward_ranges( - _Get_unwrapped(_First1), _Get_unwrapped(_RANGES end(_Range1)), - _Get_unwrapped(_RANGES begin(_Range2)), _Get_unwrapped(_RANGES end(_Range2)), - _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + auto _UResult = _Forward_ranges(_Get_unwrapped(_First1), _Uend(_Range1), + _Ubegin(_Range2), _Uend(_Range2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); return _Rewrap_subrange>(_First1, _STD move(_UResult)); } } From 4d3c78094bd2de631c06782a23ff3eb094a208f9 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 22 Jun 2020 16:55:31 -0700 Subject: [PATCH 8/9] cleanup unnecessary warning suppressions in _Find_last_iterator --- stl/inc/xutility | 4 ---- 1 file changed, 4 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index b2d74495b05..044f6cd4b7b 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -3267,10 +3267,6 @@ namespace ranges { const _It& _First, const _Se& _Last, const iter_difference_t<_It> _Count) { // Find the iterator in [_First, _Last) (of length _Count) which equals _Last _STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) == _Count); - (void) _First; - (void) _Last; - (void) _Count; - if constexpr (is_same_v<_It, _Se>) { return _Last; } else { From 31b6d07faf328099d09fef3c1022bdc68286ffd1 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 23 Jun 2020 13:35:02 -0700 Subject: [PATCH 9/9] Review comment --- .../tests/P0896R4_ranges_alg_search/test.cpp | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/tests/std/tests/P0896R4_ranges_alg_search/test.cpp b/tests/std/tests/P0896R4_ranges_alg_search/test.cpp index 4c4fb63a091..cc284d34379 100644 --- a/tests/std/tests/P0896R4_ranges_alg_search/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_search/test.cpp @@ -22,28 +22,34 @@ STATIC_ASSERT(same_as{}, borrowed{ struct instantiator { static constexpr array pairs = { P{0, 42}, P{1, 42}, P{2, 42}, P{3, 42}, P{4, 42}, P{5, 42}, P{6, 42}, P{7, 42}}; - static constexpr array not_pairs = {2, 3, 4}; + static constexpr array not_pairs = {2, 3, 4}; + static constexpr array neg_not_pairs = {-2, -3, -4}; template static constexpr void call() { const Fwd1 range1{pairs}; - const Fwd2 range2{not_pairs}; - - // defaulted predicate and projections - { - auto result = ranges::search(range1, range1); - STATIC_ASSERT(same_as>>); - assert(result.begin() == range1.begin()); - assert(result.end() == range1.end()); - } { - auto result = - ranges::search(ranges::begin(range2), ranges::end(range2), ranges::begin(range2), ranges::end(range2)); - STATIC_ASSERT(same_as>>); - assert(result.begin() == range2.begin()); - assert(result.end() == range2.end()); + const Fwd2 range2{not_pairs}; + + // defaulted predicate and projections + { + auto result = ranges::search(range1, range1); + STATIC_ASSERT(same_as>>); + assert(result.begin() == range1.begin()); + assert(result.end() == range1.end()); + } + { + auto result = ranges::search( + ranges::begin(range2), ranges::end(range2), ranges::begin(range2), ranges::end(range2)); + STATIC_ASSERT(same_as>>); + assert(result.begin() == range2.begin()); + assert(result.end() == range2.end()); + } } + const Fwd2 range2{neg_not_pairs}; + const auto pred = [](int x, int y) { return x == -y; }; + // explicit predicate { auto result = ranges::search(range2, range2, ranges::equal_to{}); @@ -61,30 +67,30 @@ struct instantiator { // explicit predicate and one projection { - auto result = ranges::search(range1, range2, ranges::equal_to{}, get_first); + auto result = ranges::search(range1, range2, pred, get_first); STATIC_ASSERT(same_as>>); assert(result.begin() == ranges::next(range1.begin(), 2)); assert(result.end() == ranges::next(range1.begin(), 5)); } { auto result = ranges::search(ranges::begin(range1), ranges::end(range1), ranges::begin(range2), - ranges::end(range2), ranges::equal_to{}, get_first); + ranges::end(range2), pred, get_first); STATIC_ASSERT(same_as>>); assert(result.begin() == ranges::next(range1.begin(), 2)); assert(result.end() == ranges::next(range1.begin(), 5)); } // explicit predicate and two projections - constexpr auto plus1 = [](int x) { return x + 1; }; + constexpr auto minus1 = [](int x) { return x - 1; }; { - auto result = ranges::search(range1, range2, ranges::equal_to{}, get_first, plus1); + auto result = ranges::search(range1, range2, pred, get_first, minus1); STATIC_ASSERT(same_as>>); assert(result.begin() == ranges::next(range1.begin(), 3)); assert(result.end() == ranges::next(range1.begin(), 6)); } { auto result = ranges::search(ranges::begin(range1), ranges::end(range1), ranges::begin(range2), - ranges::end(range2), ranges::equal_to{}, get_first, plus1); + ranges::end(range2), pred, get_first, minus1); STATIC_ASSERT(same_as>>); assert(result.begin() == ranges::next(range1.begin(), 3)); assert(result.end() == ranges::next(range1.begin(), 6)); @@ -92,13 +98,13 @@ struct instantiator { // negative case { - auto result = ranges::search(range2, range1, ranges::equal_to{}, plus1, get_first); + auto result = ranges::search(range2, range1, pred, minus1, get_first); STATIC_ASSERT(same_as>>); assert(result.empty()); } { auto result = ranges::search(ranges::begin(range2), ranges::end(range2), ranges::begin(range1), - ranges::end(range1), ranges::equal_to{}, plus1, get_first); + ranges::end(range1), pred, minus1, get_first); STATIC_ASSERT(same_as>>); assert(result.empty()); }