diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 712c68b62c9..12077672aa6 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -690,30 +690,37 @@ _NODISCARD pair<_FwdIt1, _FwdIt2> mismatch( #ifdef __cpp_lib_concepts namespace ranges { - 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 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; } + } - 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) { + _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; @@ -2214,6 +2221,300 @@ namespace ranges { }; inline constexpr _Search_n_fn search_n{_Not_quite_object::_Construct_tag{}}; + +#if _HAS_CXX23 + 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); + + 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 > _ULast1 - _UFirst1) { + return false; + } + } + + 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 _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; + } + } + + // 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_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)) { + return false; + } + } + + return _RANGES _Equal_count(_Ubegin(_Range1), _Ubegin(_Range2), + static_cast>(_Count2), _Pass_fn(_Pred), _Pass_fn(_Proj1), + _Pass_fn(_Proj2)); + } else { + 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; + } + } + }; + + inline constexpr _Starts_with_fn starts_with{_Not_quite_object::_Construct_tag{}}; + + 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); + 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)); + + const auto _Count1 = _Distance_helper(_UFirst1, _ULast1); + const auto _Count2 = _Distance_helper(_UFirst2, _ULast2); + + 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 + 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 auto _Count1 = _Distance_helper(_Range1); + const auto _Count2 = _Distance_helper(_Range2); + + return _Ends_with_impl(_Ubegin(_Range1), _Uend(_Range1), _Count1, _Ubegin(_Range2), _Uend(_Range2), _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_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>>); + _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_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, + _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 _Both_bidi_common = _Bidi_common<_It1, _Se1> && _Bidi_common<_It2, _Se2>; + + if constexpr (_Both_sized) { + if (_Count2 > _Count1) { + return false; + } + } + + if constexpr (_Sized2) { + if (_Count2 == 0) { + return true; + } + } + + 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); + } else { + return _Match_backwards(_STD move(_First1), _STD move(_Last1), _STD move(_First2), + _STD move(_Last2), _Pred, _Proj1, _Proj2); + } + } else if constexpr (_Sized2) { + return _Ends_with_sized_needle( + _STD move(_First1), _STD move(_Last1), _Count1, _STD move(_First2), _Count2, _Pred, _Proj1, _Proj2); + } else { + return _Ends_with_unsized_needle(_STD move(_First1), _STD move(_Last1), _Count1, _STD move(_First2), + _STD move(_Last2), _Pred, _Proj1, _Proj2); + } + } + }; + + 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 9bc966945b5..1d7cf2771ee 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -4764,44 +4764,40 @@ namespace ranges { template using mismatch_result = in_in_result<_In1, _In2>; - 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; - } + // 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; } - - _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)); - - for (; _UFirst1 != _ULast1 && _UFirst2 != _ULast2; ++_UFirst1, (void) ++_UFirst2) { - if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_UFirst1), _STD invoke(_Proj2, *_UFirst2))) { - break; - } - } + return {_STD move(_First1), _STD move(_First2)}; + } + + // clang-format off + template _Se1, input_iterator _It2, sentinel_for<_It2> _Se2, class _Pr, + class _Pj1, class _Pj2> + requires indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2> + _NODISCARD constexpr mismatch_result<_It1, _It2> _Mismatch_4( + _It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + // clang-format on - _Seek_wrapped(_First1, _STD move(_UFirst1)); - _Seek_wrapped(_First2, _STD move(_UFirst2)); - return {_STD move(_First1), _STD move(_First2)}; + for (; _First1 != _Last1 && _First2 != _Last2; ++_First1, (void) ++_First2) { + if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) { + break; + } } + return {_STD move(_First1), _STD move(_First2)}; + } + + class _Mismatch_fn : private _Not_quite_object { public: using _Not_quite_object::_Not_quite_object; @@ -4811,45 +4807,64 @@ namespace ranges { requires indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2> _NODISCARD constexpr mismatch_result<_It1, _It2> operator()(_It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + // clang-format on _Adl_verify_range(_First1, _Last1); _Adl_verify_range(_First2, _Last2); if constexpr (sized_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2>) { - iter_difference_t<_It1> _Count1 = _Last1 - _First1; + iter_difference_t<_It1> _Count1 = _Last1 - _First1; const iter_difference_t<_It2> _Count2 = _Last2 - _First2; if (_Count1 > _Count2) { _Count1 = static_cast(_Count2); } - return _Mismatch_n(_STD move(_First1), _STD move(_First2), _Count1, - _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + auto _Result = _RANGES _Mismatch_n(_Get_unwrapped(_STD move(_First1)), + _Get_unwrapped(_STD move(_First2)), _Count1, _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + _Seek_wrapped(_First1, _STD move(_Result.in1)); + _Seek_wrapped(_First2, _STD move(_Result.in2)); + return {_STD move(_First1), _STD move(_First2)}; } else { - return _Mismatch_4(_STD move(_First1), _STD move(_Last1), _STD move(_First2), _STD move(_Last2), - _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + auto _Result = _RANGES _Mismatch_4(_Get_unwrapped(_STD move(_First1)), + _Get_unwrapped(_STD move(_Last1)), _Get_unwrapped(_STD move(_First2)), + _Get_unwrapped(_STD move(_Last2)), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + _Seek_wrapped(_First1, _STD move(_Result.in1)); + _Seek_wrapped(_First2, _STD move(_Result.in2)); + return {_STD move(_First1), _STD move(_First2)}; } } + // clang-format off template 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)); + auto _First1 = _RANGES begin(_Range1); + auto _First2 = _RANGES begin(_Range2); + auto _Result = _RANGES _Mismatch_n(_Get_unwrapped(_STD move(_First1)), + _Get_unwrapped(_STD move(_First2)), _Count1, _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + _Seek_wrapped(_First1, _STD move(_Result.in1)); + _Seek_wrapped(_First2, _STD move(_Result.in2)); + return {_STD move(_First1), _STD move(_First2)}; } else { - return _Mismatch_4(_RANGES begin(_Range1), _RANGES end(_Range1), - _RANGES begin(_Range2), _RANGES end(_Range2), - _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + auto _First1 = _RANGES begin(_Range1); + auto _First2 = _RANGES begin(_Range2); + auto _Result = _RANGES _Mismatch_4(_Get_unwrapped(_STD move(_First1)), _Uend(_Range1), + _Get_unwrapped(_STD move(_First2)), _Uend(_Range2), _Pass_fn(_Pred), _Pass_fn(_Proj1), + _Pass_fn(_Proj2)); + _Seek_wrapped(_First1, _STD move(_Result.in1)); + _Seek_wrapped(_First2, _STD move(_Result.in2)); + return {_STD move(_First1), _STD move(_First2)}; } } - // clang-format on }; inline constexpr _Mismatch_fn mismatch{_Not_quite_object::_Construct_tag{}}; @@ -5196,6 +5211,10 @@ namespace ranges { concept _Sized_or_unreachable_sentinel_for = sized_sentinel_for<_Se, _It> || same_as<_Se, unreachable_sentinel_t>; // clang-format off + template + concept _Sized_or_infinite_range = range<_Rng> + && (sized_range<_Rng> || same_as, unreachable_sentinel_t>); + // 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/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 5e79fcfb5d0..fdcb3c2203b 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -276,6 +276,7 @@ // P1048R1 is_scoped_enum // P1132R7 out_ptr(), inout_ptr() // P1425R4 Iterator Pair Constructors For stack And queue +// P1659R3 ranges::starts_with, ranges::ends_with // P1679R3 contains() For basic_string/basic_string_view // P1682R3 to_underlying() For Enumerations // P1951R1 Default Template Arguments For pair's Forwarding Constructor @@ -1380,7 +1381,8 @@ #define __cpp_lib_is_scoped_enum 202011L #ifdef __cpp_lib_concepts -#define __cpp_lib_out_ptr 202106L +#define __cpp_lib_out_ptr 202106L +#define __cpp_lib_ranges_starts_ends_with 202106L #endif // __cpp_lib_concepts #define __cpp_lib_spanstream 202106L diff --git a/tests/std/test.lst b/tests/std/test.lst index 06d667b39f6..0d849e583c3 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -441,6 +441,8 @@ tests\P1502R1_standard_library_header_units tests\P1518R2_stop_overconstraining_allocators tests\P1614R2_spaceship tests\P1645R1_constexpr_numeric +tests\P1659R3_ranges_alg_ends_with +tests\P1659R3_ranges_alg_starts_with tests\P1682R3_to_underlying tests\P1951R1_default_arguments_pair_forward_ctor tests\P2136R3_invoke_r 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..ca553e46e11 --- /dev/null +++ b/tests/std/tests/P1659R3_ranges_alg_ends_with/test.cpp @@ -0,0 +1,121 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +#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}, {1, 42}, {2, 42}, {4, 42}}; + static constexpr pair short_haystack[] = {{4, 42}}; + 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 + static constexpr void test_range() { + using ranges::ends_with, ranges::equal_to; + + { + 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); + } + { + 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{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 + static constexpr void test_iterator_sentinel() { + using ranges::begin, ranges::end, ranges::ends_with, ranges::equal_to; + + { + In1 h{haystack}; + 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}; + 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{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 + static void call() { + 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>(); +#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..b8f545ed522 --- /dev/null +++ b/tests/std/tests/P1659R3_ranges_alg_starts_with/test.cpp @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +#include + +using namespace std; + +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}}; + + template + static constexpr void test() { + using ranges::begin, ranges::end, ranges::equal_to, ranges::starts_with; + + // 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 match = + starts_with(In1{haystack}, In2{wrong_needle}, equal_to{}, get_first, get_second); + assert(!match); + } + { + const same_as auto match = + starts_with(In1{short_haystack}, In2{needle}, equal_to{}, get_first, get_second); + assert(!match); + } + + // Validate iterator + sentinel overload + { + In1 h{haystack}; + In2 n{needle}; + const same_as auto match = + starts_with(begin(h), end(h), begin(n), end(n), equal_to{}, get_first, get_second); + assert(match); + } + { + In1 h{haystack}; + In2 n{wrong_needle}; + const same_as 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); + } + } + + template + static void call() { + test(); + STATIC_ASSERT((test(), true)); + } +}; + +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>(); +#endif // TRANSITION, GH-1030 +} 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 86aaf50b681..4bba8cfd6cd 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 @@ -1294,6 +1294,20 @@ STATIC_ASSERT(__cpp_lib_ranges == 202106L); #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