From: Patrick Palka Date: Thu, 30 Jan 2020 22:37:07 +0000 (-0500) Subject: libstdc++: Implement C++20 range adaptors X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=cba9ef069e58eac00f30489d3ef21390caee6e45;p=gcc.git libstdc++: Implement C++20 range adaptors This patch implements [range.adaptors]. It also includes the changes from P3280 and P3278 and P3323, without which many standard examples won't work. The implementation is mostly dictated by the spec and there was not much room for implementation discretion. The most interesting part that was not specified by the spec is the design of the range adaptors and range adaptor closures, which I tried to design in a way that minimizes boilerplate and statefulness (so that e.g. the composition of two stateless closures is stateless). What is left unimplemented is caching of calls to begin() in filter_view, drop_view and reverse_view, which is required to guarantee that begin() has amortized constant time complexity. I can implement this in a subsequent patch. "Interesting" parts of the patch are marked with XXX comments. libstdc++-v3/ChangeLog: Implement C++20 range adaptors * include/std/ranges: Include and . (subrange::_S_store_size): Mark as const instead of constexpr to avoid what seems to be a bug in GCC. (__detail::__box): Give it defaulted copy and move constructors. (views::_Single::operator()): Mark constexpr. (views::_Iota::operator()): Mark constexpr. (__detail::Empty): Define. (views::_RangeAdaptor, views::_RangeAdaptorClosure, ref_view, all_view, views::all, filter_view, views::filter, transform_view, views::transform, take_view, views::take, take_while_view, views::take_while, drop_view, views::drop, join_view, views::join, __detail::require_constant, __detail::tiny_range, split_view, views::split, views::_Counted, views::counted, common_view, views::common, reverse_view, views::reverse, views::__detail::__is_reversible_subrange, views::__detail::__is_reverse_view, reverse_view, views::reverse, __detail::__has_tuple_element, elements_view, views::elements, views::keys, views::values): Define. * testsuite/std/ranges/adaptors/all.cc: New test. * testsuite/std/ranges/adaptors/common.cc: Likewise. * testsuite/std/ranges/adaptors/counted.cc: Likewise. * testsuite/std/ranges/adaptors/drop.cc: Likewise. * testsuite/std/ranges/adaptors/drop_while.cc: Likewise. * testsuite/std/ranges/adaptors/elements.cc: Likewise. * testsuite/std/ranges/adaptors/filter.cc: Likewise. * testsuite/std/ranges/adaptors/join.cc: Likewise. * testsuite/std/ranges/adaptors/reverse.cc: Likewise. * testsuite/std/ranges/adaptors/split.cc: Likewise. * testsuite/std/ranges/adaptors/take.cc: Likewise. * testsuite/std/ranges/adaptors/take_while.cc: Likewise. * testsuite/std/ranges/adaptors/transform.cc: Likewise. --- diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 0fce279775a..2903074dabe 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,38 @@ +2020-02-07 Patrick Palka + + Implement C++20 range adaptors + * include/std/ranges: Include and . + (subrange::_S_store_size): Mark as const instead of constexpr to + avoid what seems to be a bug in GCC. + (__detail::__box): Give it defaulted copy and move constructors. + (views::_Single::operator()): Mark constexpr. + (views::_Iota::operator()): Mark constexpr. + (__detail::Empty): Define. + (views::_RangeAdaptor, views::_RangeAdaptorClosure, ref_view, all_view, + views::all, filter_view, views::filter, transform_view, + views::transform, take_view, views::take, take_while_view, + views::take_while, drop_view, views::drop, join_view, views::join, + __detail::require_constant, __detail::tiny_range, split_view, + views::split, views::_Counted, views::counted, common_view, + views::common, reverse_view, views::reverse, + views::__detail::__is_reversible_subrange, + views::__detail::__is_reverse_view, reverse_view, views::reverse, + __detail::__has_tuple_element, elements_view, views::elements, + views::keys, views::values): Define. + * testsuite/std/ranges/adaptors/all.cc: New test. + * testsuite/std/ranges/adaptors/common.cc: Likewise. + * testsuite/std/ranges/adaptors/counted.cc: Likewise. + * testsuite/std/ranges/adaptors/drop.cc: Likewise. + * testsuite/std/ranges/adaptors/drop_while.cc: Likewise. + * testsuite/std/ranges/adaptors/elements.cc: Likewise. + * testsuite/std/ranges/adaptors/filter.cc: Likewise. + * testsuite/std/ranges/adaptors/join.cc: Likewise. + * testsuite/std/ranges/adaptors/reverse.cc: Likewise. + * testsuite/std/ranges/adaptors/split.cc: Likewise. + * testsuite/std/ranges/adaptors/take.cc: Likewise. + * testsuite/std/ranges/adaptors/take_while.cc: Likewise. + * testsuite/std/ranges/adaptors/transform.cc: Likewise. + 2020-02-07 Jonathan Wakely * libsupc++/compare (__cmp_cat::type): Define typedef for underlying diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index dc277a74fb6..9f4fa3414d0 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -38,11 +38,13 @@ #if __cpp_lib_concepts +#include #include #include #include #include #include +#include /** * @defgroup ranges Ranges @@ -255,7 +257,8 @@ namespace ranges class subrange : public view_interface> { private: - static constexpr bool _S_store_size + // XXX: gcc complains when using constexpr here + static const bool _S_store_size = _Kind == subrange_kind::sized && !sized_sentinel_for<_Sent, _It>; _It _M_begin = _It(); @@ -507,6 +510,9 @@ namespace ranges : std::optional<_Tp>{std::in_place} { } + __box(const __box&) = default; + __box(__box&&) = default; + using std::optional<_Tp>::operator=; __box& @@ -922,7 +928,7 @@ namespace views struct _Single { template - auto + constexpr auto operator()(_Tp&& __e) const { return single_view{std::forward<_Tp>(__e)}; } }; @@ -932,20 +938,2409 @@ namespace views struct _Iota { template - auto + constexpr auto operator()(_Tp&& __e) const { return iota_view{std::forward<_Tp>(__e)}; } template - auto + constexpr auto operator()(_Tp&& __e, _Up&& __f) const { return iota_view{std::forward<_Tp>(__e), std::forward<_Tp>(__f)}; } }; inline constexpr _Iota iota{}; +} // namespace views + +namespace __detail +{ + struct _Empty { }; +} // namespace __detail + +namespace views +{ + namespace __adaptor + { + template + struct _RangeAdaptorClosure; + + template + struct _RangeAdaptor + { + protected: + [[no_unique_address]] + conditional_t, + _Callable, __detail::_Empty> _M_callable; + + public: + constexpr + _RangeAdaptor(const _Callable& = {}) + requires is_default_constructible_v<_Callable> + { } + + constexpr + _RangeAdaptor(_Callable __callable) + requires (!is_default_constructible_v<_Callable>) + : _M_callable(std::move(__callable)) + { } + + template + requires (sizeof...(_Args) >= 1) + constexpr auto + operator()(_Args&&... __args) const + { + if constexpr (is_invocable_v<_Callable, _Args...>) + { + static_assert(sizeof...(_Args) != 1, + "a _RangeAdaptor that accepts only one argument " + "should be defined as a _RangeAdaptorClosure"); + return _Callable{}(std::forward<_Args>(__args)...); + } + else + { + auto __closure = [__args...] (_Range&& __r) { + return _Callable{}(std::forward<_Range>(__r), __args...); + }; + using _ClosureType = decltype(__closure); + return _RangeAdaptorClosure<_ClosureType>(std::move(__closure)); + } + } + }; + + template + struct _RangeAdaptorClosure : public _RangeAdaptor<_Callable> + { + using _RangeAdaptor<_Callable>::_RangeAdaptor; + + template + requires requires { declval<_Callable>()(declval<_Range>()); } + constexpr auto + operator()(_Range&& __r) const + { + if constexpr (is_default_constructible_v<_Callable>) + return _Callable{}(std::forward<_Range>(__r)); + else + return this->_M_callable(std::forward<_Range>(__r)); + } + + template + requires requires { declval<_Callable>()(declval<_Range>()); } + friend constexpr auto + operator|(_Range&& __r, const _RangeAdaptorClosure& __o) + { return __o(std::forward<_Range>(__r)); } + template + friend constexpr auto + operator|(const _RangeAdaptorClosure<_Tp>& __x, + const _RangeAdaptorClosure& __y) + { + if constexpr (is_default_constructible_v<_Tp> + && is_default_constructible_v<_Callable>) + { + auto __closure = [] (_Up&& __e) { + return std::forward<_Up>(__e) | decltype(__x){} | decltype(__y){}; + }; + return _RangeAdaptorClosure(__closure); + } + else if constexpr (is_default_constructible_v<_Tp> + && !is_default_constructible_v<_Callable>) + { + auto __closure = [__y] (_Up&& __e) { + return std::forward<_Up>(__e) | decltype(__x){} | __y; + }; + return _RangeAdaptorClosure(__closure); + } + else if constexpr (!is_default_constructible_v<_Tp> + && is_default_constructible_v<_Callable>) + { + auto __closure = [__x] (_Up&& __e) { + return std::forward<_Up>(__e) | __x | decltype(__y){}; + }; + return _RangeAdaptorClosure(__closure); + } + else + { + auto __closure = [__x, __y] (_Up&& __e) { + return std::forward<_Up>(__e) | __x | __y; + }; + return _RangeAdaptorClosure(__closure); + } + } + }; + + template + _RangeAdaptorClosure(_Callable) -> _RangeAdaptorClosure<_Callable>; + } // namespace __adaptor } // namespace views -} // namespace ranges + + template requires is_object_v<_Range> + class ref_view : public view_interface> + { + private: + _Range* _M_r = nullptr; + + static void _S_fun(_Range&); // not defined + static void _S_fun(_Range&&) = delete; + + public: + constexpr + ref_view() noexcept = default; + + template<__detail::__not_same_as _Tp> + requires convertible_to<_Tp, _Range&> + && requires { _S_fun(declval<_Tp>()); } + constexpr + ref_view(_Tp&& __t) + : _M_r(std::__addressof(static_cast<_Range&>(std::forward<_Tp>(__t)))) + { } + + constexpr _Range& + base() const + { return *_M_r; } + + constexpr iterator_t<_Range> + begin() const + { return ranges::begin(*_M_r); } + + constexpr sentinel_t<_Range> + end() const + { return ranges::end(*_M_r); } + + constexpr bool + empty() const requires requires { ranges::empty(*_M_r); } + { return ranges::empty(*_M_r); } + + constexpr auto + size() const requires sized_range<_Range> + { return ranges::size(*_M_r); } + + constexpr auto + data() const requires contiguous_range<_Range> + { return ranges::data(*_M_r); } + }; + + template + ref_view(_Range&) -> ref_view<_Range>; + + template + inline constexpr bool enable_safe_range> = true; + + namespace views + { + inline constexpr __adaptor::_RangeAdaptorClosure all + = [] (_Range&& __r) + { + if constexpr (view>) + return std::forward<_Range>(__r); + else if constexpr (requires { ref_view{std::forward<_Range>(__r)}; }) + return ref_view{std::forward<_Range>(__r)}; + else + return subrange{std::forward<_Range>(__r)}; + }; + } // namespace views + + template + using all_view = decltype(views::all(declval<_Range>())); + + // XXX: the following algos are copied from ranges_algo.h to avoid a circular + // dependency with that header. + namespace __detail + { + template _Sent, + typename _Proj = identity, + indirect_unary_predicate> _Pred> + constexpr _Iter + find_if(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {}) + { + while (__first != __last + && !(bool)std::__invoke(__pred, std::__invoke(__proj, *__first))) + ++__first; + return __first; + } + + template _Sent, + typename _Proj = identity, + indirect_unary_predicate> _Pred> + constexpr _Iter + find_if_not(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {}) + { + while (__first != __last + && (bool)std::__invoke(__pred, std::__invoke(__proj, *__first))) + ++__first; + return __first; + } + + template> + _Comp = ranges::less> + constexpr const _Tp& + min(const _Tp& __a, const _Tp& __b, _Comp __comp = {}, _Proj __proj = {}) + { + if (std::__invoke(std::move(__comp), + std::__invoke(__proj, __b), + std::__invoke(__proj, __a))) + return __b; + else + return __a; + } + + template _Sent1, + input_iterator _Iter2, sentinel_for<_Iter2> _Sent2, + typename _Pred = ranges::equal_to, + typename _Proj1 = identity, typename _Proj2 = identity> + requires indirectly_comparable<_Iter1, _Iter2, _Pred, _Proj1, _Proj2> + constexpr pair<_Iter1, _Iter2> + mismatch(_Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Sent2 __last2, + _Pred __pred = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) + { + while (__first1 != __last1 && __first2 != __last2 + && (bool)std::__invoke(__pred, + std::__invoke(__proj1, *__first1), + std::__invoke(__proj2, *__first2))) + { + ++__first1; + ++__first2; + } + return { std::move(__first1), std::move(__first2) }; + } + } // namespace __detail + + template> _Pred> + requires view<_Vp> && is_object_v<_Pred> + class filter_view : public view_interface> + { + private: + struct _Sentinel; + + struct _Iterator + { + private: + static constexpr auto + _S_iter_concept() + { + if constexpr (bidirectional_range<_Vp>) + return bidirectional_iterator_tag{}; + else if constexpr (forward_range<_Vp>) + return forward_iterator_tag{}; + else + return input_iterator_tag{}; + } + + static constexpr auto + _S_iter_cat() + { + using _Cat = iterator_traits>::iterator_category; + if constexpr (derived_from<_Cat, bidirectional_iterator_tag>) + return bidirectional_iterator_tag{}; + else if constexpr (derived_from<_Cat, forward_iterator_tag>) + return forward_iterator_tag{}; + else + return _Cat{}; + } + + friend filter_view; + + iterator_t<_Vp> _M_current = iterator_t<_Vp>(); + filter_view* _M_parent = nullptr; + + public: + using iterator_concept = decltype(_S_iter_concept()); + using iterator_category = decltype(_S_iter_cat()); + using value_type = range_value_t<_Vp>; + using difference_type = range_difference_t<_Vp>; + + _Iterator() = default; + + constexpr + _Iterator(filter_view& __parent, iterator_t<_Vp> __current) + : _M_current(std::move(__current)), + _M_parent(std::__addressof(__parent)) + { } + + constexpr iterator_t<_Vp> + base() const & + requires copyable> + { return _M_current; } + + constexpr iterator_t<_Vp> + base() && + { return std::move(_M_current); } + + constexpr range_reference_t<_Vp> + operator*() const + { return *_M_current; } + + constexpr iterator_t<_Vp> + operator->() const + requires __detail::__has_arrow> + && copyable> + { return _M_current; } + + constexpr _Iterator& + operator++() + { + _M_current = __detail::find_if(std::move(++_M_current), + ranges::end(_M_parent->_M_base), + std::ref(*_M_parent->_M_pred)); + return *this; + } + + constexpr void + operator++(int) + { ++*this; } + + constexpr _Iterator + operator++(int) requires forward_range<_Vp> + { + auto __tmp = *this; + ++*this; + return __tmp; + } + + constexpr _Iterator& + operator--() requires bidirectional_range<_Vp> + { + do + --_M_current; + while (!std::__invoke(*_M_parent->_M_pred, *_M_current)); + return *this; + } + + constexpr _Iterator + operator--(int) requires bidirectional_range<_Vp> + { + auto __tmp = *this; + --*this; + return __tmp; + } + + friend constexpr bool + operator==(const _Iterator& __x, const _Iterator& __y) + requires equality_comparable> + { return __x._M_current == __y._M_current; } + + friend constexpr range_rvalue_reference_t<_Vp> + iter_move(const _Iterator& __i) + noexcept(noexcept(ranges::iter_move(__i._M_current))) + { return ranges::iter_move(__i._M_current); } + + friend constexpr void + iter_swap(const _Iterator& __x, const _Iterator& __y) + noexcept(noexcept(ranges::iter_swap(__x._M_current, __y._M_current))) + requires indirectly_swappable> + { ranges::iter_swap(__x._M_current, __y._M_current); } + }; + + struct _Sentinel + { + private: + sentinel_t<_Vp> _M_end = sentinel_t<_Vp>(); + + constexpr bool + __equal(const _Iterator& __i) const + { return __i._M_current == _M_end; } + + public: + _Sentinel() = default; + + constexpr explicit + _Sentinel(filter_view& __parent) + : _M_end(ranges::end(__parent._M_base)) + { } + + constexpr sentinel_t<_Vp> + base() const + { return _M_end; } + + friend constexpr bool + operator==(const _Iterator& __x, const _Sentinel& __y) + { return __y.__equal(__x); } + }; + + _Vp _M_base = _Vp(); + __detail::__box<_Pred> _M_pred; + + public: + filter_view() = default; + + constexpr + filter_view(_Vp __base, _Pred __pred) + : _M_base(std::move(__base)), _M_pred(std::move(__pred)) + { } + + /* XXX: P3280 removes this constructor + template + requires viewable_range<_Range> + && constructible_from<_Vp, all_view<_Range>> + constexpr + filter_view(_Range&& __r, _Pred __pred) + : _M_base(views::all(std::forward<_Range>(__r))), + _M_pred(std::move(__pred)) + { } + */ + + constexpr _Vp + base() const& requires copy_constructible<_Vp> + { return _M_base; } + + constexpr _Vp + base() && + { return std::move(_M_base); } + + constexpr _Iterator + begin() + { + // XXX: we need to cache the result here as per [range.filter.view] + __glibcxx_assert(_M_pred.has_value()); + return {*this, __detail::find_if(ranges::begin(_M_base), + ranges::end(_M_base), + std::ref(*_M_pred))}; + } + + constexpr auto + end() + { + if constexpr (common_range<_Vp>) + return _Iterator{*this, ranges::end(_M_base)}; + else + return _Sentinel{*this}; + } + }; + + template + filter_view(_Range&&, _Pred) -> filter_view, _Pred>; + + namespace views + { + inline constexpr __adaptor::_RangeAdaptor filter + = [] (_Range&& __r, _Pred&& __p) + { + return filter_view{std::forward<_Range>(__r), std::forward<_Pred>(__p)}; + }; + } // namespace views + + template + requires view<_Vp> && is_object_v<_Fp> + && regular_invocable<_Fp&, range_reference_t<_Vp>> + class transform_view : public view_interface> + { + private: + template + struct _Sentinel; + + template + struct _Iterator + { + private: + using _Parent + = conditional_t<_Const, const transform_view, transform_view>; + using _Base = conditional_t<_Const, const _Vp, _Vp>; + + static constexpr auto + _S_iter_concept() + { + if constexpr (random_access_range<_Vp>) + return random_access_iterator_tag{}; + else if constexpr (bidirectional_range<_Vp>) + return bidirectional_iterator_tag{}; + else if constexpr (forward_range<_Vp>) + return forward_iterator_tag{}; + else + return input_iterator_tag{}; + } + + static constexpr auto + _S_iter_cat() + { + using _Cat = iterator_traits>::iterator_category; + if constexpr (derived_from<_Cat, contiguous_iterator_tag>) + return random_access_iterator_tag{}; + else + return _Cat{}; + } + + static constexpr decltype(auto) + __iter_move(const _Iterator& __i = {}) + noexcept(noexcept(std::__invoke(*__i._M_parent->_M_fun, + *__i._M_current))) + { + if constexpr (is_lvalue_reference_v) + return std::move(*__i); + else + return *__i; + } + + iterator_t<_Base> _M_current = iterator_t<_Base>(); + _Parent* _M_parent = nullptr; + + public: + using iterator_concept = decltype(_S_iter_concept()); + using iterator_category = decltype(_S_iter_cat()); + using value_type + = remove_cvref_t>>; + using difference_type = range_difference_t<_Base>; + + _Iterator() = default; + + constexpr + _Iterator(_Parent& __parent, iterator_t<_Base> __current) + : _M_current(std::move(__current)), + _M_parent(std::__addressof(__parent)) + { } + + constexpr + _Iterator(_Iterator __i) + requires _Const + && convertible_to, iterator_t<_Base>> + : _M_current(std::move(__i._M_current)), _M_parent(__i._M_parent) + { } + + constexpr iterator_t<_Base> + base() const & + requires copyable> + { return _M_current; } + + constexpr iterator_t<_Base> + base() && + { return std::move(_M_current); } + + constexpr decltype(auto) + operator*() const + { return std::__invoke(*_M_parent->_M_fun, *_M_current); } + + constexpr _Iterator& + operator++() + { + ++_M_current; + return *this; + } + + constexpr void + operator++(int) + { ++_M_current; } + + constexpr _Iterator + operator++(int) requires forward_range<_Base> + { + auto __tmp = *this; + ++*this; + return __tmp; + } + + constexpr _Iterator& + operator--() requires bidirectional_range<_Base> + { + --_M_current; + return *this; + } + + constexpr _Iterator + operator--(int) requires bidirectional_range<_Base> + { + auto __tmp = *this; + --*this; + return __tmp; + } + + constexpr _Iterator& + operator+=(difference_type __n) requires random_access_range<_Base> + { + _M_current += __n; + return *this; + } + + constexpr _Iterator& + operator-=(difference_type __n) requires random_access_range<_Base> + { + _M_current -= __n; + return *this; + } + + constexpr decltype(auto) + operator[](difference_type __n) const + requires random_access_range<_Base> + { return std::__invoke(*_M_parent->_M_fun, _M_current[__n]); } + + friend constexpr bool + operator==(const _Iterator& __x, const _Iterator& __y) + requires equality_comparable> + { return __x._M_current == __y._M_current; } + + friend constexpr bool + operator<(const _Iterator& __x, const _Iterator& __y) + requires random_access_range<_Base> + { return __x._M_current < __y._M_current; } + + friend constexpr bool + operator>(const _Iterator& __x, const _Iterator& __y) + requires random_access_range<_Base> + { return __y < __x; } + + friend constexpr bool + operator<=(const _Iterator& __x, const _Iterator& __y) + requires random_access_range<_Base> + { return !(__y < __x); } + + friend constexpr bool + operator>=(const _Iterator& __x, const _Iterator& __y) + requires random_access_range<_Base> + { return !(__x < __y); } + +#ifdef __cpp_lib_three_way_comparison + friend constexpr auto + operator<=>(const _Iterator& __x, const _Iterator& __y) + requires random_access_range<_Base> + && three_way_comparable> + { return __x._M_current <=> __y._M_current; } +#endif + + friend constexpr _Iterator + operator+(_Iterator __i, difference_type __n) + requires random_access_range<_Base> + { return {*__i._M_parent, __i._M_current + __n}; } + + friend constexpr _Iterator + operator+(difference_type __n, _Iterator __i) + requires random_access_range<_Base> + { return {*__i._M_parent, __i._M_current + __n}; } + + friend constexpr _Iterator + operator-(_Iterator __i, difference_type __n) + requires random_access_range<_Base> + { return {*__i._M_parent, __i._M_current - __n}; } + + friend constexpr difference_type + operator-(const _Iterator& __x, const _Iterator& __y) + requires random_access_range<_Base> + { return __x._M_current - __y._M_current; } + + friend constexpr decltype(auto) + iter_move(const _Iterator& __i) noexcept(noexcept(__iter_move())) + { return __iter_move(__i); } + + friend constexpr void + iter_swap(const _Iterator& __x, const _Iterator& __y) + noexcept(noexcept(ranges::iter_swap(__x._M_current, __y._M_current))) + requires indirectly_swappable> + { return ranges::iter_swap(__x._M_current, __y._M_current); } + + friend _Sentinel<_Const>; + }; + + template + struct _Sentinel + { + private: + using _Parent + = conditional_t<_Const, const transform_view, transform_view>; + using _Base = conditional_t<_Const, const _Vp, _Vp>; + + constexpr range_difference_t<_Base> + __distance_from(const _Iterator<_Const>& __i) const + { return _M_end - __i._M_current; } + + constexpr bool + __equal(const _Iterator<_Const>& __i) const + { return __i._M_current == _M_end; } + + sentinel_t<_Base> _M_end = sentinel_t<_Base>(); + + public: + _Sentinel() = default; + + constexpr explicit + _Sentinel(sentinel_t<_Base> __end) + : _M_end(__end) + { } + + constexpr + _Sentinel(_Sentinel __i) + requires _Const + && convertible_to, sentinel_t<_Base>> + : _M_end(std::move(__i._M_end)) + { } + + constexpr sentinel_t<_Base> + base() const + { return _M_end; } + + friend constexpr bool + operator==(const _Iterator<_Const>& __x, const _Sentinel& __y) + { return __y.__equal(__x); } + + friend constexpr range_difference_t<_Base> + operator-(const _Iterator<_Const>& __x, const _Sentinel& __y) + requires sized_sentinel_for, iterator_t<_Base>> + { return -__y.__distance_from(__x); } + + friend constexpr range_difference_t<_Base> + operator-(const _Sentinel& __y, const _Iterator<_Const>& __x) + requires sized_sentinel_for, iterator_t<_Base>> + { return __y.__distance_from(__x); } + }; + + _Vp _M_base = _Vp(); + __detail::__box<_Fp> _M_fun; + + public: + transform_view() = default; + + constexpr + transform_view(_Vp __base, _Fp __fun) + : _M_base(std::move(__base)), _M_fun(std::move(__fun)) + { } + + /* XXX: P3280 removes this constructor + template + requires viewable_range<_Range> + && constructible_from<_Vp, all_view<_Range>> + constexpr + transform_view(_Range&& __r, _Fp __fun) + : _M_base(views::all(std::forward<_Range>(__r))) + { + } + */ + + constexpr _Vp + base() const& requires copy_constructible<_Vp> + { return _M_base ; } + + constexpr _Vp + base() && + { return std::move(_M_base); } + + constexpr _Iterator + begin() + { return _Iterator{*this, ranges::begin(_M_base)}; } + + constexpr _Iterator + begin() const + requires range + && regular_invocable> + { return _Iterator{*this, ranges::begin(_M_base)}; } + + constexpr _Sentinel + end() + { return _Sentinel{ranges::end(_M_base)}; } + + constexpr _Iterator + end() requires common_range<_Vp> + { return _Iterator{*this, ranges::end(_M_base)}; } + + constexpr _Sentinel + end() const + requires range + && regular_invocable> + { return _Sentinel{ranges::end(_M_base)}; } + + constexpr _Iterator + end() const + requires common_range + && regular_invocable> + { return _Iterator{*this, ranges::end(_M_base)}; } + + constexpr auto + size() requires sized_range<_Vp> + { return ranges::size(_M_base); } + + constexpr auto + size() const requires sized_range + { return ranges::size(_M_base); } + }; + + template + transform_view(_Range&&, _Fp) -> transform_view, _Fp>; + + namespace views + { + inline constexpr __adaptor::_RangeAdaptor transform + = [] (_Range&& __r, _Fp&& __f) + { + return transform_view{std::forward<_Range>(__r), std::forward<_Fp>(__f)}; + }; + } // namespace views + + template + class take_view : public view_interface> + { + private: + template + struct _Sentinel + { + private: + using _Base = conditional_t<_Const, const _Vp, _Vp>; + using _CI = counted_iterator>; + + sentinel_t<_Base> _M_end = sentinel_t<_Base>(); + + public: + _Sentinel() = default; + + constexpr explicit + _Sentinel(sentinel_t<_Base> __end) + : _M_end(__end) + { } + + constexpr + _Sentinel(_Sentinel __s) + requires _Const && convertible_to, sentinel_t<_Base>> + : _M_end(std::move(__s._M_end)) + { } + + constexpr sentinel_t<_Base> + base() const + { return _M_end; } + + friend constexpr bool operator==(const _CI& __y, const _Sentinel& __x) + { return __y.count() == 0 || __y.base() == __x._M_end; } + }; + + _Vp _M_base = _Vp(); + range_difference_t<_Vp> _M_count = 0; + + public: + take_view() = default; + + constexpr + take_view(_Vp base, range_difference_t<_Vp> __count) + : _M_base(std::move(base)), _M_count(std::move(__count)) + { } + + /* XXX: P3280 removes this constructor + template + requires constructible_from<_Vp, all_view<_Range>> + constexpr + take_view(_Range&& __r, range_difference_t<_Vp> __count) + : _M_base(views::all(std::forward<_Range>(__r))), _M_count(__count) + { } + */ + + constexpr _Vp + base() const& requires copy_constructible<_Vp> + { return _M_base; } + + constexpr _Vp + base() && + { return std::move(_M_base); } + + constexpr auto + begin() requires (!__detail::__simple_view<_Vp>) + { + if constexpr (sized_range<_Vp>) + { + if constexpr (random_access_range<_Vp>) + return ranges::begin(_M_base); + else + return counted_iterator{ranges::begin(_M_base), size()}; + } + else + return counted_iterator{ranges::begin(_M_base), _M_count}; + } + + constexpr auto + begin() const requires range + { + if constexpr (sized_range) + { + if constexpr (random_access_range) + return ranges::begin(_M_base); + else + return counted_iterator{ranges::begin(_M_base), size()}; + } + else + return counted_iterator{ranges::begin(_M_base), _M_count}; + } + + constexpr auto + end() requires (!__detail::__simple_view<_Vp>) + { + if constexpr (sized_range<_Vp>) + { + if constexpr (random_access_range<_Vp>) + return ranges::begin(_M_base) + size(); + else + return default_sentinel; + } + else + return _Sentinel{ranges::end(_M_base)}; + } + + constexpr auto + end() const requires range + { + if constexpr (sized_range) + { + if constexpr (random_access_range) + return ranges::begin(_M_base) + size(); + else + return default_sentinel; + } + else + return _Sentinel{ranges::end(_M_base)}; + } + + constexpr auto + size() requires sized_range<_Vp> + { + auto __n = ranges::size(_M_base); + return __detail::min(__n, static_cast(_M_count)); + } + + constexpr auto + size() const requires sized_range + { + auto __n = ranges::size(_M_base); + return __detail::min(__n, static_cast(_M_count)); + } + }; + + template + take_view(_Range&&, range_difference_t<_Range>) + -> take_view>; + + namespace views + { + inline constexpr __adaptor::_RangeAdaptor take + = [] (_Range&& __r, _Tp&& __n) + { + return take_view{std::forward<_Range>(__r), std::forward<_Tp>(__n)}; + }; + } // namespace views + + template + requires input_range<_Vp> && is_object_v<_Pred> + && indirect_unary_predicate> + class take_while_view : public view_interface> + { + template + struct _Sentinel + { + private: + using _Base = conditional_t<_Const, const _Vp, _Vp>; + + sentinel_t<_Base> _M_end = sentinel_t<_Base>(); + const _Pred* _M_pred = nullptr; + + public: + _Sentinel() = default; + + constexpr explicit + _Sentinel(sentinel_t<_Base> __end, const _Pred* __pred) + : _M_end(__end), _M_pred(__pred) + { } + + constexpr + _Sentinel(_Sentinel __s) + requires _Const && convertible_to, sentinel_t<_Base>> + : _M_end(__s._M_end), _M_pred(__s._M_pred) + { } + + constexpr sentinel_t<_Base> + base() const { return _M_end; } + + friend constexpr bool + operator==(const iterator_t<_Base>& __x, const _Sentinel& __y) + { return __y._M_end == __x || !std::__invoke(*__y._M_pred, *__x); } + }; + + _Vp _M_base; + __detail::__box<_Pred> _M_pred; + + public: + take_while_view() = default; + + constexpr + take_while_view(_Vp base, _Pred __pred) + : _M_base(std::move(base)), _M_pred(std::move(__pred)) + { + } + + constexpr _Vp + base() const& requires copy_constructible<_Vp> + { return _M_base; } + + constexpr _Vp + base() && + { return std::move(_M_base); } + + constexpr const _Pred& + pred() const + { return *_M_pred; } + + constexpr auto + begin() requires (!__detail::__simple_view<_Vp>) + { return ranges::begin(_M_base); } + + constexpr auto + begin() const requires range + { return ranges::begin(_M_base); } + + constexpr auto + end() requires (!__detail::__simple_view<_Vp>) + { return _Sentinel(ranges::end(_M_base), + std::__addressof(*_M_pred)); } + + constexpr auto + end() const requires range + { return _Sentinel(ranges::end(_M_base), + std::__addressof(*_M_pred)); } + }; + + template + take_while_view(_Range&&, _Pred) + -> take_while_view, _Pred>; + + namespace views + { + inline constexpr __adaptor::_RangeAdaptor take_while + = [] (_Range&& __r, _Pred&& __p) + { + return take_while_view{std::forward<_Range>(__r), std::forward<_Pred>(__p)}; + }; + } // namespace views + + template + class drop_view : public view_interface> + { + private: + _Vp _M_base; + range_difference_t<_Vp> _M_count; + + public: + drop_view() = default; + + constexpr + drop_view(_Vp __base, range_difference_t<_Vp> __count) + : _M_base(std::move(__base)), _M_count(__count) + { __glibcxx_assert(__count >= 0); } + + constexpr _Vp + base() const& requires copy_constructible<_Vp> + { return _M_base; } + + constexpr _Vp + base() && + { return std::move(_M_base); } + + constexpr auto + begin() requires (!(__detail::__simple_view<_Vp> + && random_access_range<_Vp>)) + { + // XXX: we need to cache the result here as per [range.drop.view] + return ranges::next(ranges::begin(_M_base), _M_count, + ranges::end(_M_base)); + } + + constexpr auto + begin() const requires random_access_range + { + return ranges::next(ranges::begin(_M_base), _M_count, + ranges::end(_M_base)); + } + + constexpr auto + end() requires (!__detail::__simple_view<_Vp>) + { return ranges::end(_M_base); } + + constexpr auto + end() const requires range + { return ranges::end(_M_base); } + + constexpr auto + size() requires sized_range<_Vp> + { + const auto __s = ranges::size(_M_base); + const auto __c = static_cast(_M_count); + return __s < __c ? 0 : __s - __c; + } + + constexpr auto + size() const requires sized_range + { + const auto __s = ranges::size(_M_base); + const auto __c = static_cast(_M_count); + return __s < __c ? 0 : __s - __c; + } + }; + + template + drop_view(_Range&&, range_difference_t<_Range>) + -> drop_view>; + + namespace views + { + inline constexpr __adaptor::_RangeAdaptor drop + = [] (_Range&& __r, _Tp&& __n) + { + return drop_view{std::forward<_Range>(__r), std::forward<_Tp>(__n)}; + }; + } // namespace views + + template + requires input_range<_Vp> && is_object_v<_Pred> + && indirect_unary_predicate> + class drop_while_view : public view_interface> + { + private: + _Vp _M_base; + __detail::__box<_Pred> _M_pred; + + public: + drop_while_view() = default; + + constexpr + drop_while_view(_Vp __base, _Pred __pred) + : _M_base(std::move(__base)), _M_pred(std::move(__pred)) + { } + + constexpr _Vp + base() const& requires copy_constructible<_Vp> + { return _M_base; } + + constexpr _Vp + base() && + { return std::move(_M_base); } + + constexpr const _Pred& + pred() const + { return *_M_pred; } + + constexpr auto + begin() + { + // XXX: we need to cache the result here as per [range.drop.while.view] + return __detail::find_if_not(ranges::begin(_M_base), + ranges::end(_M_base), + std::cref(*_M_pred)); + } + + constexpr auto + end() + { return ranges::end(_M_base); } + }; + + template + drop_while_view(_Range&&, _Pred) + -> drop_while_view, _Pred>; + + namespace views + { + inline constexpr __adaptor::_RangeAdaptor drop_while + = [] (_Range&& __r, _Pred&& __p) + { + return drop_while_view{std::forward<_Range>(__r), + std::forward<_Pred>(__p)}; + }; + } // namespace views + + template + requires view<_Vp> && input_range> + && (is_reference_v> + || view>) + class join_view : public view_interface> + { + private: + using _InnerRange = range_reference_t<_Vp>; + + template + struct _Sentinel; + + template + struct _Iterator + { + private: + using _Parent = conditional_t<_Const, const join_view, join_view>; + using _Base = conditional_t<_Const, const _Vp, _Vp>; + + static constexpr bool _S_ref_is_glvalue + = is_reference_v>; + + constexpr void + _M_satisfy() + { + auto __update_inner = [this] (range_reference_t<_Base> __x) -> auto& + { + if constexpr (_S_ref_is_glvalue) + return __x; + else + return (_M_parent->_M_inner = views::all(std::move(__x))); + }; + + for (; _M_outer != ranges::end(_M_parent->_M_base); ++_M_outer) + { + auto& inner = __update_inner(*_M_outer); + _M_inner = ranges::begin(inner); + if (_M_inner != ranges::end(inner)) + return; + } + + if constexpr (_S_ref_is_glvalue) + _M_inner = iterator_t>(); + } + + static constexpr auto + _S_iter_concept() + { + if constexpr (_S_ref_is_glvalue + && bidirectional_range<_Base> + && bidirectional_range>) + return bidirectional_iterator_tag{}; + else if constexpr (_S_ref_is_glvalue + && forward_range<_Base> + && forward_range>) + return forward_iterator_tag{}; + else + return input_iterator_tag{}; + } + + static constexpr auto + _S_iter_cat() + { + using _OuterCat + = iterator_traits>::iterator_category; + using _InnerCat + = iterator_traits>> + ::iterator_category; + if constexpr (_S_ref_is_glvalue + && derived_from<_OuterCat, bidirectional_iterator_tag> + && derived_from<_InnerCat, bidirectional_iterator_tag>) + return bidirectional_iterator_tag{}; + else if constexpr (_S_ref_is_glvalue + && derived_from<_OuterCat, forward_iterator_tag> + && derived_from<_InnerCat, forward_iterator_tag>) + return forward_iterator_tag{}; + else if constexpr (derived_from<_OuterCat, input_iterator_tag> + && derived_from<_InnerCat, input_iterator_tag>) + return input_iterator_tag{}; + else + return output_iterator_tag{}; + } + + iterator_t<_Base> _M_outer = iterator_t<_Base>(); + iterator_t> _M_inner + = iterator_t>(); + _Parent* _M_parent = nullptr; + + public: + using iterator_concept = decltype(_S_iter_concept()); + using iterator_category = decltype(_S_iter_cat()); + using value_type = range_value_t>; + using difference_type + = common_type_t, + range_difference_t>>; + + _Iterator() = default; + + // XXX: had to change the type of __outer from iterator_t<_Vp> to + // iterator_t<_Base> here, a possible defect in the spec? + constexpr + _Iterator(_Parent& __parent, iterator_t<_Base> __outer) + : _M_outer(std::move(__outer)), + _M_parent(std::__addressof(__parent)) + { _M_satisfy(); } + + constexpr + _Iterator(_Iterator __i) + requires _Const + && convertible_to, iterator_t<_Base>> + && convertible_to, + iterator_t>> + : _M_outer(std::move(__i._M_outer)), _M_inner(__i._M_inner), + _M_parent(__i._M_parent) + { } + + constexpr decltype(auto) + operator*() const + { return *_M_inner; } + + constexpr iterator_t<_Base> + operator->() const + requires __detail::__has_arrow> + && copyable> + { return _M_inner; } + + constexpr _Iterator& + operator++() + { + auto&& __inner_range = [this] () -> decltype(auto) { + if constexpr (_S_ref_is_glvalue) + return *_M_outer; + else + return _M_parent->_M_inner; + }(); + if (++_M_inner == ranges::end(__inner_range)) + { + ++_M_outer; + _M_satisfy(); + } + return *this; + } + + constexpr void + operator++(int) + { ++*this; } + + constexpr _Iterator + operator++(int) + requires _S_ref_is_glvalue && forward_range<_Base> + && forward_range> + { + auto __tmp = *this; + ++*this; + return __tmp; + } + + constexpr _Iterator& + operator--() + requires _S_ref_is_glvalue && bidirectional_range<_Base> + && bidirectional_range> + { + if (_M_outer == ranges::end(_M_parent->_M_base)) + _M_inner = ranges::end(*--_M_outer); + while (_M_inner == ranges::begin(*_M_outer)) + _M_inner = ranges::end(*--_M_outer); + --_M_inner; + return *this; + } + + constexpr _Iterator + operator--(int) + requires _S_ref_is_glvalue && bidirectional_range<_Base> + && bidirectional_range> + { + auto __tmp = *this; + --*this; + return __tmp; + } + + friend constexpr bool + operator==(const _Iterator& __x, const _Iterator& __y) + requires _S_ref_is_glvalue + && equality_comparable> + && equality_comparable>> + { + return (__x._M_outer == __y._M_outer + && __x._M_inner == __y._M_inner); + } + + friend constexpr decltype(auto) + iter_move(const _Iterator& __i) + noexcept(noexcept(ranges::iter_move(__i._M_inner))) + { return ranges::iter_move(__i._M_inner); } + + friend constexpr void + iter_swap(const _Iterator& __x, const _Iterator& __y) + noexcept(noexcept(ranges::iter_swap(__x._M_inner, __y._M_inner))) + { return ranges::iter_swap(__x._M_inner, __y._M_inner); } + + friend _Sentinel<_Const>; + }; + + template + struct _Sentinel + { + private: + using _Parent = conditional_t<_Const, const join_view, join_view>; + using _Base = conditional_t<_Const, const _Vp, _Vp>; + + constexpr bool + __equal(const _Iterator<_Const>& __i) const + { return __i._M_outer == _M_end; } + + sentinel_t<_Base> _M_end = sentinel_t<_Base>(); + + public: + _Sentinel() = default; + + constexpr explicit + _Sentinel(_Parent& __parent) + : _M_end(ranges::end(__parent._M_base)) + { } + + constexpr + _Sentinel(_Sentinel __s) + requires _Const && convertible_to, sentinel_t<_Base>> + : _M_end(std::move(__s._M_end)) + { } + + friend constexpr bool + operator==(const _Iterator<_Const>& __x, const _Sentinel& __y) + { return __y.__equal(__x); } + }; + + _Vp _M_base = _Vp(); + + // XXX: _M_inner is "present only when !is_reference_v<_InnerRange>" + // Applied P3278 and made this field mutable. + [[no_unique_address]] mutable + conditional_t, + all_view<_InnerRange>, __detail::_Empty> _M_inner; + + public: + join_view() = default; + + constexpr explicit + join_view(_Vp __base) + : _M_base(std::move(__base)) + { } + + /* XXX: P3280 removes this constructor + template + requires viewable_range<_Range> + && constructible_from<_Vp, all_view<_Range>> + constexpr explicit + join_view(_Range&& __r) + : _M_base(views::all(std::forward<_Range>(__r))) + { } + */ + + constexpr _Vp + base() const& requires copy_constructible<_Vp> + { return _M_base; } + + constexpr _Vp + base() && + { return std::move(_M_base); } + + constexpr auto + begin() + { + return _Iterator<__detail::__simple_view<_Vp>>{*this, + ranges::begin(_M_base)}; + } + + constexpr auto + begin() const + requires input_range + && is_reference_v> + { + return _Iterator{*this, ranges::begin(_M_base)}; + } + + constexpr auto + end() + { + if constexpr (forward_range<_Vp> && is_reference_v<_InnerRange> + && forward_range<_InnerRange> + && common_range<_Vp> && common_range<_InnerRange>) + return _Iterator<__detail::__simple_view<_Vp>>{*this, + ranges::end(_M_base)}; + else + return _Sentinel<__detail::__simple_view<_Vp>>{*this}; + } + + constexpr auto + end() const + requires input_range + && is_reference_v> + { + if constexpr (forward_range + && is_reference_v> + && forward_range> + && common_range + && common_range>) + return _Iterator{*this, ranges::end(_M_base)}; + else + return _Sentinel{*this}; + } + }; + + template + explicit join_view(_Range&&) -> join_view>; + + namespace views + { + inline constexpr __adaptor::_RangeAdaptorClosure join + = [] (_Range&& __r) + { + return join_view{std::forward<_Range>(__r)}; + }; + } // namespace views + + namespace __detail + { + template + struct __require_constant; + + template + concept __tiny_range = sized_range<_Range> + && requires + { typename __require_constant::size()>; } + && (remove_reference_t<_Range>::size() <= 1); + } + + template + requires view<_Vp> && view<_Pattern> + && indirectly_comparable, iterator_t<_Pattern>, + ranges::equal_to> + && (forward_range<_Vp> || __detail::__tiny_range<_Pattern>) + class split_view : public view_interface> + { + private: + template + struct _InnerIter; + + template + struct _OuterIter + { + private: + using _Parent = conditional_t<_Const, const split_view, split_view>; + using _Base = conditional_t<_Const, const _Vp, _Vp>; + + constexpr bool + __at_end() const + { return _M_current == ranges::end(_M_parent->_M_base); } + + // XXX: [24.7.11.3.1] + // Many of the following specifications refer to the notional member + // current of outer-iterator. current is equivalent to current_­ if + // V models forward_range, and parent_->current_­ otherwise. + constexpr auto& + __current() + { + if constexpr (forward_range<_Vp>) + return _M_current; + else + return _M_parent->_M_current; + } + + constexpr auto& + __current() const + { + if constexpr (forward_range<_Vp>) + return _M_current; + else + return _M_parent->_M_current; + } + + _Parent* _M_parent = nullptr; + + // XXX: _M_current is present only if "V models forward_range" + [[no_unique_address]] + conditional_t, + iterator_t<_Base>, __detail::_Empty> _M_current; + + public: + using iterator_concept = conditional_t, + forward_iterator_tag, + input_iterator_tag>; + using iterator_category = input_iterator_tag; + using difference_type = range_difference_t<_Base>; + + struct value_type : view_interface + { + private: + _OuterIter _M_i = _OuterIter(); + + public: + value_type() = default; + + constexpr explicit + value_type(_OuterIter __i) + : _M_i(std::move(__i)) + { } + + constexpr _InnerIter<_Const> + begin() const + requires copyable<_OuterIter> + { return _InnerIter<_Const>{_M_i}; } + + constexpr _InnerIter<_Const> + begin() + requires (!copyable<_OuterIter>) + { return _InnerIter<_Const>{std::move(_M_i)}; } + + constexpr default_sentinel_t + end() const + { return default_sentinel; } + }; + + _OuterIter() = default; + + constexpr explicit + _OuterIter(_Parent& __parent) requires (!forward_range<_Base>) + : _M_parent(address(__parent)) + { } + + constexpr + _OuterIter(_Parent& __parent, iterator_t<_Base> __current) + requires forward_range<_Base> + : _M_parent(std::__addressof(__parent)), + _M_current(std::move(__current)) + { } + + constexpr + _OuterIter(_OuterIter __i) + requires _Const + && convertible_to, iterator_t> + : _M_parent(__i._M_parent), _M_current(std::move(__i._M_current)) + { } + + constexpr value_type + operator*() const + { return value_type{*this}; } + + constexpr _OuterIter& + operator++() + { + const auto __end = ranges::end(_M_parent->_M_base); + if (_M_current == __end) + return *this; + const auto [__pbegin, __pend] = subrange{_M_parent->_M_pattern}; + if (__pbegin == __pend) + ++_M_current; + else + do + { + auto [__b, __p] + = __detail::mismatch(std::move(_M_current), __end, + __pbegin, __pend); + _M_current = std::move(__b); + if (__p == __pend) + break; + } while (++_M_current != __end); + return *this; + } + + constexpr decltype(auto) + operator++(int) + { + if constexpr (forward_range<_Base>) + { + auto __tmp = *this; + ++*this; + return __tmp; + } + else + ++*this; + } + + friend constexpr bool + operator==(const _OuterIter& __x, const _OuterIter& __y) + requires forward_range<_Base> + { return __x._M_current == __y._M_current; } + + friend constexpr bool + operator==(const _OuterIter& __x, default_sentinel_t) + { return __x.__at_end(); }; + + friend _InnerIter<_Const>; + }; + + template + struct _InnerIter + { + private: + using _Base = conditional_t<_Const, const _Vp, _Vp>; + + constexpr bool + __at_end() const + { + auto [__pcur, __pend] = subrange{_M_i._M_parent->_M_pattern}; + auto __end = ranges::end(_M_i._M_parent->_M_base); + if constexpr (__detail::__tiny_range<_Pattern>) + { + const auto& __cur = _M_i.__current(); + if (__cur == __end) + return true; + if (__pcur == __pend) + return _M_incremented; + return *__cur == *__pcur; + } + else + { + auto __cur = _M_i.__current(); + if (__cur == __end) + return true; + if (__pcur == __pend) + return _M_incremented; + do + { + if (*__cur != *__pcur) + return false; + if (++__pcur == __pend) + return true; + } while (++__cur != __end); + return false; + } + } + + static constexpr auto + _S_iter_cat() + { + using _Cat = iterator_traits>::iterator_category; + if constexpr (derived_from<_Cat, forward_iterator_tag>) + return forward_iterator_tag{}; + else + return _Cat{}; + } + + static constexpr decltype(auto) + __iter_move(const _InnerIter& __i = {}) + noexcept(noexcept(ranges::iter_move(__i._M_i.__current()))) + { return ranges::iter_move(__i._M_i.__current()); } + + static constexpr void + __iter_swap(const _InnerIter& __x = {}, const _InnerIter& __y = {}) + noexcept(noexcept(ranges::iter_swap(__x._M_i.__current(), + __y._M_i.__current()))) + { ranges::iter_swap(__x._M_i.__current(), __y._M_i.__current()); } + + _OuterIter<_Const> _M_i = _OuterIter<_Const>(); + bool _M_incremented = false; + + public: + using iterator_concept = typename _OuterIter<_Const>::iterator_concept; + using iterator_category = decltype(_S_iter_cat()); + using value_type = range_value_t<_Base>; + using difference_type = range_difference_t<_Base>; + + _InnerIter() = default; + + constexpr explicit + _InnerIter(_OuterIter<_Const> __i) + : _M_i(std::move(__i)) + { } + + constexpr decltype(auto) + operator*() const + { return *_M_i._M_current; } + + constexpr _InnerIter& + operator++() + { + _M_incremented = true; + if constexpr (!forward_range<_Base>) + if constexpr (_Pattern::size() == 0) + return *this; + ++_M_i.__current(); + return *this; + } + + constexpr decltype(auto) + operator++(int) + { + if constexpr (forward_range<_Vp>) + { + auto __tmp = *this; + ++*this; + return __tmp; + } + else + ++*this; + } + + friend constexpr bool + operator==(const _InnerIter& __x, const _InnerIter& __y) + requires forward_range<_Base> + { return __x._M_i.__current() == __y._M_i.__current(); } + + friend constexpr bool + operator==(const _InnerIter& __x, default_sentinel_t) + { return __x.__at_end(); } + + friend constexpr decltype(auto) + iter_move(const _InnerIter& __i) noexcept(noexcept(__iter_move())) + { return __iter_move(__i); } + + friend constexpr void + iter_swap(const _InnerIter& __x, const _InnerIter& __y) + noexcept(noexcept(__iter_swap())) + requires indirectly_swappable> + { __iter_swap(__x, __y); } + }; + + _Vp _M_base = _Vp(); + _Pattern _M_pattern = _Pattern(); + + // XXX: _M_current is "present only if !forward_range" + [[no_unique_address]] + conditional_t, + iterator_t<_Vp>, __detail::_Empty> _M_current; + + + public: + split_view() = default; + + constexpr + split_view(_Vp __base, _Pattern __pattern) + : _M_base(std::move(__base)), _M_pattern(std::move(__pattern)) + { } + + /* XXX: P3280 removes this constructor + template + requires constructible_from<_Vp, all_view<_Range>> + && constructible_from<_Pattern, all_view<_Pred>> + constexpr + split_view(_Range&& __r, _Pred&& __p) + : _M_base(views::all(std::forward<_Range>(__r))), + _M_pattern(views::all(std::forward<_Pred>(__p))) + { } + */ + + template + requires constructible_from<_Vp, all_view<_Range>> + && constructible_from<_Pattern, single_view>> + constexpr + split_view(_Range&& __r, range_value_t<_Range> __e) + : _M_base(views::all(std::forward<_Range>(__r))), + _M_pattern(std::move(__e)) + { } + + constexpr _Vp + base() const& requires copy_constructible<_Vp> + { return _M_base; } + + constexpr _Vp + base() && + { return std::move(_M_base); } + + constexpr auto + begin() + { + if constexpr (forward_range<_Vp>) + return _OuterIter<__detail::__simple_view<_Vp>>{*this, + ranges::begin(_M_base)}; + else + { + _M_current = ranges::begin(_M_base); + return _OuterIter{*this}; + } + } + + constexpr auto + begin() const requires forward_range<_Vp> && forward_range + { + return _OuterIter{*this, ranges::begin(_M_base)}; + } + + constexpr auto + end() requires forward_range<_Vp> && common_range<_Vp> + { + return _OuterIter<__detail::__simple_view<_Vp>>{*this, ranges::end(_M_base)}; + } + + constexpr auto + end() const + { + if constexpr (forward_range<_Vp> + && forward_range + && common_range) + return _OuterIter{*this, ranges::end(_M_base)}; + else + return default_sentinel; + } + }; + + template + split_view(_Range&&, _Pred&&) + -> split_view, all_view<_Pred>>; + + template + split_view(_Range&&, range_value_t<_Range>) + -> split_view, single_view>>; + + namespace views + { + inline constexpr __adaptor::_RangeAdaptor split + = [] (_Range&& __r, _Fp&& __f) + { + return split_view{std::forward<_Range>(__r), std::forward<_Fp>(__f)}; + }; + } // namespace views + + namespace views + { + struct _Counted + { + template + constexpr auto + operator()(_Iter __i, iter_difference_t<_Iter> __n) const + { + if constexpr (random_access_iterator<_Iter>) + return subrange{__i, __i + __n}; + else + return subrange{counted_iterator{std::move(__i), __n}, + default_sentinel}; + } + }; + + inline constexpr _Counted counted{}; + } // namespace views + + template + requires (!common_range<_Vp>) && copyable> + class common_view : public view_interface> + { + private: + _Vp _M_base = _Vp(); + + public: + common_view() = default; + + constexpr explicit + common_view(_Vp __r) + : _M_base(std::move(__r)) + { } + + /* XXX: P3280 doesn't remove this constructor, but I think it should? + template + requires (!common_range<_Range>) && constructible_from<_Vp, all_view<_Range>> + constexpr explicit + common_view(_Range&& __r) + : _M_base(views::all(std::forward<_Range>(__r))) + { } + */ + + constexpr _Vp + base() const& requires copy_constructible<_Vp> + { return _M_base; } + + constexpr _Vp + base() && + { return std::move(_M_base); } + + constexpr auto + begin() + { + if constexpr (random_access_range<_Vp> && sized_range<_Vp>) + return ranges::begin(_M_base); + else + return common_iterator, sentinel_t<_Vp>> + (ranges::begin(_M_base)); + } + + constexpr auto + begin() const requires range + { + if constexpr (random_access_range && sized_range) + return ranges::begin(_M_base); + else + return common_iterator, sentinel_t> + (ranges::begin(_M_base)); + } + + constexpr auto + end() + { + if constexpr (random_access_range<_Vp> && sized_range<_Vp>) + return ranges::begin(_M_base) + ranges::size(_M_base); + else + return common_iterator, sentinel_t<_Vp>> + (ranges::end(_M_base)); + } + + constexpr auto + end() const requires range + { + if constexpr (random_access_range && sized_range) + return ranges::begin(_M_base) + ranges::size(_M_base); + else + return common_iterator, sentinel_t> + (ranges::end(_M_base)); + } + + constexpr auto + size() requires sized_range<_Vp> + { return ranges::size(_M_base); } + + constexpr auto + size() const requires sized_range + { return ranges::size(_M_base); } + }; + + template + common_view(_Range&&) -> common_view>; + + namespace views + { + inline constexpr __adaptor::_RangeAdaptorClosure common + = [] (_Range&& __r) + { + if constexpr (common_range<_Range> + && requires { views::all(std::forward<_Range>(__r)); }) + return views::all(std::forward<_Range>(__r)); + else + return common_view{std::forward<_Range>(__r)}; + }; + + } // namespace views + + template + requires bidirectional_range<_Vp> + class reverse_view : public view_interface> + { + private: + _Vp _M_base = _Vp(); + + public: + reverse_view() = default; + + constexpr explicit + reverse_view(_Vp __r) + : _M_base(std::move(__r)) + { } + + /* XXX: P3280 removes this constructor + template + requires bidirectional_range<_Range> && constructible_from<_Vp, all_view<_Range>> + constexpr explicit + reverse_view(_Range&& __r) + : _M_base(views::all(std::forward<_Range>(__r))) + { } + */ + + constexpr _Vp + base() const& requires copy_constructible<_Vp> + { return _M_base; } + + constexpr _Vp + base() && + { return std::move(_M_base); } + + constexpr reverse_iterator> + begin() + { + // XXX: we need to cache the result here as per [range.reverse.view] + return make_reverse_iterator(ranges::next(ranges::begin(_M_base), + ranges::end(_M_base))); + } + + constexpr auto + begin() requires common_range<_Vp> + { return make_reverse_iterator(ranges::end(_M_base)); } + + constexpr auto + begin() const requires common_range + { return make_reverse_iterator(ranges::end(_M_base)); } + + constexpr reverse_iterator> + end() + { return make_reverse_iterator(ranges::begin(_M_base)); } + + constexpr auto + end() const requires common_range + { return make_reverse_iterator(ranges::begin(_M_base)); } + + constexpr auto + size() requires sized_range<_Vp> + { return ranges::size(_M_base); } + + constexpr auto + size() const requires sized_range + { return ranges::size(_M_base); } + }; + + template + reverse_view(_Range&&) -> reverse_view>; + + namespace views + { + namespace __detail + { + template + inline constexpr bool __is_reversible_subrange = false; + + template + inline constexpr bool + __is_reversible_subrange, + reverse_iterator<_Iter>, + _Kind>> = true; + + template + inline constexpr bool __is_reverse_view = false; + + template + inline constexpr bool __is_reverse_view> = true; + } + + inline constexpr __adaptor::_RangeAdaptorClosure reverse + = [] (_Range&& __r) + { + using _Tp = remove_cvref_t<_Range>; + if constexpr (__detail::__is_reverse_view<_Tp>) + return std::forward<_Range>(__r).base(); + else if constexpr (__detail::__is_reversible_subrange<_Tp>) + { + using _Iter = decltype(ranges::begin(__r).base()); + if constexpr (sized_range<_Tp>) + return subrange<_Iter, _Iter, subrange_kind::sized> + (__r.end().base(), __r.begin().base(), __r.size()); + else + return subrange<_Iter, _Iter, subrange_kind::unsized> + (__r.end().base(), __r.begin().base()); + } + else + return reverse_view{std::forward<_Range>(__r)}; + }; + } // namespace views + + namespace __detail + { + template + concept __has_tuple_element = requires(_Tp __t) + { + typename tuple_size<_Tp>::type; + requires _Nm < tuple_size_v<_Tp>; + typename tuple_element_t<_Nm, _Tp>; + // XXX: we applied P3323 here + { std::get<_Nm>(__t) } + -> convertible_to&>; + }; + } + + template + requires view<_Vp> + && __detail::__has_tuple_element, _Nm> + && __detail::__has_tuple_element>, + _Nm> + class elements_view : public view_interface> + { + public: + elements_view() = default; + + constexpr explicit + elements_view(_Vp base) + : _M_base(std::move(base)) + { } + + constexpr _Vp + base() const& requires copy_constructible<_Vp> + { return _M_base; } + + constexpr _Vp + base() && + { return std::move(_M_base); } + + constexpr auto + begin() requires (!__detail::__simple_view<_Vp>) + { return _Iterator(ranges::begin(_M_base)); } + + constexpr auto + begin() const requires __detail::__simple_view<_Vp> + { return _Iterator(ranges::begin(_M_base)); } + + constexpr auto + end() requires (!__detail::__simple_view<_Vp>) + { return ranges::end(_M_base); } + + constexpr auto + end() const requires __detail::__simple_view<_Vp> + { return ranges::end(_M_base); } + + constexpr auto + size() requires sized_range<_Vp> + { return ranges::size(_M_base); } + + constexpr auto + size() const requires sized_range + { return ranges::size(_M_base); } + + private: + template + struct _Iterator + { + using _Base = conditional_t<_Const, const _Vp, _Vp>; + + iterator_t<_Base> _M_current; + + friend _Iterator; + + public: + using iterator_category + = typename iterator_traits>::iterator_category; + using value_type + = remove_cvref_t>>; + using difference_type = range_difference_t<_Base>; + + _Iterator() = default; + + constexpr explicit + _Iterator(iterator_t<_Base> current) + : _M_current(std::move(current)) + { } + + constexpr + _Iterator(_Iterator i) + requires _Const && convertible_to, iterator_t<_Base>> + : _M_current(std::move(i._M_current)) + { } + + constexpr iterator_t<_Base> + base() const& + requires copyable> + { return _M_current; } + + constexpr iterator_t<_Base> + base() && + { return std::move(_M_current); } + + constexpr decltype(auto) + operator*() const + { return std::get<_Nm>(*_M_current); } + + constexpr _Iterator& + operator++() + { + ++_M_current; + return *this; + } + + constexpr void + operator++(int) requires (!forward_range<_Base>) + { ++_M_current; } + + constexpr _Iterator + operator++(int) requires forward_range<_Base> + { + auto __tmp = *this; + ++_M_current; + return __tmp; + } + + constexpr _Iterator& + operator--() requires bidirectional_range<_Base> + { + --_M_current; + return *this; + } + + constexpr _Iterator + operator--(int) requires bidirectional_range<_Base> + { + auto __tmp = *this; + --_M_current; + return __tmp; + } + + constexpr _Iterator& + operator+=(difference_type __n) + requires random_access_range<_Base> + { + _M_current += __n; + return *this; + } + + constexpr _Iterator& + operator-=(difference_type __n) + requires random_access_range<_Base> + { + _M_current -= __n; + return *this; + } + + constexpr decltype(auto) + operator[](difference_type __n) const + requires random_access_range<_Base> + { return std::get<_Nm>(*(_M_current + __n)); } + + friend constexpr bool + operator==(const _Iterator& __x, const _Iterator& __y) + requires equality_comparable> + { return __x._M_current == __y._M_current; } + + friend constexpr bool + operator==(const _Iterator& __x, const sentinel_t<_Base>& __y) + { return __x._M_current == __y; } + + friend constexpr bool + operator<(const _Iterator& __x, const _Iterator& __y) + requires random_access_range<_Base> + { return __x._M_current < __y._M_current; } + + friend constexpr bool + operator>(const _Iterator& __x, const _Iterator& __y) + requires random_access_range<_Base> + { return __y._M_current < __x._M_current; } + + friend constexpr bool + operator<=(const _Iterator& __x, const _Iterator& __y) + requires random_access_range<_Base> + { return !(__y._M_current > __x._M_current); } + + friend constexpr bool + operator>=(const _Iterator& __x, const _Iterator& __y) + requires random_access_range<_Base> + { return !(__x._M_current > __y._M_current); } + +#ifdef __cpp_lib_three_way_comparison + friend constexpr auto + operator<=>(const _Iterator& __x, const _Iterator& __y) + requires random_access_range<_Base> + && three_way_comparable> + { return __x._M_current <=> __y._M_current; } +#endif + + friend constexpr _Iterator + operator+(const _Iterator& __x, difference_type __y) + requires random_access_range<_Base> + { return _Iterator{__x} += __y; } + + friend constexpr _Iterator + operator+(difference_type __x, const _Iterator& __y) + requires random_access_range<_Base> + { return __y + __x; } + + friend constexpr _Iterator + operator-(const _Iterator& __x, difference_type __y) + requires random_access_range<_Base> + { return _Iterator{__x} -= __y; } + + friend constexpr difference_type + operator-(const _Iterator& __x, const _Iterator& __y) + requires random_access_range<_Base> + { return __x._M_current - __y._M_current; } + + friend constexpr difference_type + operator-(const _Iterator<_Const>& __x, const sentinel_t<_Base>& __y) + requires sized_sentinel_for, iterator_t<_Base>> + { return __x._M_current - __y; } + + friend constexpr difference_type + operator-(const sentinel_t<_Base>& __x, const _Iterator<_Const>& __y) + requires sized_sentinel_for, iterator_t<_Base>> + { return -(__y - __x); } + }; + + _Vp _M_base = _Vp(); + }; + + template + using keys_view = elements_view, 0>; + + template + using values_view = elements_view, 1>; + + namespace views + { + template + inline constexpr __adaptor::_RangeAdaptorClosure elements + = [] (_Range&& __r) + { + return elements_view, _Nm>{std::forward<_Range>(__r)}; + }; + + inline constexpr __adaptor::_RangeAdaptorClosure keys = elements<0>; + inline constexpr __adaptor::_RangeAdaptorClosure values = elements<1>; + } // namespace views + +} // namespace ranges + + namespace views = ranges::views; + + template + struct tuple_size> + : integral_constant + { }; + + template + struct tuple_element<0, ranges::subrange<_Iter, _Sent, _Kind>> + { using type = _Iter; }; + + template + struct tuple_element<1, ranges::subrange<_Iter, _Sent, _Kind>> + { using type = _Sent; }; + _GLIBCXX_END_NAMESPACE_VERSION } // namespace #endif // library concepts diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc new file mode 100644 index 00000000000..577ce7460e6 --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc @@ -0,0 +1,122 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include +#include +#include +#include + +namespace ranges = std::ranges; +namespace views = std::ranges::views; + +void +test01() +{ + int x[] = {1,2,3,4,5}; + auto v = views::all(x); + + static_assert(ranges::view); + static_assert(ranges::random_access_range); + + VERIFY( ranges::size(v) == 5 ); + VERIFY( ranges::size(x | views::all) == 5 ); + VERIFY( ranges::size(v | views::all | views::all) == 5 ); + VERIFY( ranges::size(v | (views::all | views::all)) == 5 ); + + ranges::reverse(v); + VERIFY( ranges::equal(v, (int[]){5,4,3,2,1}) ); +} + +void +test02() +{ + int x[5] = { 0 }; + int k = 0; + for (auto&& i : ranges::ref_view{x}) + i += ++k; + VERIFY( ranges::equal(x, (int[]){1,2,3,4,5}) ); +} + +constexpr bool +test03() +{ + std::array ints{0,1,2,3,4,5}; + auto even = [] (int i) { return i%2==0; }; + auto odd = [] (int i) { return i%2==1; }; + auto square = [] (int i) { return i*i; }; + int sum = 0; + for (auto v : (ints + | (views::all + | (views::filter(even) + | (views::filter(odd) | views::all))) + | views::transform(square))) + sum += v; + return sum == 0; +} + +constexpr bool +test04() +{ + auto odd = [] (int i) { return i%2==1; }; + auto square = [] (int i) { return i*i; }; + auto increment = [] (int i) { return i+1; }; + auto small = [] (int i) { return i<30; }; + auto non_negative = [] (int i) { return i>=0; }; + auto negative = [] (int i) { return i<0; }; + return ranges::equal(views::iota(-5) + | views::drop_while(negative) + | views::take_while(non_negative) + | views::transform(increment) + | views::filter(odd) + | views::take(3) + | views::all + | views::transform(square), + views::iota(-5) + | views::drop_while(negative) + | views::drop(1) + | views::filter(odd) + | views::transform(square) + | views::take_while(small) + | views::take_while(small)); +} + +static_assert(std::is_empty_v); +static_assert(sizeof(decltype(views::take(5) | views::drop(5))) + == sizeof(decltype(views::take(5) + | views::join + | views::common + | views::all + | views::keys + | views::drop(5) + | views::reverse))); + +int +main() +{ + test01(); + test02(); + static_assert(test03()); + static_assert(test04()); +} diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/common.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/common.cc new file mode 100644 index 00000000000..b73796835be --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/common.cc @@ -0,0 +1,68 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include +#include +#include +#include + +using __gnu_test::test_range; +using __gnu_test::forward_iterator_wrapper; + +namespace ranges = std::ranges; +namespace views = ranges::views; + +void +test01() +{ + int x[] = {1,2,1,3}; + auto v = x | views::common; + VERIFY( std::count(v.begin(), v.end(), 1) == 2); + static_assert(ranges::common_range); + static_assert(ranges::view); + static_assert(ranges::random_access_range); + static_assert(std::same_as); + + auto v2 = v | (views::common | views::common); + VERIFY( std::count(v2.begin(), v2.end(), 1) == 2); +} + +void +test02() +{ + int x[] = {1,2,1,3}; + test_range rx(x); + auto v = ranges::common_view(rx); + VERIFY( std::count(v.begin(), v.end(), 1) == 2); + static_assert(ranges::common_range); + static_assert(ranges::view); + static_assert(ranges::forward_range); + static_assert(std::same_as); + + auto v2 = v | (views::common | views::common); + VERIFY( std::count(v2.begin(), v2.end(), 1) == 2); +} + +int +main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/counted.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/counted.cc new file mode 100644 index 00000000000..e81f9062d39 --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/counted.cc @@ -0,0 +1,64 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include +#include +#include +#include + +using __gnu_test::test_range; +using __gnu_test::forward_iterator_wrapper; + +namespace ranges = std::ranges; +namespace views = ranges::views; + +void +test01() +{ + int x[] = {0,1,2,3,4,5,0,1,2,3,4,5}; + auto v = views::counted(x, 5); + VERIFY( ranges::equal(v, (int[]){0,1,2,3,4}) ); + using R = decltype(v); + static_assert(ranges::view); + static_assert(ranges::sized_range); + static_assert(ranges::common_range); + static_assert(ranges::random_access_range); +} + +void +test02() +{ + int x[] = {0,1,2,3,4,5,0,1,2,3,4,5}; + test_range rx(x); + auto v = views::counted(rx.begin(), 5); + VERIFY( ranges::equal(v, (int[]){0,1,2,3,4}) ); + using R = decltype(v); + static_assert(ranges::view); + static_assert(ranges::sized_range); + static_assert(!ranges::common_range); + static_assert(!ranges::bidirectional_range); +} + +int +main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc new file mode 100644 index 00000000000..93fbafcf5a3 --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc @@ -0,0 +1,107 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include +#include +#include +#include + +using __gnu_test::test_range; +using __gnu_test::bidirectional_iterator_wrapper; + +namespace ranges = std::ranges; +namespace views = ranges::views; + +void +test01() +{ + int x[] = {1,2,3,4,5}; + auto v = x | views::drop(3); + using R = decltype(v); + static_assert(ranges::view); + static_assert(ranges::sized_range); + static_assert(ranges::random_access_range); + VERIFY( ranges::equal(v, (int[]){4,5}) ); +} + +void +test02() +{ + int x[] = {1,2,3,4,5}; + auto t = views::drop(3) | views::reverse; + auto v = x | t; + using R = decltype(v); + static_assert(ranges::view); + static_assert(ranges::sized_range); + static_assert(ranges::random_access_range); + VERIFY( ranges::equal(v, (int[]){5,4}) ); +} + +void +test03() +{ + int x[] = {1,2,3,4,5}; + test_range rx(x); + auto v = rx | views::drop(3); + using R = decltype(v); + static_assert(ranges::view); + static_assert(!ranges::sized_range); + static_assert(ranges::bidirectional_range); + VERIFY( ranges::equal(v, (int[]){4,5}) ); +} + + +void +test04() +{ + auto v = views::iota(0) | views::drop(10); + using R = decltype(v); + static_assert(ranges::view); + static_assert(!ranges::sized_range); + VERIFY( ranges::equal(v | views::take(3), (int[]){10,11,12}) ); +} + +void +test05() +{ + int x[] = {1,2,3}; + auto r = ranges::subrange(x, x+1); + auto v = views::drop(r, 2); + VERIFY( ranges::begin(v) == x+1 ); + VERIFY( ranges::size(v) == 0 ); +} + +void +test06() +{ + int x[] = {1,2,3}; + VERIFY( ranges::empty(x | views::drop(10)) ); +} + +int +main() +{ + test01(); + test02(); + test03(); + test04(); + test05(); + test06(); +} diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/drop_while.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/drop_while.cc new file mode 100644 index 00000000000..be47551563d --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/drop_while.cc @@ -0,0 +1,63 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include +#include +#include +#include + +using __gnu_test::test_range; +using __gnu_test::bidirectional_iterator_wrapper; + +namespace ranges = std::ranges; +namespace views = std::ranges::views; + +void +test01() +{ + auto p = [] (int i) { return i != 16; }; + auto v = views::iota(10) | views::drop_while(p); + VERIFY( ranges::equal(v | views::take(5), (int[]){16,17,18,19,20}) ); + using R = decltype(v); + static_assert(ranges::view); + static_assert(!ranges::common_range); + static_assert(ranges::random_access_range); +} + +void +test02() +{ + int x[] = {1,2,3,4,5}; + test_range rx(x); + auto v = rx | views::drop_while([] (int i) { return i<4; }); + VERIFY( ranges::equal(v, (int[]){4,5}) ); + using R = decltype(v); + static_assert(ranges::view); + static_assert(!ranges::common_range); + static_assert(ranges::bidirectional_range); +} + +int +main() +{ + test01(); + test02(); +} + diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc new file mode 100644 index 00000000000..d846c4cf33e --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc @@ -0,0 +1,52 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include +#include +#include +#include +#include + +namespace ranges = std::ranges; +namespace views = ranges::views; + +void +test01() +{ + std::tuple x[] = {{1,2},{3,4},{5,6}}; + auto v0 = x | views::elements<0>; + VERIFY( ranges::equal(v0, (int[]){1,3,5}) ); + VERIFY( ranges::equal(v0, x | views::keys) ); + VERIFY( ranges::size(v0) == 3 ); + + using R0 = decltype(v0); + static_assert(ranges::random_access_range); + static_assert(ranges::sized_range); + + auto v1 = x | views::reverse | views::elements<1> | views::reverse; + VERIFY( ranges::equal(v1, (int[]){2,4,6}) ); + VERIFY( ranges::equal(v1, x | views::values) ); +} + +int +main() +{ + test01(); +} diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc new file mode 100644 index 00000000000..83d52967a0f --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc @@ -0,0 +1,97 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include +#include +#include +#include + +using __gnu_test::test_range; +using __gnu_test::bidirectional_iterator_wrapper; + +namespace ranges = std::ranges; +namespace views = std::ranges::views; + +void +test01() +{ + int x[] = {1,2,3,4,5,6}; + auto is_odd = [] (int i) { return i%2==1; }; + auto v = x | views::filter(is_odd); + using R = decltype(v); + static_assert(std::same_as); + static_assert(ranges::view); + static_assert(ranges::input_range); + static_assert(ranges::common_range); + static_assert(!ranges::sized_range); + static_assert(ranges::bidirectional_range); + static_assert(!ranges::random_access_range); + static_assert(ranges::range>); + VERIFY( ranges::equal(v, (int[]){1,3,5}) ); + VERIFY( ranges::equal(v | views::reverse, (int[]){5,3,1}) ); +} + +void +test02() +{ + int x[] = {1,2,3,4,5,6}; + auto f = [flag=false] (int) mutable { return flag = !flag; }; + auto v = views::filter(f)(x); + using R = decltype(v); + static_assert(std::same_as); + static_assert(ranges::range); + static_assert(std::copyable); + static_assert(!ranges::view); + VERIFY( ranges::equal(v, (int[]){1,3,5}) ); +} + +struct X +{ + int i, j; +}; + +void +test03() +{ + X x[] = {{1,3}, {2,5}, {3,7}, {4,9}}; + test_range rx(x); + auto v = rx | views::filter([] (auto&& p) { return p.i%2==0; }); + int sum = 0; + for (auto i = v.begin(); i != v.end(); ++i) + sum += i->j; + VERIFY( sum == 14 ); +} + +void +test04() +{ + auto yes = [] (int) { return true; }; + VERIFY( ranges::equal(views::iota(0) | views::filter(yes) | views::take(1), + (int[]){0}) ); +} + +int +main() +{ + test01(); + test02(); + test03(); + test04(); +} diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc new file mode 100644 index 00000000000..d3e652da009 --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc @@ -0,0 +1,112 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include +#include +#include +#include +#include +#include +#include + +namespace ranges = std::ranges; +namespace views = std::ranges::views; + +void +test01() +{ + using namespace std::literals; + std::string_view cs[] = {"the", "quick", "brown", "fox"}; + auto v = cs | views::join; + VERIFY( ranges::equal(v, "thequickbrownfox"sv) ); + using R = decltype(v); + static_assert(ranges::bidirectional_range); + static_assert(ranges::bidirectional_range); + static_assert(ranges::common_range); + static_assert(ranges::common_range); +} + +void +test02() +{ + auto v = (views::iota(0,4) + | views::transform([] (int i) { return views::iota(0,i); }) + | views::join); + VERIFY( ranges::equal(v, (int[]){0,0,1,0,1,2}) ); + using R = decltype(v); + static_assert(ranges::input_range); + static_assert(!ranges::range); + static_assert(!ranges::forward_range); + static_assert(!ranges::common_range); +} + +void +test03() +{ + auto v = (views::iota(0,4) + | views::transform([] (int i) { return views::iota(0,i); }) + | views::filter([] (auto) { return true; }) + | views::join); + VERIFY( ranges::equal(v, (int[]){0,0,1,0,1,2}) ); + using R = decltype(v); + static_assert(ranges::input_range); + static_assert(!ranges::range); + static_assert(!ranges::forward_range); + static_assert(!ranges::common_range); +} + +void +test04() +{ + auto v = (views::iota(0,4) + | views::transform([] (int i) { return views::iota(0,i); })); + auto v2 = ranges::ref_view{v}; + VERIFY( ranges::equal(v2 | views::join, (int[]){0,0,1,0,1,2}) ); + using R = decltype(v2); + static_assert(ranges::random_access_range); + static_assert(ranges::range); + static_assert(ranges::common_range); + static_assert(ranges::random_access_range>); + static_assert(!std::is_reference_v>); +} + +void +test05() +{ + using namespace std::literals; + std::vector x = {"the", " ", "quick", " ", "brown", " ", "fox"}; + auto v = x | views::join | views::split(' '); + auto i = v.begin(); + VERIFY( ranges::equal(*i++, "the"sv) ); + VERIFY( ranges::equal(*i++, "quick"sv) ); + VERIFY( ranges::equal(*i++, "brown"sv) ); + VERIFY( ranges::equal(*i++, "fox"sv) ); + VERIFY( i == v.end() ); +} + +int +main() +{ + test01(); + test02(); + test03(); + test04(); + test05(); +} diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/reverse.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/reverse.cc new file mode 100644 index 00000000000..0c6aceabbed --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/reverse.cc @@ -0,0 +1,86 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include +#include +#include +#include + +using __gnu_test::test_range; +using __gnu_test::bidirectional_iterator_wrapper; + +namespace ranges = std::ranges; +namespace views = ranges::views; + +void +test01() +{ + int x[] = {1,2,3,4,5}; + auto v = x | views::reverse; + VERIFY( ranges::equal(v, (int[]){5,4,3,2,1}) ); + VERIFY( ranges::equal(v | views::reverse, x) ); + static_assert(ranges::view); + static_assert(ranges::sized_range); + static_assert(ranges::common_range); + static_assert(ranges::random_access_range); +} + +void +test02() +{ + int x[] = {1,2,3,4,5}; + test_range rx(x); + auto v = views::reverse(rx); + VERIFY( ranges::equal(v, (int[]){5,4,3,2,1}) ); + VERIFY( ranges::equal(v | views::reverse, rx) ); + static_assert(ranges::view); + static_assert(!ranges::sized_range); + static_assert(ranges::common_range); + static_assert(!ranges::random_access_range); + static_assert(ranges::bidirectional_range); +} + +void +test03() +{ + int x[] = {1,7,3,6,5,2,4,8}; + auto is_even = [] (int i) { return i%2==0; }; + int sum = 0; + for (auto i : x | views::reverse | views::filter(is_even)) + sum += i; + VERIFY( sum == 20 ); +} + +void +test04() +{ + int x[] = {1,2,3,4,5}; + VERIFY( ranges::equal(x | views::reverse | (views::reverse | views::reverse), + (int[]){5,4,3,2,1}) ); +} + +int +main() +{ + test01(); + test02(); + test03(); + test04(); +} diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc new file mode 100644 index 00000000000..129a8249f21 --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc @@ -0,0 +1,82 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include +#include +#include +#include +#include +#include + +using __gnu_test::test_range; +using __gnu_test::forward_iterator_wrapper; + +namespace ranges = std::ranges; +namespace views = std::ranges::views; + +using namespace std::literals; + +void +test01() +{ + auto x = "the quick brown fox"sv; + auto p = std::string{" "}; + auto v = x | views::split(p); + auto i = v.begin(); + VERIFY( ranges::equal(*i++, "the"sv) ); + VERIFY( ranges::equal(*i++, "quick"sv) ); + VERIFY( ranges::equal(*i++, "brown"sv) ); + VERIFY( ranges::equal(*i++, "fox"sv) ); + VERIFY( i == v.end() ); +} + +void +test02() +{ + auto x = "the quick brown fox"sv; + auto v = x | views::split(' '); + auto i = v.begin(); + VERIFY( ranges::equal(*i++, "the"sv) ); + VERIFY( ranges::equal(*i++, "quick"sv) ); + VERIFY( ranges::equal(*i++, "brown"sv) ); + VERIFY( ranges::equal(*i++, "fox"sv) ); + VERIFY( i == v.end() ); +} + +void +test03() +{ + char x[] = "the quick brown fox"; + test_range rx(x); + auto v = rx | views::split(' '); + auto i = v.begin(); + VERIFY( ranges::equal(*i++, "the"sv) ); + VERIFY( ranges::equal(*i++, "quick"sv) ); + VERIFY( ranges::equal(*i++, "brown"sv) ); + VERIFY( ranges::equal(*i++, "fox"sv) ); + VERIFY( i == v.end() ); +} + +int +main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc new file mode 100644 index 00000000000..e2d2edbe0a8 --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc @@ -0,0 +1,95 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include +#include +#include +#include + +using __gnu_test::test_range; +using __gnu_test::bidirectional_iterator_wrapper; + +namespace ranges = std::ranges; +namespace views = ranges::views; + +void +test01() +{ + auto v = views::iota(0) | views::take(5); + static_assert(ranges::view); + static_assert(!ranges::sized_range); + static_assert(!ranges::common_range); + static_assert(ranges::random_access_range); + static_assert(!ranges::contiguous_range); + static_assert(ranges::range); + VERIFY( ranges::equal(v, (int[]){0,1,2,3,4}) ); +} + +void +test02() +{ + auto v = views::take(views::iota(0, 20), 5); + static_assert(ranges::view); + static_assert(ranges::sized_range); + static_assert(ranges::common_range); + static_assert(ranges::random_access_range); + static_assert(!ranges::contiguous_range); + static_assert(ranges::range); + VERIFY( ranges::equal(v, (int[]){0,1,2,3,4}) ); +} + +void +test03() +{ + int x[] = {0,1,2,3,4,5}; + auto is_odd = [] (int i) { return i%2 == 1; }; + auto v = x | views::filter(is_odd) | views::take(3); + ranges::begin(v); + using R = decltype(v); + static_assert(ranges::view); + static_assert(!ranges::sized_range); + static_assert(!ranges::common_range); + static_assert(ranges::forward_range); + static_assert(!ranges::random_access_range); + static_assert(!ranges::range); + VERIFY( ranges::equal(v, (int[]){1,3,5}) ); +} + +void +test04() +{ + int x[] = {1,2,3,4,5}; + test_range rx(x); + auto v = ranges::take_view{rx, 3}; + using R = decltype(v); + static_assert(ranges::view); + static_assert(!ranges::sized_range); + static_assert(ranges::bidirectional_range); + VERIFY( ranges::equal(v | views::take(5), (int[]){1,2,3}) ); +} + +int +main() +{ + test01(); + test02(); + test03(); + test04(); +} diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc new file mode 100644 index 00000000000..b261ffd1aae --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc @@ -0,0 +1,62 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include +#include +#include +#include + +using __gnu_test::test_range; +using __gnu_test::forward_iterator_wrapper; + +namespace ranges = std::ranges; +namespace views = std::ranges::views; + +void +test01() +{ + auto p = [] (int i) { return i != 16; }; + auto v = views::iota(10) | views::take_while(p); + VERIFY( ranges::equal(v, (int[]){10,11,12,13,14,15}) ); + using R = decltype(v); + static_assert(ranges::view); + static_assert(!ranges::common_range); + static_assert(ranges::random_access_range); +} + +void +test02() +{ + int x[] = {1,2,3,4,5}; + test_range rx(x); + auto v = rx | views::take_while([] (int i) { return i<4; }); + VERIFY( ranges::equal(v, (int[]){1,2,3}) ); + using R = decltype(v); + static_assert(ranges::view); + static_assert(!ranges::common_range); + static_assert(ranges::forward_range); +} + +int +main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc new file mode 100644 index 00000000000..ad51fffb43d --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc @@ -0,0 +1,86 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include +#include +#include +#include + +using __gnu_test::test_range; +using __gnu_test::random_access_iterator_wrapper; + +namespace ranges = std::ranges; +namespace views = std::ranges::views; + +void +test01() +{ + int x[] = {1,2,3,4,5}; + auto is_odd = [] (int i) { return i%2==1; }; + auto v = x | views::transform(is_odd); + VERIFY( ranges::equal(v, (int[]){1,0,1,0,1}) ); + using R = decltype(v); + static_assert(std::same_as); + static_assert(ranges::view); + static_assert(ranges::sized_range); + static_assert(ranges::random_access_range); +} + +struct X +{ + int i,j; +}; + +void +test02() +{ + X x[] = {{1,2},{3,4},{5,6},{7,8},{9,10}}; + test_range rx(x); + auto v = rx | views::transform(&X::i); + VERIFY( ranges::size(v) == 5 ); + VERIFY( ranges::distance(v.begin(), v.end()) == 5 ); + VERIFY( ranges::equal(v, (int[]){1,3,5,7,9}) ); + VERIFY( ranges::equal(v | views::reverse, (int[]){9,7,5,3,1}) ); + using R = decltype(v); + static_assert(std::same_as); + static_assert(std::same_as>>); + static_assert(ranges::view); + static_assert(ranges::sized_range); + static_assert(!ranges::common_range); + static_assert(ranges::random_access_range); +} + +void +test03() +{ + auto id = [] (int i) { return i; }; + auto v = views::iota(0) | (views::filter(id) + | views::transform(id) + | views::take(5)); + VERIFY( ranges::equal(v, (int[]){1,2,3,4,5}) ); +} + +int +main() +{ + test01(); + test02(); + test03(); +}