libstdc++: Implement ranges::repeat_view from P2474R2
Checks
Commit Message
Tested on x86_64-pc-linux-gnu, does this look OK for trunk? (The paper
also makes changes to views::take and views::drop, which will be
implemented separately.)
libstdc++-v3/ChangeLog:
* include/std/ranges (repeat_view): Define.
(repeat_view::_Iterator): Define.
(views::__detail::__can_repeat_view): Define.
(views::__detail::__can_bounded_repeat_view): Define.
(views::_Repeat, views::repeat): Define.
* testsuite/std/ranges/repeat/1.cc: New test.
---
libstdc++-v3/include/std/ranges | 210 ++++++++++++++++++
libstdc++-v3/testsuite/std/ranges/repeat/1.cc | 93 ++++++++
2 files changed, 303 insertions(+)
create mode 100644 libstdc++-v3/testsuite/std/ranges/repeat/1.cc
Comments
On Tue, 11 Oct 2022 at 03:51, Patrick Palka via Libstdc++
<libstdc++@gcc.gnu.org> wrote:
>
> Tested on x86_64-pc-linux-gnu, does this look OK for trunk? (The paper
> also makes changes to views::take and views::drop, which will be
> implemented separately.)
OK, thanks.
@@ -7356,6 +7356,216 @@ namespace views::__adaptor
inline constexpr _JoinWith join_with;
} // namespace views
+
+ template<copy_constructible _Tp, semiregular _Bound = unreachable_sentinel_t>
+ requires (is_object_v<_Tp> && same_as<_Tp, remove_cv_t<_Tp>>
+ && (__detail::__is_integer_like<_Bound> || same_as<_Bound, unreachable_sentinel_t>))
+ class repeat_view : public view_interface<repeat_view<_Tp, _Bound>>
+ {
+ __detail::__box<_Tp> _M_value = _Tp();
+ [[no_unique_address]] _Bound _M_bound = _Bound();
+
+ struct _Iterator;
+
+ public:
+ repeat_view() requires default_initializable<_Tp> = default;
+
+ constexpr explicit
+ repeat_view(const _Tp& __value, _Bound __bound = _Bound())
+ : _M_value(__value), _M_bound(__bound)
+ {
+ if constexpr (!same_as<_Bound, unreachable_sentinel_t>)
+ __glibcxx_assert(__bound >= 0);
+ }
+
+ constexpr explicit
+ repeat_view(_Tp&& __value, _Bound __bound = _Bound())
+ : _M_value(std::move(__value)), _M_bound(__bound)
+ { }
+
+ template<typename... _Args, typename... _BoundArgs>
+ requires constructible_from<_Tp, _Args...>
+ && constructible_from<_Bound, _BoundArgs...>
+ constexpr explicit
+ repeat_view(piecewise_construct_t,
+ tuple<_Args...> __args,
+ tuple<_BoundArgs...> __bound_args = tuple<>{})
+ : _M_value(std::make_from_tuple<_Tp>(std::move(__args))),
+ _M_bound(std::make_from_tuple<_Bound>(std::move(__bound_args)))
+ { }
+
+ constexpr _Iterator
+ begin() const
+ { return _Iterator(std::__addressof(*_M_value)); }
+
+ constexpr _Iterator
+ end() const requires (!same_as<_Bound, unreachable_sentinel_t>)
+ { return _Iterator(std::__addressof(*_M_value), _M_bound); }
+
+ constexpr unreachable_sentinel_t
+ end() const noexcept
+ { return unreachable_sentinel; }
+
+ constexpr auto
+ size() const requires (!same_as<_Bound, unreachable_sentinel_t>)
+ { return __detail::__to_unsigned_like(_M_bound); }
+ };
+
+ template<typename _Tp, typename _Bound>
+ repeat_view(_Tp, _Bound) -> repeat_view<_Tp, _Bound>;
+
+ template<copy_constructible _Tp, semiregular _Bound>
+ requires __detail::__is_integer_like<_Bound> || same_as<_Bound, unreachable_sentinel_t>
+ class repeat_view<_Tp, _Bound>::_Iterator
+ {
+ using __index_type
+ = __conditional_t<same_as<_Bound, unreachable_sentinel_t>, ptrdiff_t, _Bound>;
+
+ const _Tp* _M_value = nullptr;
+ __index_type _M_current = __index_type();
+
+ constexpr explicit
+ _Iterator(const _Tp* __value, __index_type __bound = __index_type())
+ : _M_value(__value), _M_current(__bound)
+ {
+ if constexpr (!same_as<_Bound, unreachable_sentinel_t>)
+ __glibcxx_assert(__bound >= 0);
+ }
+
+ friend repeat_view;
+
+ public:
+ using iterator_concept = random_access_iterator_tag;
+ using iterator_category = random_access_iterator_tag;
+ using value_type = _Tp;
+ using difference_type = __conditional_t<__detail::__is_signed_integer_like<__index_type>,
+ __index_type,
+ __detail::__iota_diff_t<__index_type>>;
+
+ _Iterator() = default;
+
+ constexpr const _Tp&
+ operator*() const noexcept
+ { return *_M_value; }
+
+ constexpr _Iterator&
+ operator++()
+ {
+ ++_M_current;
+ return *this;
+ }
+
+ constexpr _Iterator
+ operator++(int)
+ {
+ auto __tmp = *this;
+ ++*this;
+ return __tmp;
+ }
+
+ constexpr _Iterator&
+ operator--()
+ {
+ if constexpr (!same_as<_Bound, unreachable_sentinel_t>)
+ __glibcxx_assert(_M_current > 0);
+ --_M_current;
+ return *this;
+ }
+
+ constexpr _Iterator
+ operator--(int)
+ {
+ auto __tmp = *this;
+ --*this;
+ return __tmp;
+ }
+
+ constexpr _Iterator&
+ operator+=(difference_type __n)
+ {
+ if constexpr (!same_as<_Bound, unreachable_sentinel_t>)
+ __glibcxx_assert(_M_current + __n >= 0);
+ _M_current += __n;
+ return *this;
+ }
+
+ constexpr _Iterator&
+ operator-=(difference_type __n)
+ {
+ if constexpr (!same_as<_Bound, unreachable_sentinel_t>)
+ __glibcxx_assert(_M_current - __n >= 0);
+ _M_current -= __n;
+ return *this;
+ }
+
+ constexpr const _Tp&
+ operator[](difference_type __n) const noexcept
+ { return *(*this + __n); }
+
+ friend constexpr bool
+ operator==(const _Iterator& __x, const _Iterator& __y)
+ { return __x._M_current == __y._M_current; }
+
+ friend constexpr auto
+ operator<=>(const _Iterator& __x, const _Iterator& __y)
+ { return __x._M_current <=> __y._M_current; }
+
+ friend constexpr _Iterator
+ operator+(_Iterator __i, difference_type __n)
+ {
+ __i += __n;
+ return __i;
+ }
+
+ friend constexpr _Iterator
+ operator+(difference_type __n, _Iterator __i)
+ { return __i + __n; }
+
+ friend constexpr _Iterator
+ operator-(_Iterator __i, difference_type __n)
+ {
+ __i -= __n;
+ return __i;
+ }
+
+ friend constexpr difference_type
+ operator-(const _Iterator& __x, const _Iterator& __y)
+ {
+ return (static_cast<difference_type>(__x._M_current)
+ - static_cast<difference_type>(__y._M_current));
+ }
+ };
+
+ namespace views
+ {
+ namespace __detail
+ {
+ template<typename _Tp>
+ concept __can_repeat_view
+ = requires { repeat_view(std::declval<_Tp>()); };
+
+ template<typename _Tp, typename _Bound>
+ concept __can_bounded_repeat_view
+ = requires { repeat_view(std::declval<_Tp>(), std::declval<_Bound>()); };
+ }
+
+ struct _Repeat
+ {
+ template<typename _Tp>
+ requires __detail::__can_repeat_view<_Tp>
+ constexpr auto
+ operator() [[nodiscard]] (_Tp&& __value) const
+ { return repeat_view(std::forward<_Tp>(__value)); }
+
+ template<typename _Tp, typename _Bound>
+ requires __detail::__can_bounded_repeat_view<_Tp, _Bound>
+ constexpr auto
+ operator() [[nodiscard]] (_Tp&& __value, _Bound __bound) const
+ { return repeat_view(std::forward<_Tp>(__value), __bound); }
+ };
+
+ inline constexpr _Repeat repeat;
+ }
#endif // C++23
} // namespace ranges
new file mode 100644
@@ -0,0 +1,93 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <ranges>
+#include <algorithm>
+#include <testsuite_hooks.h>
+
+namespace ranges = std::ranges;
+namespace views = std::views;
+
+constexpr bool
+test01()
+{
+ auto v = views::repeat(42);
+ static_assert(ranges::random_access_range<decltype(v)>
+ && !ranges::sized_range<decltype(v)>);
+ auto i = ranges::begin(v);
+ auto s = ranges::end(v);
+ VERIFY( *i == 42 );
+ VERIFY( i[0] == 42 );
+ VERIFY( &i[0] == &i[1] );
+ VERIFY( &*i == &*(i+1) );
+ VERIFY( i != s );
+ auto j = i + 5, k = i + 12;
+ VERIFY( k - i == 12 );
+ VERIFY( k - j == 7 );
+ VERIFY( i - j == -5 );
+ VERIFY( k > j );
+ VERIFY( j < k );
+ VERIFY( i + 5 == j );
+ VERIFY( i != j );
+ VERIFY( i + 5 <= j );
+ VERIFY( j - 5 >= i );
+
+ return true;
+}
+
+constexpr bool
+test02()
+{
+ constexpr int bound = 20;
+ auto v = views::repeat(42, bound);
+ static_assert(ranges::random_access_range<decltype(v)>
+ && ranges::sized_range<decltype(v)>);
+ VERIFY( ranges::equal(v, views::repeat(42) | views::take(bound)) );
+ auto i = ranges::begin(v);
+ auto s = ranges::end(v);
+ VERIFY( *i == 42 );
+ VERIFY( i[0] == 42 );
+ VERIFY( &i[0] == &i[1] );
+ VERIFY( &*i == &*(i+1) );
+ VERIFY( i != s );
+ auto j = i + 5, k = i + 12;
+ VERIFY( k - i == 12 );
+ VERIFY( k - j == 7 );
+ VERIFY( i - j == -5 );
+ VERIFY( k > j );
+ VERIFY( j < k );
+ VERIFY( i + 5 == j );
+ VERIFY( i != j );
+ VERIFY( i + 5 <= j );
+ VERIFY( j - 5 >= i );
+
+ VERIFY( ranges::size(v) == bound );
+ VERIFY( s - i == bound );
+ VERIFY( s - j == bound - (j - i) );
+ VERIFY( i + bound == s );
+ VERIFY( bound + i == s );
+
+ return true;
+}
+
+constexpr bool
+test03()
+{
+ struct A { int n, m; };
+ auto v = ranges::repeat_view<A, unsigned>(std::piecewise_construct,
+ std::tuple{1, 2},
+ std::tuple{3});
+ VERIFY( v[0].n == 1 );
+ VERIFY( v[0].m == 2 );
+ VERIFY( ranges::size(v) == 3 );
+
+ return true;
+}
+
+int
+main()
+{
+ static_assert(test01());
+ static_assert(test02());
+ static_assert(test03());
+}