diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 8f13788c19..c338842a32 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -114,23 +114,6 @@ namespace ranges { } }; - // STRUCT TEMPLATE in_in_result - template - struct in_in_result { - /* [[no_unique_address]] */ _In1 in1; - /* [[no_unique_address]] */ _In2 in2; - - template <_Convertible_from _IIn1, _Convertible_from _IIn2> - constexpr operator in_in_result<_IIn1, _IIn2>() const& { - return {in1, in2}; - } - - template <_Convertible_from<_In1> _IIn1, _Convertible_from<_In2> _IIn2> - constexpr operator in_in_result<_IIn1, _IIn2>() && { - return {_STD move(in1), _STD move(in2)}; - } - }; - // STRUCT TEMPLATE in_in_out_result template struct in_in_out_result { @@ -416,21 +399,6 @@ namespace ranges { inline constexpr _For_each_n_fn for_each_n{_Not_quite_object::_Construct_tag{}}; // VARIABLE ranges::find - // clang-format off - // concept-constrained for strict enforcement as it is used by several algorithms - template _Se, class _Ty, class _Pj> - requires indirect_binary_predicate, const _Ty*> - _NODISCARD constexpr _It _Find_unchecked(_It _First, const _Se _Last, const _Ty& _Val, _Pj _Proj) { - for (; _First != _Last; ++_First) { - if (_STD invoke(_Proj, *_First) == _Val) { - break; - } - } - - return _First; - } - // clang-format on - class _Find_fn : private _Not_quite_object { public: using _Not_quite_object::_Not_quite_object; @@ -792,101 +760,6 @@ _NODISCARD pair<_FwdIt1, _FwdIt2> mismatch( #ifdef __cpp_lib_concepts namespace ranges { - // ALIAS TEMPLATE mismatch_result - template - 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; - } - } - - _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; - } - } - - _Seek_wrapped(_First1, _STD move(_UFirst1)); - _Seek_wrapped(_First2, _STD move(_UFirst2)); - return {_STD move(_First1), _STD move(_First2)}; - } - - 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 mismatch_result<_It1, _It2> 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); - - if constexpr (sized_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2>) { - 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)); - } 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)); - } - } - - 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 { - 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); - 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)); - } 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)); - } - } - // clang-format on - }; - - inline constexpr _Mismatch_fn mismatch{_Not_quite_object::_Construct_tag{}}; - // VARIABLE ranges::equal class _Equal_fn : private _Not_quite_object { private: @@ -4898,20 +4771,18 @@ _SampleIt sample(_PopIt _First, _PopIt _Last, _SampleIt _Dest, _Diff _Count, } #ifdef __cpp_lib_concepts -// STRUCT TEMPLATE _Require_constant -template -struct _Require_constant; // not defined; _Require_constant is a valid type if E is a constant expression - // CONCEPT uniform_random_bit_generator // clang-format off template -concept uniform_random_bit_generator = invocable<_Ty&> && unsigned_integral> && requires { - { (_Ty::min)() } -> same_as>; - { (_Ty::max)() } -> same_as>; - typename _Require_constant<(_Ty::min)()>; - typename _Require_constant<(_Ty::max)()>; - requires (_Ty::min)() < (_Ty::max)(); -}; +concept uniform_random_bit_generator = invocable<_Ty&> + && unsigned_integral> + && requires { + { (_Ty::min)() } -> same_as>; + { (_Ty::max)() } -> same_as>; + typename _Require_constant<(_Ty::min)()>; + typename _Require_constant<(_Ty::max)()>; + requires (_Ty::min)() < (_Ty::max)(); + }; // clang-format on namespace ranges { diff --git a/stl/inc/ranges b/stl/inc/ranges index 7381bd948e..69b334a20f 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2958,6 +2958,432 @@ namespace ranges { }; inline constexpr _Join_fn join; + } // namespace views + + // CLASS TEMPLATE ranges::split_view + // clang-format off + template + concept _Tiny_range = sized_range<_Ty> + && requires { typename _Require_constant::size()>; } + && (remove_reference_t<_Ty>::size() <= 1); + + template + requires view<_Vw> && view<_Pat> + && indirectly_comparable, iterator_t<_Pat>, _RANGES equal_to> + && (forward_range<_Vw> || _Tiny_range<_Pat>) + class split_view; + // clang-format on + + template + class _Split_view_base : public view_interface> { + protected: + /* [[no_unique_address]] */ iterator_t<_Vw> _Current{}; + }; + + template + class _Split_view_base<_Vw, _Pat> : public view_interface> {}; + + // clang-format off + template + requires view<_Vw> && view<_Pat> + && indirectly_comparable, iterator_t<_Pat>, _RANGES equal_to> + && (forward_range<_Vw> || _Tiny_range<_Pat>) + class split_view : public _Split_view_base<_Vw, _Pat> { + // clang-format on + private: + /* [[no_unique_address]] */ _Vw _Range{}; + /* [[no_unique_address]] */ _Pat _Pattern{}; + + template + class _Inner_iter; + + template + class _Outer_iter_base {}; + + // clang-format off + template + class _Outer_iter_base<_Iter> { + // clang-format on + protected: + _Iter _Current{}; + + public: + using iterator_category = input_iterator_tag; + + _Outer_iter_base() = default; + constexpr explicit _Outer_iter_base(_Iter _Current_) noexcept(is_nothrow_move_constructible_v<_Iter>) + : _Current{_STD move(_Current_)} {} + }; + + template + class _Outer_iter : public _Outer_iter_base>> { + private: + template + friend class _Inner_iter; + friend _Outer_iter; + + using _Mybase = _Outer_iter_base>>; + using _ParentTy = _Maybe_const<_Const, split_view>; + using _BaseTy = _Maybe_const<_Const, _Vw>; + + _ParentTy* _Parent = nullptr; + + _NODISCARD constexpr iterator_t<_BaseTy>& _Get_current() noexcept { + if constexpr (forward_range<_BaseTy>) { + return this->_Current; + } else { + return _Parent->_Current; + } + } + + _NODISCARD constexpr const iterator_t<_BaseTy>& _Get_current() const noexcept { + if constexpr (forward_range<_BaseTy>) { + return this->_Current; + } else { + return _Parent->_Current; + } + } + + _NODISCARD constexpr bool _At_end() const + noexcept(noexcept(_Implicitly_convert_to(_Get_current() == _RANGES end(_Parent->_Range)))) { + return _Get_current() == _RANGES end(_Parent->_Range); + } + + public: + using iterator_concept = conditional_t, forward_iterator_tag, input_iterator_tag>; + using difference_type = range_difference_t<_BaseTy>; + + class value_type : public view_interface { + private: + /* [[no_unique_address]] */ _Outer_iter _First{}; + + public: + value_type() = default; + constexpr explicit value_type(_Outer_iter _First_) noexcept( + is_nothrow_move_constructible_v<_Outer_iter>) // strengthened + : _First{_STD move(_First_)} {} + + _NODISCARD constexpr auto begin() const requires copyable<_Outer_iter> { + return _Inner_iter<_Const>{_First}; + } + + // clang-format off + _NODISCARD constexpr auto begin() requires (!copyable<_Outer_iter>) { + return _Inner_iter<_Const>{_STD move(_First)}; + } + // clang-format on + + _NODISCARD constexpr default_sentinel_t end() const noexcept { + return default_sentinel; + } + }; + + _Outer_iter() = default; + + // clang-format off + constexpr explicit _Outer_iter(_ParentTy& _Parent_) noexcept // strengthened + requires (!forward_range<_BaseTy>) : _Parent{_STD addressof(_Parent_)} {} + // clang-format on + + constexpr _Outer_iter(_ParentTy& _Parent_, iterator_t<_BaseTy> _Current_) noexcept( + is_nothrow_move_constructible_v>) // strengthened + requires forward_range<_BaseTy> + : _Mybase{_STD move(_Current_)}, _Parent{_STD addressof(_Parent_)} {} + + // clang-format off + constexpr _Outer_iter(_Outer_iter _It) + requires _Const && convertible_to, iterator_t<_BaseTy>> + : _Mybase{_STD move(_It._Current)}, _Parent{_It._Parent} {} + // clang-format on + + _NODISCARD constexpr auto operator*() const noexcept(noexcept(value_type{*this})) /* strengthened */ { + return value_type{*this}; + } + + constexpr _Outer_iter& operator++() { + const auto _End = _RANGES end(_Parent->_Range); + auto& _Cur = _Get_current(); + if (_Cur == _End) { + return *this; + } + + const auto _Pat_first = _RANGES begin(_Parent->_Pattern); + const auto _Pat_last = _RANGES end(_Parent->_Pattern); + if (_Pat_first == _Pat_last) { + ++_Cur; + } else if constexpr (_Tiny_range<_Pat>) { + _Cur = _RANGES _Find_unchecked(_STD move(_Cur), _End, *_Pat_first); + if (_Cur != _End) { + ++_Cur; + } + } else { + do { + auto _Result = _RANGES mismatch(_Cur, _End, _Pat_first, _Pat_last); + if (_Result.in2 == _Pat_last) { // pattern matches + _Cur = _STD move(_Result.in1); + break; + } + } while (++_Cur != _End); + } + return *this; + } + + constexpr decltype(auto) operator++(int) { + if constexpr (forward_range<_BaseTy>) { + auto _Tmp = *this; + ++*this; + return _Tmp; + } else { + ++*this; + } + } + + _NODISCARD friend constexpr bool operator==(const _Outer_iter& _Left, const _Outer_iter& _Right) noexcept( + noexcept(_Left._Current == _Right._Current)) /* strengthened */ requires forward_range<_BaseTy> { + return _Left._Current == _Right._Current; + } + _NODISCARD friend constexpr bool operator==(const _Outer_iter& _Left, default_sentinel_t) noexcept( + noexcept(_Left._At_end())) /* strengthened */ { + return _Left._At_end(); + } + }; + + template + class _Inner_iter_base {}; + + template + class _Inner_iter_base<_BaseTy> { + private: + using _BaseCategory = typename iterator_traits>::iterator_category; + + public: + using iterator_category = + conditional_t, forward_iterator_tag, _BaseCategory>; + }; + + template + class _Inner_iter : public _Inner_iter_base<_Maybe_const<_Const, _Vw>> { + private: + using _BaseTy = _Maybe_const<_Const, _Vw>; + + _Outer_iter<_Const> _It{}; + bool _Incremented = false; + + _NODISCARD constexpr bool _Equal(const _Inner_iter& _Right) const { + return _It._Get_current() == _Right._It._Get_current(); + } + + _NODISCARD constexpr iterator_t<_BaseTy>& _Get_current() noexcept { + return _It._Get_current(); + } + + _NODISCARD constexpr const iterator_t<_BaseTy>& _Get_current() const noexcept { + return _It._Get_current(); + } + + _NODISCARD constexpr bool _At_end() const { + auto _Pat_pos = _RANGES begin(_It._Parent->_Pattern); + const auto _Pat_end = _RANGES end(_It._Parent->_Pattern); + auto _Last = _RANGES end(_It._Parent->_Range); + if constexpr (_Tiny_range<_Pat>) { + const auto& _Cur = _It._Get_current(); // Intentionally a reference. Since _Pat is tiny, this could + // be a move-only iterator type. + if (_Cur == _Last) { + return true; + } + + if (_Pat_pos == _Pat_end) { + return _Incremented; + } + return *_Cur == *_Pat_pos; + } else { + auto _Cur = _It._Get_current(); // Intentionally not a reference. _Pat is not tiny, so this is a + // forward (copyable) iterator. + if (_Cur == _Last) { + return true; + } + + if (_Pat_pos == _Pat_end) { + return _Incremented; + } + + do { + if (*_Cur != *_Pat_pos) { + return false; + } + + if (++_Pat_pos == _Pat_end) { + return true; + } + } while (++_Cur != _Last); + return false; + } + } + + + public: + using iterator_concept = typename _Outer_iter<_Const>::iterator_concept; + using value_type = range_value_t<_BaseTy>; + using difference_type = range_difference_t<_BaseTy>; + + _Inner_iter() = default; + constexpr explicit _Inner_iter(_Outer_iter<_Const> _It_) noexcept( + is_nothrow_move_constructible_v<_Outer_iter<_Const>>) // strengthened + : _It{_STD move(_It_)} {} + + _NODISCARD constexpr decltype(auto) operator*() const { + return *_It._Get_current(); + } + + constexpr _Inner_iter& operator++() { + _Incremented = true; + if constexpr (!forward_range<_BaseTy>) { + if constexpr (_Pat::size() == 0) { + return *this; + } + } + ++_It._Get_current(); + return *this; + } + + constexpr decltype(auto) operator++(int) { + if constexpr (forward_range<_BaseTy>) { // per LWG-3532 + auto _Tmp = *this; + ++*this; + return _Tmp; + } else { + ++*this; + } + } + + _NODISCARD friend constexpr bool operator==( + const _Inner_iter& _Left, const _Inner_iter& _Right) requires forward_range<_BaseTy> { + return _Left._Equal(_Right); + } + + _NODISCARD friend constexpr bool operator==(const _Inner_iter& _Left, default_sentinel_t) { + return _Left._At_end(); + } + + _NODISCARD friend constexpr decltype(auto) iter_move(const _Inner_iter& _Iter) noexcept( + noexcept(_RANGES iter_move(_Iter._Get_current()))) { + return _RANGES iter_move(_Iter._Get_current()); + } + + // clang-format off + friend constexpr void iter_swap(const _Inner_iter& _Left, const _Inner_iter& _Right) + noexcept(noexcept(_RANGES iter_swap(_Left._Get_current(), _Right._Get_current()))) + requires indirectly_swappable> { + // clang-format on + _RANGES iter_swap(_Left._Get_current(), _Right._Get_current()); + } + }; + + public: + split_view() = default; + constexpr split_view(_Vw _Range_, _Pat _Pattern_) noexcept( + is_nothrow_move_constructible_v<_Vw>&& is_nothrow_move_constructible_v<_Pat>) // strengthened + : _Range(_STD move(_Range_)), _Pattern(_STD move(_Pattern_)) {} + + // clang-format off + template + requires constructible_from<_Vw, views::all_t<_Rng>> + && constructible_from<_Pat, single_view>> + constexpr split_view(_Rng&& _Range_, range_value_t<_Rng> _Elem) + noexcept(noexcept(_Vw(views::all(_STD forward<_Rng>(_Range_)))) + && noexcept(_Pat(single_view{_STD move(_Elem)}))) // strengthened + : _Range(views::all(_STD forward<_Rng>(_Range_))), _Pattern(single_view{_STD move(_Elem)}) {} + // clang-format on + + _NODISCARD constexpr _Vw base() const& noexcept(is_nothrow_copy_constructible_v<_Vw>) /* strengthened */ + requires copy_constructible<_Vw> { + return _Range; + } + _NODISCARD constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ { + return _STD move(_Range); + } + + _NODISCARD constexpr auto begin() { + if constexpr (forward_range<_Vw>) { + return _Outer_iter<_Simple_view<_Vw>>{*this, _RANGES begin(_Range)}; + } else { + this->_Current = _RANGES begin(_Range); + return _Outer_iter{*this}; + } + } + + // clang-format off + _NODISCARD constexpr auto begin() const requires forward_range<_Vw> && forward_range { + // clang-format on + return _Outer_iter{*this, _RANGES begin(_Range)}; + } + + // clang-format off + _NODISCARD constexpr auto end() requires forward_range<_Vw> && common_range<_Vw> { + // clang-format on + return _Outer_iter<_Simple_view<_Vw>>{*this, _RANGES end(_Range)}; + } + + _NODISCARD constexpr auto end() const { + if constexpr (forward_range<_Vw> && forward_range && common_range) { + return _Outer_iter{*this, _RANGES end(_Range)}; + } else { + return default_sentinel; + } + } + }; + + template + split_view(_Rng&&, _Pat&&) -> split_view, views::all_t<_Pat>>; + + template + split_view(_Rng&&, range_value_t<_Rng>) -> split_view, single_view>>; + + namespace views { + // VARIABLE views::split + class _Split_fn { + private: + template + struct _Partial : _Pipe::_Base<_Partial<_Delim>> { + /* [[no_unique_address]] */ _Delim _Delimiter; + + template + _NODISCARD constexpr auto operator()(_Rng&& _Range) const& noexcept( + noexcept(split_view{_STD forward<_Rng>(_Range), _Delimiter})) requires requires { + split_view{static_cast<_Rng&&>(_Range), _Delimiter}; + } + { return split_view{_STD forward<_Rng>(_Range), _Delimiter}; } + + template + _NODISCARD constexpr auto operator()(_Rng&& _Range) && noexcept(noexcept( + split_view{_STD forward<_Rng>(_Range), _STD forward<_Delim>(_Delimiter)})) requires requires { + split_view{static_cast<_Rng&&>(_Range), static_cast<_Delim&&>(_Delimiter)}; + } + { return split_view{_STD forward<_Rng>(_Range), _STD forward<_Delim>(_Delimiter)}; } + }; + + public: + // clang-format off + template + _NODISCARD constexpr auto operator()(_Rng&& _Range, _Pat&& _Pattern) const noexcept(noexcept( + split_view{_STD forward<_Rng>(_Range), _STD forward<_Pat>(_Pattern)})) requires requires { + split_view{static_cast<_Rng&&>(_Range), static_cast<_Pat&&>(_Pattern)}; + } { + // clang-format on + return split_view{_STD forward<_Rng>(_Range), _STD forward<_Pat>(_Pattern)}; + } + + // clang-format off + template + requires is_lvalue_reference_v<_Delim> || move_constructible<_Delim> + _NODISCARD constexpr auto operator()(_Delim&& _Delimiter) const + noexcept(is_lvalue_reference_v<_Delim> || is_nothrow_move_constructible_v<_Delim>) { + // clang-format on + return _Partial<_Delim>{._Delimiter = _STD forward<_Delim>(_Delimiter)}; + } + }; + + inline constexpr _Split_fn split; // VARIABLE views::counted class _Counted_fn { diff --git a/stl/inc/xmemory b/stl/inc/xmemory index ca3bb9bc9b..aea3f2e916 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -1483,10 +1483,6 @@ struct _NODISCARD _Uninitialized_backout { #ifdef __cpp_lib_concepts namespace ranges { - // CONCEPT _Convertible_from - template - concept _Convertible_from = convertible_to<_From, _To>; - // STRUCT TEMPLATE in_out_result template struct in_out_result { diff --git a/stl/inc/xutility b/stl/inc/xutility index 2179e1251f..9c7fa721a6 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -4710,6 +4710,141 @@ _NODISCARD bool equal(_ExPo&& _Exec, const _FwdIt1 _First1, const _FwdIt1 _Last1 } #endif // _HAS_CXX17 +#ifdef __cpp_lib_concepts +namespace ranges { + // 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*> + _NODISCARD constexpr _It _Find_unchecked(_It _First, const _Se _Last, const _Ty& _Val, _Pj _Proj = {}) { + for (; _First != _Last; ++_First) { + if (_STD invoke(_Proj, *_First) == _Val) { + break; + } + } + + return _First; + } + // clang-format on + + // CONCEPT _Convertible_from + template + concept _Convertible_from = convertible_to<_From, _To>; + + // STRUCT TEMPLATE in_in_result + template + struct in_in_result { + /* [[no_unique_address]] */ _In1 in1; + /* [[no_unique_address]] */ _In2 in2; + + template <_Convertible_from _IIn1, _Convertible_from _IIn2> + constexpr operator in_in_result<_IIn1, _IIn2>() const& { + return {in1, in2}; + } + + template <_Convertible_from<_In1> _IIn1, _Convertible_from<_In2> _IIn2> + constexpr operator in_in_result<_IIn1, _IIn2>() && { + return {_STD move(in1), _STD move(in2)}; + } + }; + + // ALIAS TEMPLATE mismatch_result + template + 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; + } + } + + _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; + } + } + + _Seek_wrapped(_First1, _STD move(_UFirst1)); + _Seek_wrapped(_First2, _STD move(_UFirst2)); + return {_STD move(_First1), _STD move(_First2)}; + } + + 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 mismatch_result<_It1, _It2> 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); + + if constexpr (sized_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2>) { + 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)); + } 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)); + } + } + + 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 { + 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); + 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)); + } 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)); + } + } + // clang-format on + }; + + inline constexpr _Mismatch_fn mismatch{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE lexicographical_compare template struct _Lex_compare_check_element_types_helper @@ -5307,6 +5442,11 @@ namespace ranges { inline constexpr _Find_if_not_fn find_if_not{_Not_quite_object::_Construct_tag{}}; } // namespace ranges + +// STRUCT TEMPLATE _Require_constant +template +struct _Require_constant; // _Require_constant is a valid template-id iff E is a constant expression of structural + // type #endif // __cpp_lib_concepts // FUNCTION TEMPLATE lower_bound diff --git a/tests/std/test.lst b/tests/std/test.lst index 4d3b516a52..c604f8bb1f 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -378,6 +378,7 @@ tests\P0896R4_views_iota tests\P0896R4_views_join tests\P0896R4_views_reverse tests\P0896R4_views_single +tests\P0896R4_views_split tests\P0896R4_views_take tests\P0896R4_views_take_while tests\P0896R4_views_take_while_death diff --git a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp index 47ec793dcf..683dade013 100644 --- a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp @@ -109,6 +109,7 @@ STATIC_ASSERT(test_cpo(ranges::views::join)); STATIC_ASSERT(test_cpo(ranges::views::keys)); STATIC_ASSERT(test_cpo(ranges::views::reverse)); STATIC_ASSERT(test_cpo(ranges::views::single)); +STATIC_ASSERT(test_cpo(ranges::views::split)); STATIC_ASSERT(test_cpo(ranges::views::take)); STATIC_ASSERT(test_cpo(ranges::views::take_while)); STATIC_ASSERT(test_cpo(ranges::views::transform)); diff --git a/tests/std/tests/P0896R4_views_split/env.lst b/tests/std/tests/P0896R4_views_split/env.lst new file mode 100644 index 0000000000..62a2402447 --- /dev/null +++ b/tests/std/tests/P0896R4_views_split/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_views_split/test.cpp b/tests/std/tests/P0896R4_views_split/test.cpp new file mode 100644 index 0000000000..548cc07c6b --- /dev/null +++ b/tests/std/tests/P0896R4_views_split/test.cpp @@ -0,0 +1,342 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include + +#include +using namespace std; + +template +concept CanViewSplit = requires(Rng&& r, Delimiter&& d) { + views::split(static_cast(r), static_cast(d)); +}; + +constexpr auto equal_ranges = [](auto&& left, auto&& right) { return ranges::equal(left, right); }; +constexpr auto text = "This is a test, this is only a test."sv; + +template +struct delimiter_view_impl { + template + using apply = ranges::single_view>; +}; +template <> +struct delimiter_view_impl { + template + using apply = views::all_t; +}; +template +using delimiter_view_t = + typename delimiter_view_impl>>::template apply; + +template +constexpr void test_one(Base&& base, Delimiter&& delimiter, Expected&& expected) { + STATIC_ASSERT(CanViewSplit); + using R = decltype(views::split(forward(base), forward(delimiter))); + + // Validate type properties + STATIC_ASSERT(ranges::view); + STATIC_ASSERT(ranges::input_range); + STATIC_ASSERT(ranges::forward_range == ranges::forward_range); + STATIC_ASSERT(!ranges::bidirectional_range); + + // Validate range adaptor object and range adaptor closure + using DV = delimiter_view_t; + const auto closure = views::split(delimiter); + + constexpr bool is_view = ranges::view>; + + // ... with lvalue argument + STATIC_ASSERT(CanViewSplit == (!is_view || copyable>) ); + if constexpr (CanViewSplit) { // Validate lvalue + constexpr bool is_noexcept = + (!is_view || is_nothrow_copy_constructible_v>) &&is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::split(base, delimiter)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(base | closure) == is_noexcept); + } + + // ... with const lvalue argument + STATIC_ASSERT( + CanViewSplit&, Delimiter&> == (!is_view || copyable>) ); + if constexpr (is_view && copyable>) { + constexpr bool is_noexcept = + is_nothrow_copy_constructible_v> && is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::split(as_const(base), delimiter)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(base) | closure) == is_noexcept); + } else if constexpr (!is_view) { + using RC = ranges::split_view&>, DV>; + constexpr bool is_noexcept = is_nothrow_constructible_v&, Delimiter&>; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::split(as_const(base), delimiter)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(base) | closure) == is_noexcept); + } + + // ... with rvalue argument + STATIC_ASSERT( + CanViewSplit, Delimiter&> == is_view || ranges::borrowed_range>); + if constexpr (is_view) { + constexpr bool is_noexcept = + is_nothrow_move_constructible_v> && is_nothrow_copy_constructible_v; + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::split(move(base), delimiter)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(base) | closure) == is_noexcept); + } else if constexpr (ranges::borrowed_range>) { + using S = decltype(ranges::subrange{declval>()}); + using RS = ranges::split_view; + constexpr bool is_noexcept = + noexcept(S{declval>()}) && is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::split(move(base), delimiter)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(base) | closure) == is_noexcept); + } + + // ... with const rvalue argument + STATIC_ASSERT( + CanViewSplit, Delimiter&> == (is_view && copyable>) + || (!is_view && ranges::borrowed_range>) ); + if constexpr (is_view && copyable>) { + constexpr bool is_noexcept = + is_nothrow_copy_constructible_v> && is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::split(move(as_const(base)), delimiter)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(as_const(base)) | closure) == is_noexcept); + } else if constexpr (!is_view && ranges::borrowed_range>) { + using S = decltype(ranges::subrange{declval>()}); + using RS = ranges::split_view; + constexpr bool is_noexcept = + noexcept(S{declval>()}) && is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::split(move(as_const(base)), delimiter)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(as_const(base)) | closure) == is_noexcept); + } + + // Validate deduction guide + same_as auto r = ranges::split_view{forward(base), forward(delimiter)}; + assert(ranges::equal(r, expected, equal_ranges)); + + // Validate view_interface::empty and operator bool + const bool is_empty = ranges::empty(expected); + STATIC_ASSERT(CanMemberEmpty == ranges::forward_range); + STATIC_ASSERT(CanBool == ranges::forward_range); + if constexpr (ranges::forward_range) { + assert(r.empty() == is_empty); + assert(static_cast(r) == !is_empty); + } + + // Validate split_view::begin + STATIC_ASSERT(CanMemberBegin); + if (ranges::forward_range) { // intentionally not if constexpr + const auto i = r.begin(); + if (!is_empty) { + assert(ranges::equal(*i, *ranges::begin(expected))); + } + + if constexpr (copyable) { + auto r2 = r; + const auto i2 = r2.begin(); + if (!is_empty) { + assert(ranges::equal(*i2, *i)); + } + } + + STATIC_ASSERT(CanMemberBegin == ranges::forward_range>); + STATIC_ASSERT(CanBegin == CanMemberBegin); + if constexpr (CanBegin) { + const auto ic = as_const(r).begin(); + if (!is_empty) { + assert(ranges::equal(*ic, *ranges::begin(expected))); + } + + if constexpr (copyable>) { + auto r2 = r; + const auto i2 = as_const(r2).begin(); + if (!is_empty) { + assert(ranges::equal(*i2, *ic)); + } + } + } + } + + // Validate split_view::end + STATIC_ASSERT(CanMemberEnd); + [[maybe_unused]] same_as> auto s = r.end(); + if (ranges::forward_range) { + assert((r.begin() == s) == is_empty); + } + STATIC_ASSERT(ranges::common_range == (ranges::forward_range && ranges::common_range) ); + if constexpr (!ranges::common_range) { + STATIC_ASSERT(same_as, default_sentinel_t>); + } + + STATIC_ASSERT(CanMemberEnd); + // clang-format off + constexpr bool should_be_const_common = ranges::forward_range + && ranges::forward_range> && ranges::common_range>; + // clang-format on + STATIC_ASSERT(ranges::common_range == should_be_const_common); + const auto sc = as_const(r).end(); + if constexpr (ranges::forward_range && ranges::forward_range>) { + STATIC_ASSERT(same_as>); + assert((as_const(r).begin() == sc) == is_empty); + } + if constexpr (!ranges::common_range) { + STATIC_ASSERT(same_as, default_sentinel_t>); + } + + // Validate view_interface::data + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + + // Validate view_interface::size + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanSize); + + // Validate view_interface::operator[] + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); + + if (!is_empty) { + // Validate view_interface::front and back + STATIC_ASSERT(CanMemberFront == ranges::forward_range); + if constexpr (ranges::forward_range) { + assert(ranges::equal(r.front(), *ranges::begin(expected))); + } + + STATIC_ASSERT(CanMemberFront == ranges::forward_range>); + if constexpr (CanMemberFront) { + assert(ranges::equal(as_const(r).front(), *ranges::begin(expected))); + } + + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + } + + // Validate split_view::base() const& + STATIC_ASSERT(CanMemberBase == copy_constructible>); + if constexpr (CanMemberBase && ranges::forward_range) { + same_as> auto b1 = as_const(r).base(); + STATIC_ASSERT(noexcept(as_const(r).base()) == is_nothrow_copy_constructible_v>); + if (!is_empty) { + assert(*b1.begin() == *ranges::begin(*ranges::begin(expected))); + } + } + + // Validate split_view::base() && (NB: do this last since it leaves r moved-from) + if (ranges::forward_range>) { // intentionally not if constexpr + same_as> auto b2 = move(r).base(); + STATIC_ASSERT(noexcept(move(r).base()) == is_nothrow_move_constructible_v>); + if (!is_empty) { + assert(*b2.begin() == *ranges::begin(*ranges::begin(expected))); + } + } +} + +struct instantiator { + static constexpr string_view expected_single[] = { + "This"sv, "is"sv, "a"sv, "test,"sv, "this"sv, "is"sv, "only"sv, "a"sv, "test."sv}; + static constexpr string_view expected_range[] = {"Th"sv, " "sv, " a test, th"sv, " "sv, " only a test."sv}; + static constexpr string_view expected_empty[] = {"T"sv, "h"sv, "i"sv, "s"sv, " "sv, "i"sv, "s"sv, " "sv, "a"sv, + " "sv, "t"sv, "e"sv, "s"sv, "t"sv, ","sv, " "sv, "t"sv, "h"sv, "i"sv, "s"sv, " "sv, "i"sv, "s"sv, " "sv, "o"sv, + "n"sv, "l"sv, "y"sv, " "sv, "a"sv, " "sv, "t"sv, "e"sv, "s"sv, "t"sv, "."sv}; + + template + static constexpr decltype(auto) move_if_needed(T& t) noexcept { + if constexpr (ranges::view && !copyable) { + return move(t); + } else { + return t; + } + } + + template + static constexpr void call() { + { // Single-element delimiter + Read read{span{text}}; + test_one(move_if_needed(read), ' ', expected_single); + + Read empty{}; + test_one(move_if_needed(empty), ' ', views::empty); + } + { // Empty delimiter + Read read{span{text}}; + test_one(move_if_needed(read), views::empty, expected_empty); + + Read empty{}; + test_one(move_if_needed(empty), views::empty, views::empty); + } + if constexpr (ranges::forward_range) { // Range delimiter + Read read{span{text}}; + test_one(move_if_needed(read), "is"sv, expected_range); + + Read empty{}; + test_one(move_if_needed(empty), "is"sv, views::empty); + } + } +}; + +template +using test_range = test::range}, IsCommon, + test::CanCompare{derived_from || IsCommon == test::Common::yes}, + test::ProxyRef{!derived_from}, IsView, CanCopy>; + +constexpr bool instantiation_test() { + using test::CanView, test::Common, test::Copyability; + + // The view is sensitive to: + // 1. Category of the range to be split (input, forward) + // 2. Copyability + // 4. Commonality + // 3. Length of delimiter pattern (0/static 1/dynamic) [covered in instantiator::call] + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + + return true; +} + +int main() { + STATIC_ASSERT(instantiation_test()); + instantiation_test(); +}