From fa805580daa825f06f42e4006b1184e00416dfe1 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 25 Feb 2021 13:35:02 -0800 Subject: [PATCH 1/8] Implement ranges::split_view --- stl/inc/algorithm | 143 ------ stl/inc/ranges | 406 +++++++++++++++++- stl/inc/xmemory | 4 - stl/inc/xutility | 152 +++++++ tests/std/test.lst | 1 + .../P0896R4_ranges_range_machinery/test.cpp | 1 + tests/std/tests/P0896R4_views_split/env.lst | 4 + tests/std/tests/P0896R4_views_split/test.cpp | 341 +++++++++++++++ 8 files changed, 904 insertions(+), 148 deletions(-) create mode 100644 tests/std/tests/P0896R4_views_split/env.lst create mode 100644 tests/std/tests/P0896R4_views_split/test.cpp diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 8f13788c19..4127570f01 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,22 +4771,6 @@ _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)(); -}; -// clang-format on - namespace ranges { // VARIABLE ranges::sample class _Sample_fn : private _Not_quite_object { diff --git a/stl/inc/ranges b/stl/inc/ranges index 367648ffe8..b27c8444ea 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1634,7 +1634,7 @@ namespace ranges { } friend constexpr void iter_swap(const _Iterator& _Left, const _Iterator& _Right) _NOEXCEPT_IDL0(noexcept( - ranges::iter_swap(_Left._Current, _Right._Current))) requires indirectly_swappable> { + _RANGES iter_swap(_Left._Current, _Right._Current))) requires indirectly_swappable> { #if _ITERATOR_DEBUG_LEVEL != 0 _Left._Check_dereference(); _Right._Check_dereference(); @@ -2490,6 +2490,410 @@ namespace ranges { }; inline constexpr _Drop_fn drop; + } // 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 + struct _Outer_iter_base {}; + + // clang-format off + template + struct _Outer_iter_base<_Iter> { + // clang-format on + _Iter _Current{}; + }; + + template + class _Inner_iter; + + template + class _Outer_iter : private _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 iterator_category = input_iterator_tag; + using difference_type = range_difference_t<_BaseTy>; + + class value_type : public view_interface { + private: + /* [[no_unique_address]] */ _Outer_iter _It{}; + + public: + value_type() = default; + constexpr explicit value_type(_Outer_iter _It_) noexcept( + is_nothrow_move_constructible_v<_Outer_iter>) // strengthened + : _It{_STD move(_It_)} {} + + _NODISCARD constexpr _Inner_iter<_Const> begin() const requires copyable<_Outer_iter> { + return _Inner_iter<_Const>{_It}; + } + + // clang-format off + _NODISCARD constexpr _Inner_iter<_Const> begin() requires (!copyable<_Outer_iter>) { + return _Inner_iter<_Const>{_STD move(_It)}; + } + // 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 value_type 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>) { // Per LWG-3505 + _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())) /* strenghtened */ { + return _Left._At_end(); + } + }; + + template + class _Inner_iter { + 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(); + if (_Cur == _Last) { + return true; + } + + if (_Pat_pos == _Pat_end) { + return _Incremented; + } + return *_Cur == *_Pat_pos; + } else { + auto _Cur = _It._Get_current(); + 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 iterator_category = conditional_t< + derived_from>::iterator_category, forward_iterator_tag>, + forward_iterator_tag, typename iterator_traits>::iterator_category>; + 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 issue unnumbered as of 2021-03-11 + 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..e20e76e020 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,23 @@ namespace ranges { inline constexpr _Find_if_not_fn find_if_not{_Not_quite_object::_Construct_tag{}}; } // namespace ranges + +// CONCEPT uniform_random_bit_generator +template +struct _Require_constant; // not defined; _Require_constant is a valid type if E is a constant expression + +// 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)(); + }; +// clang-format on #endif // __cpp_lib_concepts // FUNCTION TEMPLATE lower_bound diff --git a/tests/std/test.lst b/tests/std/test.lst index e5866c93af..05a526ff77 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -377,6 +377,7 @@ tests\P0896R4_views_filter_death tests\P0896R4_views_iota 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 2e4698779e..c633b77eea 100644 --- a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp @@ -108,6 +108,7 @@ STATIC_ASSERT(test_cpo(ranges::views::iota)); 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..bc1defdbdb --- /dev/null +++ b/tests/std/tests/P0896R4_views_split/test.cpp @@ -0,0 +1,341 @@ +// 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); + constexpr bool should_be_const_common = + ranges::forward_range< + Base> && ranges::forward_range> && ranges::common_range>; + 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(); +} From 64396d6e99c1e529c63f490f1362cbd0bcec7f20 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 11 Mar 2021 14:10:16 -0800 Subject: [PATCH 2/8] Overlong lines --- stl/inc/ranges | 3 ++- tests/std/tests/P0896R4_views_split/test.cpp | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index b27c8444ea..07d501ba51 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2801,7 +2801,8 @@ namespace ranges { 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 + 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 diff --git a/tests/std/tests/P0896R4_views_split/test.cpp b/tests/std/tests/P0896R4_views_split/test.cpp index bc1defdbdb..548cc07c6b 100644 --- a/tests/std/tests/P0896R4_views_split/test.cpp +++ b/tests/std/tests/P0896R4_views_split/test.cpp @@ -197,9 +197,10 @@ constexpr void test_one(Base&& base, Delimiter&& delimiter, Expected&& expected) } STATIC_ASSERT(CanMemberEnd); - constexpr bool should_be_const_common = - ranges::forward_range< - Base> && ranges::forward_range> && ranges::common_range>; + // 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>) { From 24d9e942413d94a163e5889bcd4d474daea23616 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 11 Mar 2021 15:51:27 -0800 Subject: [PATCH 3/8] Casey's review comments --- stl/inc/algorithm | 14 ++++++++++++++ stl/inc/xutility | 18 +++--------------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 4127570f01..c338842a32 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -4771,6 +4771,20 @@ _SampleIt sample(_PopIt _First, _PopIt _Last, _SampleIt _Dest, _Diff _Count, } #ifdef __cpp_lib_concepts +// 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)(); + }; +// clang-format on + namespace ranges { // VARIABLE ranges::sample class _Sample_fn : private _Not_quite_object { diff --git a/stl/inc/xutility b/stl/inc/xutility index e20e76e020..1185b19d99 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -5443,22 +5443,10 @@ namespace ranges { inline constexpr _Find_if_not_fn find_if_not{_Not_quite_object::_Construct_tag{}}; } // namespace ranges -// CONCEPT uniform_random_bit_generator +// STRUCT TEMPLATE _Require_constant template -struct _Require_constant; // not defined; _Require_constant is a valid type if E is a constant expression - -// 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)(); - }; -// clang-format on +struct _Require_constant; // not defined; _Require_constant is a valid type if E is a constant expression of + // structural type #endif // __cpp_lib_concepts // FUNCTION TEMPLATE lower_bound From 4b2aba5884db694ea0658501c7fff03622f1c539 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 11 Mar 2021 21:04:13 -0800 Subject: [PATCH 4/8] STL's review comments --- stl/inc/ranges | 19 ++++++++++--------- stl/inc/xutility | 4 ++-- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 07d501ba51..7556aa1a85 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2545,7 +2545,7 @@ namespace ranges { friend class _Inner_iter; friend _Outer_iter; - using _MyBase = _Outer_iter_base>>; + using _Mybase = _Outer_iter_base>>; using _ParentTy = _Maybe_const<_Const, split_view>; using _BaseTy = _Maybe_const<_Const, _Vw>; @@ -2612,12 +2612,12 @@ namespace ranges { 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_)} {} + : _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} {} + : _Mybase{_STD move(_It._Current)}, _Parent{_It._Parent} {} // clang-format on _NODISCARD constexpr value_type operator*() const noexcept(noexcept(value_type{*this})) /* strengthened */ { @@ -2635,7 +2635,7 @@ namespace ranges { const auto _Pat_last = _RANGES end(_Parent->_Pattern); if (_Pat_first == _Pat_last) { ++_Cur; - } else if constexpr (_Tiny_range<_Pat>) { // Per LWG-3505 + } else if constexpr (_Tiny_range<_Pat>) { _Cur = _RANGES _Find_unchecked(_STD move(_Cur), _End, *_Pat_first); if (_Cur != _End) { ++_Cur; @@ -2667,7 +2667,7 @@ namespace ranges { return _Left._Current == _Right._Current; } _NODISCARD friend constexpr bool operator==(const _Outer_iter& _Left, default_sentinel_t) noexcept( - noexcept(_Left._At_end())) /* strenghtened */ { + noexcept(_Left._At_end())) /* strengthened */ { return _Left._At_end(); } }; @@ -2729,11 +2729,12 @@ namespace ranges { } } + using _BaseCategory = typename iterator_traits>::iterator_category; + public: - using iterator_concept = typename _Outer_iter<_Const>::iterator_concept; - using iterator_category = conditional_t< - derived_from>::iterator_category, forward_iterator_tag>, - forward_iterator_tag, typename iterator_traits>::iterator_category>; + using iterator_concept = typename _Outer_iter<_Const>::iterator_concept; + using iterator_category = + conditional_t, forward_iterator_tag, _BaseCategory>; using value_type = range_value_t<_BaseTy>; using difference_type = range_difference_t<_BaseTy>; diff --git a/stl/inc/xutility b/stl/inc/xutility index 1185b19d99..9c7fa721a6 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -5445,8 +5445,8 @@ namespace ranges { // STRUCT TEMPLATE _Require_constant template -struct _Require_constant; // not defined; _Require_constant is a valid type if E is a constant expression of - // structural type +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 From febe3fd6687ab1bf4c9d34073590048802fc938c Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 11 Mar 2021 22:43:10 -0800 Subject: [PATCH 5/8] Implement P2259R1's changes to split_view --- stl/inc/ranges | 46 +++++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 7556aa1a85..bd2f3428e0 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2525,21 +2525,29 @@ namespace ranges { /* [[no_unique_address]] */ _Vw _Range{}; /* [[no_unique_address]] */ _Pat _Pattern{}; + template + class _Inner_iter; + template - struct _Outer_iter_base {}; + class _Outer_iter_base {}; // clang-format off template - struct _Outer_iter_base<_Iter> { + class _Outer_iter_base<_Iter> { // clang-format on + protected: _Iter _Current{}; - }; - template - class _Inner_iter; + public: + using iterator_category = input_iterator_tag; + + _Outer_iter_base() = default; + constexpr _Outer_iter_base(_Iter _Current_) noexcept(is_nothrow_move_constructible_v<_Iter>) + : _Current{_STD move(_Current_)} {} + }; template - class _Outer_iter : private _Outer_iter_base>> { + class _Outer_iter : public _Outer_iter_base>> { private: template friend class _Inner_iter; @@ -2573,9 +2581,8 @@ namespace ranges { } public: - using iterator_concept = conditional_t, forward_iterator_tag, input_iterator_tag>; - using iterator_category = input_iterator_tag; - using difference_type = range_difference_t<_BaseTy>; + 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: @@ -2672,8 +2679,20 @@ namespace ranges { } }; + 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 { + class _Inner_iter : public _Inner_iter_base<_Maybe_const<_Const, _Vw>> { private: using _BaseTy = _Maybe_const<_Const, _Vw>; @@ -2729,14 +2748,11 @@ namespace ranges { } } - using _BaseCategory = typename iterator_traits>::iterator_category; public: using iterator_concept = typename _Outer_iter<_Const>::iterator_concept; - using iterator_category = - conditional_t, forward_iterator_tag, _BaseCategory>; - using value_type = range_value_t<_BaseTy>; - using difference_type = range_difference_t<_BaseTy>; + 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( From fdb2d438e89d0afceccc9993cfb006af4b2e841d Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Fri, 12 Mar 2021 10:23:50 -0800 Subject: [PATCH 6/8] miscco's review comments --- stl/inc/ranges | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index bd2f3428e0..a793b2b0b2 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2511,6 +2511,7 @@ namespace ranges { protected: /* [[no_unique_address]] */ iterator_t<_Vw> _Current{}; }; + template class _Split_view_base<_Vw, _Pat> : public view_interface> {}; @@ -2542,7 +2543,7 @@ namespace ranges { using iterator_category = input_iterator_tag; _Outer_iter_base() = default; - constexpr _Outer_iter_base(_Iter _Current_) noexcept(is_nothrow_move_constructible_v<_Iter>) + constexpr explicit _Outer_iter_base(_Iter _Current_) noexcept(is_nothrow_move_constructible_v<_Iter>) : _Current{_STD move(_Current_)} {} }; @@ -2586,21 +2587,21 @@ namespace ranges { class value_type : public view_interface { private: - /* [[no_unique_address]] */ _Outer_iter _It{}; + /* [[no_unique_address]] */ _Outer_iter _First{}; public: value_type() = default; - constexpr explicit value_type(_Outer_iter _It_) noexcept( + constexpr explicit value_type(_Outer_iter _First_) noexcept( is_nothrow_move_constructible_v<_Outer_iter>) // strengthened - : _It{_STD move(_It_)} {} + : _First{_STD move(_First_)} {} - _NODISCARD constexpr _Inner_iter<_Const> begin() const requires copyable<_Outer_iter> { - return _Inner_iter<_Const>{_It}; + _NODISCARD constexpr auto begin() const requires copyable<_Outer_iter> { + return _Inner_iter<_Const>{_First}; } // clang-format off - _NODISCARD constexpr _Inner_iter<_Const> begin() requires (!copyable<_Outer_iter>) { - return _Inner_iter<_Const>{_STD move(_It)}; + _NODISCARD constexpr auto begin() requires (!copyable<_Outer_iter>) { + return _Inner_iter<_Const>{_STD move(_First)}; } // clang-format on @@ -2622,12 +2623,12 @@ namespace ranges { : _Mybase{_STD move(_Current_)}, _Parent{_STD addressof(_Parent_)} {} // clang-format off - constexpr _Outer_iter( - _Outer_iter _It) requires _Const && convertible_to, iterator_t<_BaseTy>> + 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 value_type operator*() const noexcept(noexcept(value_type{*this})) /* strengthened */ { + _NODISCARD constexpr auto operator*() const noexcept(noexcept(value_type{*this})) /* strengthened */ { return value_type{*this}; } @@ -2681,6 +2682,7 @@ namespace ranges { template class _Inner_iter_base {}; + template class _Inner_iter_base<_BaseTy> { private: @@ -2765,7 +2767,7 @@ namespace ranges { constexpr _Inner_iter& operator++() { _Incremented = true; - if constexpr (!forward_range<_BaseTy>) { + if constexpr (_Tiny_range<_Pat>) { if constexpr (_Pat::size() == 0) { return *this; } From 6b07351cbb2eeafd83afd22267e640c5b0cf9efc Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Fri, 12 Mar 2021 11:39:41 -0800 Subject: [PATCH 7/8] miscco's explanatory comment --- stl/inc/ranges | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index a793b2b0b2..1fbdaf9d46 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2718,7 +2718,8 @@ namespace ranges { 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(); + 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; } @@ -2728,7 +2729,8 @@ namespace ranges { } return *_Cur == *_Pat_pos; } else { - auto _Cur = _It._Get_current(); + 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; } From b4c7700751d02e3d8262d8c72fa68ce2bf0a2bc5 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 15 Mar 2021 12:37:24 -0700 Subject: [PATCH 8/8] Revert bad change to operator++; LWG-3532 has a number --- stl/inc/ranges | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 1fbdaf9d46..50a8b6c289 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -2769,7 +2769,7 @@ namespace ranges { constexpr _Inner_iter& operator++() { _Incremented = true; - if constexpr (_Tiny_range<_Pat>) { + if constexpr (!forward_range<_BaseTy>) { if constexpr (_Pat::size() == 0) { return *this; } @@ -2779,7 +2779,7 @@ namespace ranges { } constexpr decltype(auto) operator++(int) { - if constexpr (forward_range<_BaseTy>) { // per LWG issue unnumbered as of 2021-03-11 + if constexpr (forward_range<_BaseTy>) { // per LWG-3532 auto _Tmp = *this; ++*this; return _Tmp;