From: Patrick Palka Date: Tue, 13 Sep 2022 15:18:14 +0000 (-0400) Subject: libstdc++: Implement ranges::chunk_view from P2442R1 X-Git-Tag: Thesis~3741 X-Git-Url: https://gcc.gnu.org/git/?a=commitdiff_plain;h=5d84a4418aa962a715dc74998fea2a7de9d4042c;p=gcc.git libstdc++: Implement ranges::chunk_view from P2442R1 This also implements the LWG 3707, 3710 and 3712 changes to chunk_view. libstdc++-v3/ChangeLog: * include/std/ranges (__detail::__div_ceil): Define. (chunk_view): Define. (chunk_view::_OuterIter): Define. (chunk_view::_OuterIter::value_type): Define. (chunk_view::_InnerIter): Define. (chunk_view<_Vp>): Define partial specialization for forward ranges. (enable_borrowed_range): Define. (chunk_view<_Vp>::_Iterator): Define. (views::__detail::__can_chunk_view): Define. (views::_Chunk, views::chunk): Define. * testsuite/std/ranges/adaptors/chunk/1.cc: New test. --- diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index 6297ce7cee3a..da235685ab43 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -5776,6 +5776,543 @@ namespace views::__adaptor inline constexpr auto pairwise_transform = adjacent_transform<2>; } + + namespace __detail + { + template + constexpr _Tp __div_ceil(_Tp __num, _Tp __denom) + { + _Tp __r = __num / __denom; + if (__num % __denom) + ++__r; + return __r; + } + } + + template + requires input_range<_Vp> + class chunk_view : public view_interface> + { + _Vp _M_base; + range_difference_t<_Vp> _M_n; + range_difference_t<_Vp> _M_remainder = 0; + __detail::__non_propagating_cache> _M_current; + + class _OuterIter; + class _InnerIter; + + public: + constexpr explicit + chunk_view(_Vp __base, range_difference_t<_Vp> __n) + : _M_base(std::move(__base)), _M_n(__n) + { __glibcxx_assert(__n >= 0); } + + constexpr _Vp + base() const & requires copy_constructible<_Vp> + { return _M_base; } + + constexpr _Vp + base() && + { return std::move(_M_base); } + + constexpr _OuterIter + begin() + { + _M_current = ranges::begin(_M_base); + _M_remainder = _M_n; + return _OuterIter(*this); + } + + constexpr default_sentinel_t + end() const noexcept + { return default_sentinel; } + + constexpr auto + size() requires sized_range<_Vp> + { + return __detail::__to_unsigned_like(__detail::__div_ceil + (ranges::distance(_M_base), _M_n)); + } + + constexpr auto + size() const requires sized_range + { + return __detail::__to_unsigned_like(__detail::__div_ceil + (ranges::distance(_M_base), _M_n)); + } + }; + + template + chunk_view(_Range&&, range_difference_t<_Range>) -> chunk_view>; + + template + requires input_range<_Vp> + class chunk_view<_Vp>::_OuterIter + { + chunk_view* _M_parent; + + constexpr explicit + _OuterIter(chunk_view& __parent) noexcept + : _M_parent(std::__addressof(__parent)) + { } + + friend chunk_view; + + public: + using iterator_concept = input_iterator_tag; + using difference_type = range_difference_t<_Vp>; + + struct value_type; + + _OuterIter(_OuterIter&&) = default; + _OuterIter& operator=(_OuterIter&&) = default; + + constexpr value_type + operator*() const + { + __glibcxx_assert(*this != default_sentinel); + return value_type(*_M_parent); + } + + constexpr _OuterIter& + operator++() + { + __glibcxx_assert(*this != default_sentinel); + ranges::advance(*_M_parent->_M_current, _M_parent->_M_remainder, + ranges::end(_M_parent->_M_base)); + _M_parent->_M_remainder = _M_parent->_M_n; + return *this; + } + + constexpr void + operator++(int) + { ++*this; } + + friend constexpr bool + operator==(const _OuterIter& __x, default_sentinel_t) + { + return *__x._M_parent->_M_current == ranges::end(__x._M_parent->_M_base) + && __x._M_parent->_M_remainder != 0; + } + + friend constexpr difference_type + operator-(default_sentinel_t, const _OuterIter& __x) + requires sized_sentinel_for, iterator_t<_Vp>> + { + const auto __dist = ranges::end(__x._M_parent->_M_base) - *__x._M_parent->_M_current; + + if (__dist < __x._M_parent->_M_remainder) + return __dist == 0 ? 0 : 1; + + return 1 + __detail::__div_ceil(__dist - __x._M_parent->_M_remainder, + __x._M_parent->_M_n); + } + + friend constexpr difference_type + operator-(const _OuterIter& __x, default_sentinel_t __y) + requires sized_sentinel_for, iterator_t<_Vp>> + { return -(__y - __x); } + }; + + template + requires input_range<_Vp> + struct chunk_view<_Vp>::_OuterIter::value_type : view_interface + { + private: + chunk_view* _M_parent; + + constexpr explicit + value_type(chunk_view& __parent) noexcept + : _M_parent(std::__addressof(__parent)) + { } + + friend _OuterIter; + + public: + constexpr _InnerIter + begin() const noexcept + { return _InnerIter(*_M_parent); } + + constexpr default_sentinel_t + end() const noexcept + { return default_sentinel; } + + constexpr auto + size() const + requires sized_sentinel_for, iterator_t<_Vp>> + { + return __detail::__to_unsigned_like + (ranges::min(_M_parent->_M_remainder, + ranges::end(_M_parent->_M_base) - *_M_parent->_M_current)); + } + }; + + template + requires input_range<_Vp> + class chunk_view<_Vp>::_InnerIter + { + chunk_view* _M_parent; + + constexpr explicit + _InnerIter(chunk_view& __parent) noexcept + : _M_parent(std::__addressof(__parent)) + { } + + friend _OuterIter::value_type; + + public: + using iterator_concept = input_iterator_tag; + using difference_type = range_difference_t<_Vp>; + using value_type = range_value_t<_Vp>; + + _InnerIter(_InnerIter&&) = default; + _InnerIter& operator=(_InnerIter&&) = default; + + constexpr const iterator_t<_Vp>& + base() const & + { return *_M_parent->_M_current; } + + constexpr range_reference_t<_Vp> + operator*() const + { + __glibcxx_assert(*this != default_sentinel); + return **_M_parent->_M_current; + } + + constexpr _InnerIter& + operator++() + { + __glibcxx_assert(*this != default_sentinel); + ++*_M_parent->_M_current; + if (*_M_parent->_M_current == ranges::end(_M_parent->_M_base)) + _M_parent->_M_remainder = 0; + else + --_M_parent->_M_remainder; + return *this; + } + + constexpr void + operator++(int) + { ++*this; } + + friend constexpr bool + operator==(const _InnerIter& __x, default_sentinel_t) noexcept + { return __x._M_parent->_M_remainder == 0; } + + friend constexpr difference_type + operator-(default_sentinel_t, const _InnerIter& __x) + requires sized_sentinel_for, iterator_t<_Vp>> + { + return ranges::min(__x._M_parent->_M_remainder, + ranges::end(__x._M_parent->_M_base) - *__x._M_parent->_M_current); + } + + friend constexpr difference_type + operator-(const _InnerIter& __x, default_sentinel_t __y) + requires sized_sentinel_for, iterator_t<_Vp>> + { return -(__y - __x); } + }; + + template + requires forward_range<_Vp> + class chunk_view<_Vp> : public view_interface> + { + _Vp _M_base; + range_difference_t<_Vp> _M_n; + template class _Iterator; + + public: + constexpr explicit + chunk_view(_Vp __base, range_difference_t<_Vp> __n) + : _M_base(std::move(__base)), _M_n(__n) + { __glibcxx_assert(__n > 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>) + { return _Iterator(this, ranges::begin(_M_base)); } + + constexpr auto + begin() const requires forward_range + { return _Iterator(this, ranges::begin(_M_base)); } + + constexpr auto + end() requires (!__detail::__simple_view<_Vp>) + { + if constexpr (common_range<_Vp> && sized_range<_Vp>) + { + auto __missing = (_M_n - ranges::distance(_M_base) % _M_n) % _M_n; + return _Iterator(this, ranges::end(_M_base), __missing); + } + else if constexpr (common_range<_Vp> && !bidirectional_range<_Vp>) + return _Iterator(this, ranges::end(_M_base)); + else + return default_sentinel; + } + + constexpr auto + end() const requires forward_range + { + if constexpr (common_range && sized_range) + { + auto __missing = (_M_n - ranges::distance(_M_base) % _M_n) % _M_n; + return _Iterator(this, ranges::end(_M_base), __missing); + } + else if constexpr (common_range && !bidirectional_range) + return _Iterator(this, ranges::end(_M_base)); + else + return default_sentinel; + } + + constexpr auto + size() requires sized_range<_Vp> + { + return __detail::__to_unsigned_like(__detail::__div_ceil + (ranges::distance(_M_base), _M_n)); + } + + constexpr auto + size() const requires sized_range + { + return __detail::__to_unsigned_like(__detail::__div_ceil + (ranges::distance(_M_base), _M_n)); + } + }; + + template + inline constexpr bool enable_borrowed_range> + = forward_range<_Vp> && enable_borrowed_range<_Vp>; + + template + requires forward_range<_Vp> + template + class chunk_view<_Vp>::_Iterator + { + using _Parent = __detail::__maybe_const_t<_Const, chunk_view>; + using _Base = __detail::__maybe_const_t<_Const, _Vp>; + + iterator_t<_Base> _M_current = iterator_t<_Base>(); + sentinel_t<_Base> _M_end = sentinel_t<_Base>(); + range_difference_t<_Base> _M_n = 0; + range_difference_t<_Base> _M_missing = 0; + + constexpr + _Iterator(_Parent* __parent, iterator_t<_Base> __current, + range_difference_t<_Base> __missing = 0) + : _M_current(__current), _M_end(ranges::end(__parent->_M_base)), + _M_n(__parent->_M_n), _M_missing(__missing) + { } + + static auto + _S_iter_cat() + { + if constexpr (random_access_range<_Base>) + return random_access_iterator_tag{}; + else if constexpr (bidirectional_range<_Base>) + return bidirectional_iterator_tag{}; + else + return forward_iterator_tag{}; + } + + friend chunk_view; + + public: + using iterator_category = input_iterator_tag; + using iterator_concept = decltype(_S_iter_cat()); + using value_type = decltype(views::take(subrange(_M_current, _M_end), _M_n)); + using difference_type = range_difference_t<_Base>; + + _Iterator() = default; + + constexpr _Iterator(_Iterator __i) + requires _Const + && convertible_to, iterator_t<_Base>> + && convertible_to, sentinel_t<_Base>> + : _M_current(std::move(__i._M_current)), _M_end(std::move(__i._M_end)), + _M_n(__i._M_n), _M_missing(__i._M_missing) + { } + + constexpr iterator_t<_Base> + base() const + { return _M_current; } + + constexpr value_type + operator*() const + { + __glibcxx_assert(_M_current != _M_end); + return views::take(subrange(_M_current, _M_end), _M_n); + } + + constexpr _Iterator& + operator++() + { + __glibcxx_assert(_M_current != _M_end); + _M_missing = ranges::advance(_M_current, _M_n, _M_end); + return *this; + } + + constexpr _Iterator + operator++(int) + { + auto __tmp = *this; + ++*this; + return __tmp; + } + + constexpr _Iterator& + operator--() requires bidirectional_range<_Base> + { + ranges::advance(_M_current, _M_missing - _M_n); + _M_missing = 0; + return *this; + } + + constexpr _Iterator + operator--(int) requires bidirectional_range<_Base> + { + auto __tmp = *this; + --*this; + return __tmp; + } + + constexpr _Iterator& + operator+=(difference_type __x) + requires random_access_range<_Base> + { + if (__x > 0) + { + __glibcxx_assert(ranges::distance(_M_current, _M_end) > _M_n * (__x - 1)); + _M_missing = ranges::advance(_M_current, _M_n * __x, _M_end); + } + else if (__x < 0) + { + ranges::advance(_M_current, _M_n * __x + _M_missing); + _M_missing = 0; + } + return *this; + } + + constexpr _Iterator& + operator-=(difference_type __x) + requires random_access_range<_Base> + { return *this += -__x; } + + constexpr value_type + operator[](difference_type __n) const + requires random_access_range<_Base> + { return *(*this + __n); } + + friend constexpr bool + operator==(const _Iterator& __x, const _Iterator& __y) + { return __x._M_current == __y._M_current; } + + friend constexpr bool + operator==(const _Iterator& __x, default_sentinel_t) + { return __x._M_current == __x._M_end; } + + 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); } + + 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; } + + friend constexpr _Iterator + operator+(const _Iterator& __i, difference_type __n) + requires random_access_range<_Base> + { + auto __r = __i; + __r += __n; + return __r; + } + + friend constexpr _Iterator + operator+(difference_type __n, const _Iterator& __i) + requires random_access_range<_Base> + { + auto __r = __i; + __r += __n; + return __r; + } + + friend constexpr _Iterator + operator-(const _Iterator& __i, difference_type __n) + requires random_access_range<_Base> + { + auto __r = __i; + __r -= __n; + return __r; + } + + friend constexpr difference_type + operator-(const _Iterator& __x, const _Iterator& __y) + requires sized_sentinel_for, iterator_t<_Base>> + { + return (__x._M_current - __y._M_current + + __x._M_missing - __y._M_missing) / __x._M_n; + } + + friend constexpr difference_type + operator-(default_sentinel_t __y, const _Iterator& __x) + requires sized_sentinel_for, iterator_t<_Base>> + { return __detail::__div_ceil(__x._M_end - __x._M_current, __x._M_n); } + + friend constexpr difference_type + operator-(const _Iterator& __x, default_sentinel_t __y) + requires sized_sentinel_for, iterator_t<_Base>> + { return -(__y - __x); } + }; + + namespace views + { + namespace __detail + { + template + concept __can_chunk_view + = requires { chunk_view(std::declval<_Range>(), std::declval<_Dp>()); }; + } + + struct _Chunk : __adaptor::_RangeAdaptor<_Chunk> + { + template> + requires __detail::__can_chunk_view<_Range, _Dp> + constexpr auto + operator() [[nodiscard]] (_Range&& __r, type_identity_t<_Dp> __n) const + { return chunk_view(std::forward<_Range>(__r), __n); } + + using __adaptor::_RangeAdaptor<_Chunk>::operator(); + static constexpr int _S_arity = 2; + static constexpr bool _S_has_simple_extra_args = true; + }; + + inline constexpr _Chunk chunk; + } #endif // C++23 } // namespace ranges diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/chunk/1.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/chunk/1.cc new file mode 100644 index 000000000000..125c88ef853d --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/chunk/1.cc @@ -0,0 +1,80 @@ +// { dg-options "-std=gnu++23" } +// { dg-do run { target c++23 } } + +#include +#include +#include +#include +#include + +namespace ranges = std::ranges; +namespace views = std::views; + +constexpr bool +test01() +{ + int x[] = {1, 2, 3, 4, 5}; + + auto v2 = x | views::chunk(2); + const auto i0 = v2.begin(), i1 = v2.begin() + 1; + VERIFY( i0 + 1 - 1 == i0 ); + VERIFY( i0 < i1 ); + VERIFY( i1 < v2.end() ); + VERIFY( i1 - i0 == 1 ); + VERIFY( i0 - i1 == -1 ); + VERIFY( v2.end() - i1 == 2 ); + VERIFY( i1 - v2.end() == -2 ); + auto i2 = v2.begin(); + i2 += 2; + i2 -= -1; + VERIFY( i2 == v2.end() ); + VERIFY( ranges::size(v2) == 3 ); + VERIFY( ranges::equal(v2, (std::initializer_list[]){{1, 2}, {3, 4}, {5}}, + ranges::equal) ); + + auto v1 = x | views::chunk(1); + VERIFY( ranges::size(v1) == ranges::size(x) ); + for (auto [r, n] : views::zip(v1, x)) + { + VERIFY( ranges::size(r) == 1 ); + VERIFY( *r.begin() == n ); + } + + auto v5 = x | views::chunk(5); + VERIFY( ranges::size(v5) == 1 ); + VERIFY( ranges::equal(v5[0], (int[]){1, 2, 3, 4, 5}) ); + + auto v10 = x | views::chunk(10); + VERIFY( ranges::size(v10) == 1 ); + VERIFY( ranges::equal(v10[0], (int[]){1, 2, 3, 4, 5}) ); + + return true; +} + +template +void +test02() +{ + int x[] = {1, 2, 3, 4, 5, 6, 7, 8}; + wrapper rx(x); + auto v = rx | views::chunk(3); + auto i = ranges::begin(v); + VERIFY( ranges::equal(*i, (int[]){1, 2, 3}) ); + ++i; + VERIFY( ranges::equal(*i, (int[]){4, 5, 6}) ); + ++i; + VERIFY( ranges::equal(*i, (int[]){7, 8}) ); + i++; + VERIFY( i == ranges::end(v) ); + + for (int i = 1; i <= 10; ++i) + VERIFY( ranges::equal(wrapper(x) | views::chunk(i) | views::join, x) ); +} + +int +main() +{ + static_assert(test01()); + test02<__gnu_test::test_input_range>(); + test02<__gnu_test::test_forward_range>(); +}