* [PATCH] libstdc++: Implement ranges::concat_view from P2542R7
@ 2024-04-22 21:42 Patrick Palka
2024-04-29 9:11 ` Jonathan Wakely
0 siblings, 1 reply; 2+ messages in thread
From: Patrick Palka @ 2024-04-22 21:42 UTC (permalink / raw)
To: gcc-patches; +Cc: libstdc++, Patrick Palka
Tested on x86_64-pc-linux-gnu, does this look OK for trunk? More tests
are needed but I figured I'd submit this now for possible consideration into
GCC 14 since we're getting close to release.. All changes are confined to
C++26.
-- >8 --
libstdc++-v3/ChangeLog:
* include/bits/version.def (ranges_concat): Define.
* include/bits/version.h: Regenerate.
* include/std/ranges (__detail::__concat_reference_t): Define
for C++26.
(__detail::__concat_value_t): Likewise.
(__detail::__concat_rvalue_reference_t): Likewise.
(__detail::__concat_indirectly_readable_impl): Likewise.
(__detail::__concat_indirectly_readable): Likewise.
(__detail::__concatable): Likewise.
(__detail::__all_but_last_common): Likewise.
(__detail::__concat_is_random_access): Likewise.
(__detail::__concat_is_bidirectional): Likewise.
(__detail::__last_is_common): Likewise.
(concat_view): Likewise.
(__detail::__concat_view_iter_cat): Likewise.
(concat_view::iterator): Likewise.
(views::__detail::__can_concat_view): Likewise.
(views::_Concat, views::concat): Likewise.
* testsuite/std/ranges/concat/1.cc: New test.
---
libstdc++-v3/include/bits/version.def | 8 +
libstdc++-v3/include/bits/version.h | 10 +
libstdc++-v3/include/std/ranges | 584 ++++++++++++++++++
libstdc++-v3/testsuite/std/ranges/concat/1.cc | 61 ++
4 files changed, 663 insertions(+)
create mode 100644 libstdc++-v3/testsuite/std/ranges/concat/1.cc
diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
index 5c0477fb61e..af13090c094 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1796,6 +1796,14 @@ ftms = {
};
};
+ftms = {
+ name = ranges_concat;
+ values = {
+ v = 202403;
+ cxxmin = 26;
+ };
+};
+
// Standard test specifications.
stds[97] = ">= 199711L";
stds[03] = ">= 199711L";
diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
index 65e708c73fb..1f27bfe050d 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -2003,4 +2003,14 @@
#endif /* !defined(__cpp_lib_to_string) && defined(__glibcxx_want_to_string) */
#undef __glibcxx_want_to_string
+#if !defined(__cpp_lib_ranges_concat)
+# if (__cplusplus > 202302L)
+# define __glibcxx_ranges_concat 202403L
+# if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges_concat)
+# define __cpp_lib_ranges_concat 202403L
+# endif
+# endif
+#endif /* !defined(__cpp_lib_ranges_concat) && defined(__glibcxx_want_ranges_concat) */
+#undef __glibcxx_want_ranges_concat
+
#undef __glibcxx_want_all
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index afce818376b..28a39bf6f34 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -55,6 +55,7 @@
#define __glibcxx_want_ranges_as_const
#define __glibcxx_want_ranges_as_rvalue
#define __glibcxx_want_ranges_cartesian_product
+#define __glibcxx_want_ranges_concat
#define __glibcxx_want_ranges_chunk
#define __glibcxx_want_ranges_chunk_by
#define __glibcxx_want_ranges_enumerate
@@ -9514,6 +9515,589 @@ namespace __detail
} // namespace ranges
#endif // __cpp_lib_ranges_to_container
+#if __cpp_lib_ranges_concat // C++ >= C++26
+namespace ranges
+{
+ namespace __detail
+ {
+ template<typename... _Rs>
+ using __concat_reference_t = common_reference_t<range_reference_t<_Rs>...>;
+
+ template<typename... _Rs>
+ using __concat_value_t = common_type_t<range_value_t<_Rs>...>;
+
+ template<typename... _Rs>
+ using __concat_rvalue_reference_t
+ = common_reference_t<range_rvalue_reference_t<_Rs>...>;
+
+ template<typename _Ref, typename _RRef, typename _It>
+ concept __concat_indirectly_readable_impl = requires (const _It __it) {
+ { *__it } -> convertible_to<_Ref>;
+ { ranges::iter_move(__it) } -> convertible_to<_RRef>;
+ };
+
+ template<typename... _Rs>
+ concept __concat_indirectly_readable
+ = common_reference_with<__concat_reference_t<_Rs...>&&, __concat_value_t<_Rs...>&>
+ && common_reference_with<__concat_reference_t<_Rs...>&&,
+ __concat_rvalue_reference_t<_Rs...>&&>
+ && common_reference_with<__concat_rvalue_reference_t<_Rs...>&&,
+ __concat_value_t<_Rs...> const&>
+ && (__concat_indirectly_readable_impl<__concat_reference_t<_Rs...>,
+ __concat_rvalue_reference_t<_Rs...>,
+ iterator_t<_Rs>>
+ && ...);
+
+ template<typename... _Rs>
+ concept __concatable = requires {
+ typename __concat_reference_t<_Rs...>;
+ typename __concat_value_t<_Rs...>;
+ typename __concat_rvalue_reference_t<_Rs...>;
+ } && __concat_indirectly_readable<_Rs...>;
+
+ template<bool _Const, typename _Range, typename... _Rs>
+ struct __all_but_last_common
+ {
+ static inline constexpr bool value
+ = requires { requires (common_range<__maybe_const_t<_Const, _Range>>
+ && __all_but_last_common<_Const, _Rs...>::value); };
+ };
+
+ template<bool _Const, typename _Range>
+ struct __all_but_last_common<_Const, _Range>
+ { static inline constexpr bool value = true; };
+
+ template<bool _Const, typename... _Rs>
+ concept __concat_is_random_access = __all_random_access<_Const, _Rs...>
+ && __all_but_last_common<_Const, _Rs...>::value;
+
+ template<bool _Const, typename... _Rs>
+ concept __concat_is_bidirectional = __all_bidirectional<_Const, _Rs...>
+ && __all_but_last_common<_Const, _Rs...>::value;
+
+ template<typename _Range, typename... _Rs>
+ struct __last_is_common
+ { static inline constexpr bool value = __last_is_common<_Rs...>::value; };
+
+ template<typename _Range>
+ struct __last_is_common<_Range>
+ { static inline constexpr bool value = common_range<_Range>; };
+ } // namespace __detail
+
+ template<input_range... _Vs>
+ requires (view<_Vs> && ...) && (sizeof...(_Vs) > 0) && __detail::__concatable<_Vs...>
+ class concat_view : public view_interface<concat_view<_Vs...>>
+ {
+ tuple<_Vs...> _M_views;
+
+ template<bool _Const> class iterator;
+
+ public:
+ constexpr concat_view() = default;
+
+ constexpr explicit
+ concat_view(_Vs... __views)
+ : _M_views(std::move(__views)...)
+ { }
+
+ constexpr iterator<false>
+ begin() requires(!(__detail::__simple_view<_Vs> && ...))
+ {
+ iterator<false> __it(this, in_place_index<0>, ranges::begin(std::get<0>(_M_views)));
+ __it.template _M_satisfy<0>();
+ return __it;
+ }
+
+ constexpr iterator<true>
+ begin() const requires (range<const _Vs> && ...) && __detail::__concatable<const _Vs...>
+ {
+ iterator<true> __it(this, in_place_index<0>, ranges::begin(std::get<0>(_M_views)));
+ __it.template _M_satisfy<0>();
+ return __it;
+ }
+
+ constexpr auto
+ end() requires(!(__detail::__simple_view<_Vs> && ...))
+ {
+ if constexpr (__detail::__last_is_common<_Vs...>::value)
+ {
+ constexpr auto __n = sizeof...(_Vs);
+ return iterator<false>(this, in_place_index<__n - 1>,
+ ranges::end(std::get<__n - 1>(_M_views)));
+ }
+ else
+ return default_sentinel;
+ }
+
+ constexpr auto
+ end() const requires (range<const _Vs> && ...) && __detail::__concatable<const _Vs...>
+ {
+ if constexpr (__detail::__last_is_common<const _Vs...>::value)
+ {
+ constexpr auto __n = sizeof...(_Vs);
+ return iterator<true>(this, in_place_index<__n - 1>,
+ ranges::end(std::get<__n - 1>(_M_views)));
+ }
+ else
+ return default_sentinel;
+ }
+
+ constexpr auto
+ size() requires (sized_range<_Vs>&&...)
+ {
+ return std::apply([](auto... __sizes) {
+ using _CT = __detail::__make_unsigned_like_t<common_type_t<decltype(__sizes)...>>;
+ return (_CT(__sizes) + ...);
+ }, __detail::__tuple_transform(ranges::size, _M_views));
+ }
+
+ constexpr auto
+ size() const requires (sized_range<const _Vs>&&...)
+ {
+ return std::apply([](auto... __sizes) {
+ using _CT = __detail::__make_unsigned_like_t<common_type_t<decltype(__sizes)...>>;
+ return (_CT(__sizes) + ...);
+ }, __detail::__tuple_transform(ranges::size, _M_views));
+ }
+ };
+
+ template <typename... _Rs>
+ concat_view(_Rs&&...) -> concat_view<views::all_t<_Rs>...>;
+
+ namespace __detail
+ {
+ template<bool _Const, typename... _Vs>
+ struct __concat_view_iter_cat
+ { };
+
+ template<bool _Const, typename... _Vs>
+ requires __detail::__all_forward<_Const, _Vs...>
+ struct __concat_view_iter_cat<_Const, _Vs...>
+ {
+ static auto
+ _S_iter_cat()
+ {
+ if constexpr (!is_reference_v<__concat_reference_t<__maybe_const_t<_Const, _Vs>...>>)
+ return input_iterator_tag{};
+ else
+ return []<typename... _Cats>(_Cats... __cats) {
+ if constexpr ((derived_from<_Cats, random_access_iterator_tag> && ...)
+ && __concat_is_random_access<_Const, _Vs...>)
+ return random_access_iterator_tag{};
+ else if constexpr ((derived_from<_Cats, bidirectional_iterator_tag> && ...)
+ && __concat_is_bidirectional<_Const, _Vs...>)
+ return bidirectional_iterator_tag{};
+ else if constexpr ((derived_from<_Cats, forward_iterator_tag> && ...))
+ return forward_iterator_tag{};
+ else
+ return input_iterator_tag{};
+ }(typename iterator_traits<iterator_t<__maybe_const_t<_Const, _Vs>>>
+ ::iterator_category{}...);
+ }
+ };
+ }
+
+ template<input_range... _Vs>
+ requires (view<_Vs> && ...) && (sizeof...(_Vs) > 0) && __detail::__concatable<_Vs...>
+ template<bool _Const>
+ class concat_view<_Vs...>::iterator
+ : public __detail::__concat_view_iter_cat<_Const, _Vs...>
+ {
+ static auto
+ _S_iter_concept()
+ {
+ if constexpr (__detail::__concat_is_random_access<_Const, _Vs...>)
+ return random_access_iterator_tag{};
+ else if constexpr (__detail::__concat_is_bidirectional<_Const, _Vs...>)
+ return bidirectional_iterator_tag{};
+ else if constexpr (__detail::__all_forward<_Const, _Vs...>)
+ return forward_iterator_tag{};
+ else
+ return input_iterator_tag{};
+ }
+
+ friend concat_view;
+ friend iterator<!_Const>;
+
+ public:
+ // iterator_category defined in __concat_view_iter_cat
+ using iterator_concept = decltype(_S_iter_concept());
+ using value_type = __detail::__concat_value_t<__maybe_const_t<_Const, _Vs>...>;
+ using difference_type = common_type_t<range_difference_t<__maybe_const_t<_Const, _Vs>>...>;
+
+ private:
+ using __base_iter = variant<iterator_t<__maybe_const_t<_Const, _Vs>>...>;
+
+ __maybe_const_t<_Const, concat_view>* _M_parent = nullptr;
+ __base_iter _M_it;
+
+ template<size_t _Nm>
+ constexpr void
+ _M_satisfy()
+ {
+ if constexpr (_Nm < (sizeof...(_Vs) - 1))
+ {
+ if (std::get<_Nm>(_M_it) == ranges::end(std::get<_Nm>(_M_parent->_M_views)))
+ {
+ _M_it.template emplace<_Nm + 1>(ranges::begin
+ (std::get<_Nm + 1>(_M_parent->_M_views)));
+ _M_satisfy<_Nm + 1>();
+ }
+ }
+ }
+
+ template<size_t _Nm>
+ constexpr void
+ _M_prev()
+ {
+ if constexpr (_Nm == 0)
+ --std::get<0>(_M_it);
+ else
+ {
+ if (std::get<_Nm>(_M_it) == ranges::begin(std::get<_Nm>(_M_parent->_M_views)))
+ {
+ _M_it.template emplace<_Nm - 1>(ranges::end
+ (std::get<_Nm - 1>(_M_parent->_M_views)));
+ _M_prev<_Nm - 1>();
+ }
+ else
+ --std::get<_Nm>(_M_it);
+ }
+ }
+
+ template<size_t _Nm>
+ constexpr void
+ _M_advance_fwd(difference_type __offset, difference_type __steps)
+ {
+ using _Dt = iter_difference_t<variant_alternative_t<_Nm, __base_iter>>;
+ if constexpr (_Nm == sizeof...(_Vs) - 1)
+ std::get<_Nm>(_M_it) += static_cast<_Dt>(__steps);
+ else
+ {
+ auto __n_size = ranges::distance(std::get<_Nm>(_M_parent->_M_views));
+ if (__offset + __steps < __n_size)
+ std::get<_Nm>(_M_it) += static_cast<_Dt>(__steps);
+ else
+ {
+ _M_it.template emplace<_Nm + 1>(ranges::begin
+ (std::get<_Nm + 1>(_M_parent->_M_views)));
+ _M_advance_fwd<_Nm + 1>(0, __offset + __steps - __n_size);
+ }
+ }
+ }
+
+ template<size_t _Nm>
+ constexpr void
+ _M_advance_bwd(difference_type __offset, difference_type __steps)
+ {
+ using _Dt = iter_difference_t<variant_alternative_t<_Nm, __base_iter>>;
+ if constexpr (_Nm == 0)
+ std::get<_Nm>(_M_it) -= static_cast<_Dt>(__steps);
+ else {
+ if (__offset >= __steps)
+ std::get<_Nm>(_M_it) -= static_cast<_Dt>(__steps);
+ else
+ {
+ auto __prev_size = ranges::distance(std::get<_Nm - 1>(_M_parent->_M_views));
+ _M_it.template emplace<_Nm - 1>(ranges::end
+ (std::get<_Nm - 1>(_M_parent->_M_views)));
+ _M_advance_bwd<_Nm - 1>(__prev_size, __steps - __offset);
+ }
+ }
+ }
+
+ // Invoke the function object __f, which has a call operator taking a size_t
+ // index template parameter (bounded by the number of underlying views),
+ // according the runtime value of __index.
+ template<typename _Fp>
+ static constexpr auto
+ _S_invoke_with_runtime_index(_Fp&& __f, size_t __index)
+ {
+ return [&__f, __index]<size_t _Idx>(this auto&& __self) {
+ if (_Idx == __index)
+ return __f.template operator()<_Idx>();
+ if constexpr (_Idx + 1 < sizeof...(_Vs))
+ return __self.template operator()<_Idx + 1>();
+ }.template operator()<0>();
+ }
+
+ template<typename _Fp>
+ constexpr auto
+ _M_invoke_with_runtime_index(_Fp&& __f)
+ { return _S_invoke_with_runtime_index(std::forward<_Fp>(__f), _M_it.index()); }
+
+ template<typename... _Args>
+ explicit constexpr
+ iterator(__maybe_const_t<_Const, concat_view>* __parent, _Args&&... __args)
+ requires constructible_from<__base_iter, _Args&&...>
+ : _M_parent(__parent), _M_it(std::forward<_Args>(__args)...)
+ { }
+
+ public:
+ iterator() = default;
+
+ constexpr
+ iterator(iterator<!_Const> __it)
+ requires _Const && (convertible_to<iterator_t<_Vs>, iterator_t<const _Vs>> && ...)
+ : _M_parent(__it._M_parent)
+ {
+ _M_invoke_with_runtime_index([this, &__it]<size_t _Idx>() {
+ _M_it.template emplace<_Idx>(std::get<_Idx>(std::move(__it._M_it)));
+ });
+ }
+
+ constexpr decltype(auto)
+ operator*() const
+ {
+ __glibcxx_assert(!_M_it.valueless_by_exception());
+ using reference = __detail::__concat_reference_t<__maybe_const_t<_Const, _Vs>...>;
+ return std::visit([](auto&& __it) -> reference { return *__it; }, _M_it);
+ }
+
+ constexpr iterator&
+ operator++()
+ {
+ _M_invoke_with_runtime_index([this]<size_t _Idx>() {
+ ++std::get<_Idx>(_M_it);
+ _M_satisfy<_Idx>();
+ });
+ return *this;
+ }
+
+ constexpr void
+ operator++(int)
+ {
+ ++*this;
+ }
+
+ constexpr iterator
+ operator++(int)
+ requires __detail::__all_forward<_Const, _Vs...>
+ {
+ auto __tmp = *this;
+ ++*this;
+ return __tmp;
+ }
+
+ constexpr iterator&
+ operator--()
+ requires __detail::__concat_is_bidirectional<_Const, _Vs...>
+ {
+ __glibcxx_assert(!_M_it.valueless_by_exception());
+ _M_invoke_with_runtime_index([this]<size_t _Idx>() {
+ _M_prev<_Idx>();
+ });
+ return *this;
+ }
+
+ constexpr iterator
+ operator--(int)
+ requires __detail::__concat_is_bidirectional<_Const, _Vs...>
+ {
+ auto __tmp = *this;
+ --*this;
+ return __tmp;
+ }
+
+ constexpr iterator&
+ operator+=(difference_type __n)
+ requires __detail::__concat_is_random_access<_Const, _Vs...>
+ {
+ __glibcxx_assert(!_M_it.valueless_by_exception());
+ _M_invoke_with_runtime_index([this, __n]<size_t _Idx>() {
+ auto __begin = ranges::begin(std::get<_Idx>(_M_parent->_M_views));
+ if (__n > 0)
+ _M_advance_fwd<_Idx>(std::get<_Idx>(_M_it) - __begin, __n);
+ else if (__n < 0)
+ _M_advance_bwd<_Idx>(std::get<_Idx>(_M_it) - __begin, -__n);
+ });
+ return *this;
+ }
+
+ constexpr iterator&
+ operator-=(difference_type __n)
+ requires __detail::__concat_is_random_access<_Const, _Vs...>
+ {
+ *this += -__n;
+ return *this;
+ }
+
+ constexpr decltype(auto)
+ operator[](difference_type __n) const
+ requires __detail::__concat_is_random_access<_Const, _Vs...>
+ { return *((*this) + __n); }
+
+ friend constexpr bool
+ operator==(const iterator& __x, const iterator& __y)
+ requires (equality_comparable<iterator_t<__maybe_const_t<_Const, _Vs>>> && ...)
+ {
+ __glibcxx_assert(!__x._M_it.valueless_by_exception());
+ __glibcxx_assert(!__y._M_it.valueless_by_exception());
+ return __x._M_it == __y._M_it;
+ }
+
+ friend constexpr bool
+ operator==(const iterator& __it, default_sentinel_t)
+ {
+ __glibcxx_assert(!__it._M_it.valueless_by_exception());
+ constexpr auto __last_idx = sizeof...(_Vs) - 1;
+ return (__it._M_it.index() == __last_idx
+ && (std::get<__last_idx>(__it._M_it)
+ == ranges::end(std::get<__last_idx>(__it._M_parent->_M_views))));
+ }
+
+ friend constexpr bool
+ operator<(const iterator& __x, const iterator& __y)
+ requires __detail::__all_random_access<_Const, _Vs...>
+ { return __x._M_it < __y._M_it; }
+
+ friend constexpr bool
+ operator>(const iterator& __x, const iterator& __y)
+ requires __detail::__all_random_access<_Const, _Vs...>
+ { return __x._M_it > __y._M_it; }
+
+ friend constexpr bool
+ operator<=(const iterator& __x, const iterator& __y)
+ requires __detail::__all_random_access<_Const, _Vs...>
+ { return __x._M_it <= __y._M_it; }
+
+ friend constexpr bool
+ operator>=(const iterator& __x, const iterator& __y)
+ requires __detail::__all_random_access<_Const, _Vs...>
+ { return __x._M_it >= __y._M_it; }
+
+ friend constexpr auto
+ operator<=>(const iterator& __x, const iterator& __y)
+ requires (__detail::__all_random_access<_Const, _Vs...>
+ && (three_way_comparable<iterator_t<__maybe_const_t<_Const, _Vs>>>
+ && ...))
+ { return __x._M_it <=> __y._M_it; }
+
+ friend constexpr iterator
+ operator+(const iterator& __it, difference_type __n)
+ requires __detail::__concat_is_random_access<_Const, _Vs...>
+ { return auto(__it) += __n; }
+
+ friend constexpr iterator
+ operator+(difference_type __n, const iterator& __it)
+ requires __detail::__concat_is_random_access<_Const, _Vs...>
+ { return __it + __n; }
+
+ friend constexpr iterator
+ operator-(const iterator& __it, difference_type __n)
+ requires __detail::__concat_is_random_access<_Const, _Vs...>
+ { return auto(__it) -= __n; }
+
+ friend constexpr difference_type
+ operator-(const iterator& __x, const iterator& __y)
+ requires __detail::__concat_is_random_access<_Const, _Vs...>
+ {
+ return _S_invoke_with_runtime_index([&]<size_t _Ix>() -> difference_type {
+ return _S_invoke_with_runtime_index([&]<size_t _Iy>() -> difference_type {
+ if constexpr (_Ix > _Iy)
+ {
+ auto __dy = ranges::distance(std::get<_Iy>(__y._M_it),
+ ranges::end(std::get<_Iy>(__y._M_parent
+ ->_M_views)));
+ auto __dx = ranges::distance(ranges::begin(std::get<_Ix>(__x._M_parent
+ ->_M_views)),
+ std::get<_Ix>(__x._M_it));
+ difference_type __sum = 0;
+ [&]<size_t _Idx = _Iy + 1>(this auto&& __self) {
+ if constexpr (_Idx < _Ix)
+ {
+ __sum += ranges::size(std::get<_Idx>(__x._M_parent->_M_views));
+ __self.template operator()<_Idx + 1>();
+ }
+ }();
+ return __dy + __sum + __dx;
+ }
+ else if constexpr (_Ix < _Iy)
+ return -(__y - __x);
+ else
+ return std::get<_Ix>(__x._M_it) - std::get<_Iy>(__y._M_it);
+ }, __y._M_it.index());
+ }, __x._M_it.index());
+ }
+
+ friend constexpr difference_type
+ operator-(const iterator& __x, default_sentinel_t)
+ requires __detail::__concat_is_random_access<_Const, _Vs...>
+ && __detail::__last_is_common<__maybe_const_t<_Const, _Vs>...>::value
+ {
+ return _S_invoke_with_runtime_index([&]<size_t _Ix>() -> difference_type {
+ auto __dx = ranges::distance(std::get<_Ix>(__x._M_it),
+ ranges::end(std::get<_Ix>(__x._M_parent->_M_views)));
+ difference_type __sum = 0;
+ [&]<size_t _Idx = _Ix + 1>(this auto&& __self) {
+ if constexpr (_Idx < sizeof...(_Vs))
+ {
+ __sum += ranges::size(std::get<_Idx>(__x._M_parent->_M_views));
+ __self.template operator()<_Idx + 1>();
+ }
+ }();
+ return -(__dx + __sum);
+ }, __x._M_it.index());
+ }
+
+ friend constexpr difference_type
+ operator-(default_sentinel_t, const iterator& __x)
+ requires __detail::__concat_is_random_access<_Const, _Vs...>
+ && __detail::__last_is_common<__maybe_const_t<_Const, _Vs>...>::value
+ { return -(__x - default_sentinel); }
+
+ friend constexpr decltype(auto)
+ iter_move(const iterator& __it)
+ {
+ using _Res = __detail::__concat_rvalue_reference_t<__maybe_const_t<_Const, _Vs>...>;
+ return std::visit([](const auto& __i) -> _Res {
+ return ranges::iter_move(__i);
+ }, __it._M_it);
+ }
+
+ friend constexpr void
+ iter_swap(const iterator& __x, const iterator& __y)
+ requires swappable_with<iter_reference_t<iterator>,
+ iter_reference_t<iterator>>
+ && (... && indirectly_swappable<iterator_t<__maybe_const_t<_Const, _Vs>>>)
+ {
+ std::visit([&](const auto& __it1, const auto& __it2) {
+ if constexpr (is_same_v<decltype(__it1), decltype(__it2)>)
+ ranges::iter_swap(__it1, __it2);
+ else
+ ranges::swap(*__it1, *__it2);
+ }, __x._M_it, __y._M_it);
+ }
+ };
+
+ namespace views
+ {
+ namespace __detail
+ {
+ template<typename... _Ts>
+ concept __can_concat_view = requires { concat_view(std::declval<_Ts>()...); };
+ }
+
+ struct _Concat
+ {
+ template<typename... _Ts>
+ requires __detail::__can_concat_view<_Ts...>
+ constexpr auto
+ operator() [[nodiscard]] (_Ts&&... __ts) const
+ {
+ if constexpr (sizeof...(_Ts) == 1)
+ return views::all(std::forward<_Ts>(__ts)...);
+ else
+ return concat_view(std::forward<_Ts>(__ts)...);
+ }
+ };
+
+ inline constexpr _Concat concat;
+ }
+
+} // namespace ranges
+#endif // __cpp_lib_ranges_concat
+
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std
#endif // library concepts
diff --git a/libstdc++-v3/testsuite/std/ranges/concat/1.cc b/libstdc++-v3/testsuite/std/ranges/concat/1.cc
new file mode 100644
index 00000000000..91c8893cdc5
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/concat/1.cc
@@ -0,0 +1,61 @@
+// { dg-do run { target c++26 } }
+// { dg-add-options no_pch }
+
+#include <ranges>
+
+#if __cpp_lib_ranges_concat != 202403L
+# error "Feature-test macro __cpp_lib_ranges_concat has wrong value in <ranges>"
+#endif
+
+#include <algorithm>
+#include <vector>
+#include <array>
+#include <utility>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+namespace ranges = std::ranges;
+namespace views = std::views;
+
+constexpr bool
+test01()
+{
+ std::vector<int> v1{1, 2, 3}, v2{4, 5}, v3{};
+ std::array a{6, 7, 8};
+ auto s = views::single(9);
+
+ auto v = views::concat(v1, v2, v3, a, s);
+ VERIFY( ranges::size(v) == 9 );
+ VERIFY( ranges::equal(v, views::iota(1, 10)) );
+ VERIFY( ranges::equal(v, views::iota(1, 10)) );
+ VERIFY( ranges::equal(v | views::reverse,
+ views::iota(1, 10) | views::reverse) );
+
+ auto it0 = v.begin();
+ auto cit = std::as_const(v).begin();
+ VERIFY( it0 == it0 );
+ VERIFY( cit == cit );
+ VERIFY( it0 == cit );
+ VERIFY( it0 - it0 == 0);
+ for (int i = 0; i < 10; i++)
+ {
+ VERIFY( it0 + i - it0 == i);
+ VERIFY( it0 + i - i + i == it0 + i );
+ VERIFY( it0 + i - (it0 + i) == 0 );
+ }
+ VERIFY( std::default_sentinel - it0 == 9 );
+ VERIFY( it0 + 9 == std::default_sentinel );
+
+ auto it5 = it0+5;
+ ranges::iter_swap(it0, it5);
+ VERIFY( *it0 == 6 && *it5 == 1 );
+ ranges::iter_swap(it0, it5);
+ *it0 = ranges::iter_move(it0);
+ return true;
+}
+
+int
+main()
+{
+ static_assert(test01());
+}
--
2.45.0.rc0
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [PATCH] libstdc++: Implement ranges::concat_view from P2542R7
2024-04-22 21:42 [PATCH] libstdc++: Implement ranges::concat_view from P2542R7 Patrick Palka
@ 2024-04-29 9:11 ` Jonathan Wakely
0 siblings, 0 replies; 2+ messages in thread
From: Jonathan Wakely @ 2024-04-29 9:11 UTC (permalink / raw)
To: Patrick Palka; +Cc: gcc-patches, libstdc++
On Mon, 22 Apr 2024 at 22:43, Patrick Palka wrote:
>
> Tested on x86_64-pc-linux-gnu, does this look OK for trunk? More tests
> are needed but I figured I'd submit this now for possible consideration into
> GCC 14 since we're getting close to release.. All changes are confined to
> C++26.
OK for trunk. Maybe we can backport it for 14.2 later, but not now.
Sorry for the review being slow.
>
> -- >8 --
>
> libstdc++-v3/ChangeLog:
>
> * include/bits/version.def (ranges_concat): Define.
> * include/bits/version.h: Regenerate.
> * include/std/ranges (__detail::__concat_reference_t): Define
> for C++26.
> (__detail::__concat_value_t): Likewise.
> (__detail::__concat_rvalue_reference_t): Likewise.
> (__detail::__concat_indirectly_readable_impl): Likewise.
> (__detail::__concat_indirectly_readable): Likewise.
> (__detail::__concatable): Likewise.
> (__detail::__all_but_last_common): Likewise.
> (__detail::__concat_is_random_access): Likewise.
> (__detail::__concat_is_bidirectional): Likewise.
> (__detail::__last_is_common): Likewise.
> (concat_view): Likewise.
> (__detail::__concat_view_iter_cat): Likewise.
> (concat_view::iterator): Likewise.
> (views::__detail::__can_concat_view): Likewise.
> (views::_Concat, views::concat): Likewise.
> * testsuite/std/ranges/concat/1.cc: New test.
> ---
> libstdc++-v3/include/bits/version.def | 8 +
> libstdc++-v3/include/bits/version.h | 10 +
> libstdc++-v3/include/std/ranges | 584 ++++++++++++++++++
> libstdc++-v3/testsuite/std/ranges/concat/1.cc | 61 ++
> 4 files changed, 663 insertions(+)
> create mode 100644 libstdc++-v3/testsuite/std/ranges/concat/1.cc
>
> diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
> index 5c0477fb61e..af13090c094 100644
> --- a/libstdc++-v3/include/bits/version.def
> +++ b/libstdc++-v3/include/bits/version.def
> @@ -1796,6 +1796,14 @@ ftms = {
> };
> };
>
> +ftms = {
> + name = ranges_concat;
> + values = {
> + v = 202403;
> + cxxmin = 26;
> + };
> +};
> +
> // Standard test specifications.
> stds[97] = ">= 199711L";
> stds[03] = ">= 199711L";
> diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
> index 65e708c73fb..1f27bfe050d 100644
> --- a/libstdc++-v3/include/bits/version.h
> +++ b/libstdc++-v3/include/bits/version.h
> @@ -2003,4 +2003,14 @@
> #endif /* !defined(__cpp_lib_to_string) && defined(__glibcxx_want_to_string) */
> #undef __glibcxx_want_to_string
>
> +#if !defined(__cpp_lib_ranges_concat)
> +# if (__cplusplus > 202302L)
> +# define __glibcxx_ranges_concat 202403L
> +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges_concat)
> +# define __cpp_lib_ranges_concat 202403L
> +# endif
> +# endif
> +#endif /* !defined(__cpp_lib_ranges_concat) && defined(__glibcxx_want_ranges_concat) */
> +#undef __glibcxx_want_ranges_concat
> +
> #undef __glibcxx_want_all
> diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
> index afce818376b..28a39bf6f34 100644
> --- a/libstdc++-v3/include/std/ranges
> +++ b/libstdc++-v3/include/std/ranges
> @@ -55,6 +55,7 @@
> #define __glibcxx_want_ranges_as_const
> #define __glibcxx_want_ranges_as_rvalue
> #define __glibcxx_want_ranges_cartesian_product
> +#define __glibcxx_want_ranges_concat
> #define __glibcxx_want_ranges_chunk
> #define __glibcxx_want_ranges_chunk_by
> #define __glibcxx_want_ranges_enumerate
> @@ -9514,6 +9515,589 @@ namespace __detail
> } // namespace ranges
> #endif // __cpp_lib_ranges_to_container
>
> +#if __cpp_lib_ranges_concat // C++ >= C++26
> +namespace ranges
> +{
> + namespace __detail
> + {
> + template<typename... _Rs>
> + using __concat_reference_t = common_reference_t<range_reference_t<_Rs>...>;
> +
> + template<typename... _Rs>
> + using __concat_value_t = common_type_t<range_value_t<_Rs>...>;
> +
> + template<typename... _Rs>
> + using __concat_rvalue_reference_t
> + = common_reference_t<range_rvalue_reference_t<_Rs>...>;
> +
> + template<typename _Ref, typename _RRef, typename _It>
> + concept __concat_indirectly_readable_impl = requires (const _It __it) {
> + { *__it } -> convertible_to<_Ref>;
> + { ranges::iter_move(__it) } -> convertible_to<_RRef>;
> + };
> +
> + template<typename... _Rs>
> + concept __concat_indirectly_readable
> + = common_reference_with<__concat_reference_t<_Rs...>&&, __concat_value_t<_Rs...>&>
> + && common_reference_with<__concat_reference_t<_Rs...>&&,
> + __concat_rvalue_reference_t<_Rs...>&&>
> + && common_reference_with<__concat_rvalue_reference_t<_Rs...>&&,
> + __concat_value_t<_Rs...> const&>
> + && (__concat_indirectly_readable_impl<__concat_reference_t<_Rs...>,
> + __concat_rvalue_reference_t<_Rs...>,
> + iterator_t<_Rs>>
> + && ...);
> +
> + template<typename... _Rs>
> + concept __concatable = requires {
> + typename __concat_reference_t<_Rs...>;
> + typename __concat_value_t<_Rs...>;
> + typename __concat_rvalue_reference_t<_Rs...>;
> + } && __concat_indirectly_readable<_Rs...>;
> +
> + template<bool _Const, typename _Range, typename... _Rs>
> + struct __all_but_last_common
> + {
> + static inline constexpr bool value
> + = requires { requires (common_range<__maybe_const_t<_Const, _Range>>
> + && __all_but_last_common<_Const, _Rs...>::value); };
> + };
> +
> + template<bool _Const, typename _Range>
> + struct __all_but_last_common<_Const, _Range>
> + { static inline constexpr bool value = true; };
> +
> + template<bool _Const, typename... _Rs>
> + concept __concat_is_random_access = __all_random_access<_Const, _Rs...>
> + && __all_but_last_common<_Const, _Rs...>::value;
> +
> + template<bool _Const, typename... _Rs>
> + concept __concat_is_bidirectional = __all_bidirectional<_Const, _Rs...>
> + && __all_but_last_common<_Const, _Rs...>::value;
> +
> + template<typename _Range, typename... _Rs>
> + struct __last_is_common
> + { static inline constexpr bool value = __last_is_common<_Rs...>::value; };
> +
> + template<typename _Range>
> + struct __last_is_common<_Range>
> + { static inline constexpr bool value = common_range<_Range>; };
> + } // namespace __detail
> +
> + template<input_range... _Vs>
> + requires (view<_Vs> && ...) && (sizeof...(_Vs) > 0) && __detail::__concatable<_Vs...>
> + class concat_view : public view_interface<concat_view<_Vs...>>
> + {
> + tuple<_Vs...> _M_views;
> +
> + template<bool _Const> class iterator;
> +
> + public:
> + constexpr concat_view() = default;
> +
> + constexpr explicit
> + concat_view(_Vs... __views)
> + : _M_views(std::move(__views)...)
> + { }
> +
> + constexpr iterator<false>
> + begin() requires(!(__detail::__simple_view<_Vs> && ...))
> + {
> + iterator<false> __it(this, in_place_index<0>, ranges::begin(std::get<0>(_M_views)));
> + __it.template _M_satisfy<0>();
> + return __it;
> + }
> +
> + constexpr iterator<true>
> + begin() const requires (range<const _Vs> && ...) && __detail::__concatable<const _Vs...>
> + {
> + iterator<true> __it(this, in_place_index<0>, ranges::begin(std::get<0>(_M_views)));
> + __it.template _M_satisfy<0>();
> + return __it;
> + }
> +
> + constexpr auto
> + end() requires(!(__detail::__simple_view<_Vs> && ...))
> + {
> + if constexpr (__detail::__last_is_common<_Vs...>::value)
> + {
> + constexpr auto __n = sizeof...(_Vs);
> + return iterator<false>(this, in_place_index<__n - 1>,
> + ranges::end(std::get<__n - 1>(_M_views)));
> + }
> + else
> + return default_sentinel;
> + }
> +
> + constexpr auto
> + end() const requires (range<const _Vs> && ...) && __detail::__concatable<const _Vs...>
> + {
> + if constexpr (__detail::__last_is_common<const _Vs...>::value)
> + {
> + constexpr auto __n = sizeof...(_Vs);
> + return iterator<true>(this, in_place_index<__n - 1>,
> + ranges::end(std::get<__n - 1>(_M_views)));
> + }
> + else
> + return default_sentinel;
> + }
> +
> + constexpr auto
> + size() requires (sized_range<_Vs>&&...)
> + {
> + return std::apply([](auto... __sizes) {
> + using _CT = __detail::__make_unsigned_like_t<common_type_t<decltype(__sizes)...>>;
> + return (_CT(__sizes) + ...);
> + }, __detail::__tuple_transform(ranges::size, _M_views));
> + }
> +
> + constexpr auto
> + size() const requires (sized_range<const _Vs>&&...)
> + {
> + return std::apply([](auto... __sizes) {
> + using _CT = __detail::__make_unsigned_like_t<common_type_t<decltype(__sizes)...>>;
> + return (_CT(__sizes) + ...);
> + }, __detail::__tuple_transform(ranges::size, _M_views));
> + }
> + };
> +
> + template <typename... _Rs>
> + concat_view(_Rs&&...) -> concat_view<views::all_t<_Rs>...>;
> +
> + namespace __detail
> + {
> + template<bool _Const, typename... _Vs>
> + struct __concat_view_iter_cat
> + { };
> +
> + template<bool _Const, typename... _Vs>
> + requires __detail::__all_forward<_Const, _Vs...>
> + struct __concat_view_iter_cat<_Const, _Vs...>
> + {
> + static auto
> + _S_iter_cat()
> + {
> + if constexpr (!is_reference_v<__concat_reference_t<__maybe_const_t<_Const, _Vs>...>>)
> + return input_iterator_tag{};
> + else
> + return []<typename... _Cats>(_Cats... __cats) {
> + if constexpr ((derived_from<_Cats, random_access_iterator_tag> && ...)
> + && __concat_is_random_access<_Const, _Vs...>)
> + return random_access_iterator_tag{};
> + else if constexpr ((derived_from<_Cats, bidirectional_iterator_tag> && ...)
> + && __concat_is_bidirectional<_Const, _Vs...>)
> + return bidirectional_iterator_tag{};
> + else if constexpr ((derived_from<_Cats, forward_iterator_tag> && ...))
> + return forward_iterator_tag{};
> + else
> + return input_iterator_tag{};
> + }(typename iterator_traits<iterator_t<__maybe_const_t<_Const, _Vs>>>
> + ::iterator_category{}...);
> + }
> + };
> + }
> +
> + template<input_range... _Vs>
> + requires (view<_Vs> && ...) && (sizeof...(_Vs) > 0) && __detail::__concatable<_Vs...>
> + template<bool _Const>
> + class concat_view<_Vs...>::iterator
> + : public __detail::__concat_view_iter_cat<_Const, _Vs...>
> + {
> + static auto
> + _S_iter_concept()
> + {
> + if constexpr (__detail::__concat_is_random_access<_Const, _Vs...>)
> + return random_access_iterator_tag{};
> + else if constexpr (__detail::__concat_is_bidirectional<_Const, _Vs...>)
> + return bidirectional_iterator_tag{};
> + else if constexpr (__detail::__all_forward<_Const, _Vs...>)
> + return forward_iterator_tag{};
> + else
> + return input_iterator_tag{};
> + }
> +
> + friend concat_view;
> + friend iterator<!_Const>;
> +
> + public:
> + // iterator_category defined in __concat_view_iter_cat
> + using iterator_concept = decltype(_S_iter_concept());
> + using value_type = __detail::__concat_value_t<__maybe_const_t<_Const, _Vs>...>;
> + using difference_type = common_type_t<range_difference_t<__maybe_const_t<_Const, _Vs>>...>;
> +
> + private:
> + using __base_iter = variant<iterator_t<__maybe_const_t<_Const, _Vs>>...>;
> +
> + __maybe_const_t<_Const, concat_view>* _M_parent = nullptr;
> + __base_iter _M_it;
> +
> + template<size_t _Nm>
> + constexpr void
> + _M_satisfy()
> + {
> + if constexpr (_Nm < (sizeof...(_Vs) - 1))
> + {
> + if (std::get<_Nm>(_M_it) == ranges::end(std::get<_Nm>(_M_parent->_M_views)))
> + {
> + _M_it.template emplace<_Nm + 1>(ranges::begin
> + (std::get<_Nm + 1>(_M_parent->_M_views)));
> + _M_satisfy<_Nm + 1>();
> + }
> + }
> + }
> +
> + template<size_t _Nm>
> + constexpr void
> + _M_prev()
> + {
> + if constexpr (_Nm == 0)
> + --std::get<0>(_M_it);
> + else
> + {
> + if (std::get<_Nm>(_M_it) == ranges::begin(std::get<_Nm>(_M_parent->_M_views)))
> + {
> + _M_it.template emplace<_Nm - 1>(ranges::end
> + (std::get<_Nm - 1>(_M_parent->_M_views)));
> + _M_prev<_Nm - 1>();
> + }
> + else
> + --std::get<_Nm>(_M_it);
> + }
> + }
> +
> + template<size_t _Nm>
> + constexpr void
> + _M_advance_fwd(difference_type __offset, difference_type __steps)
> + {
> + using _Dt = iter_difference_t<variant_alternative_t<_Nm, __base_iter>>;
> + if constexpr (_Nm == sizeof...(_Vs) - 1)
> + std::get<_Nm>(_M_it) += static_cast<_Dt>(__steps);
> + else
> + {
> + auto __n_size = ranges::distance(std::get<_Nm>(_M_parent->_M_views));
> + if (__offset + __steps < __n_size)
> + std::get<_Nm>(_M_it) += static_cast<_Dt>(__steps);
> + else
> + {
> + _M_it.template emplace<_Nm + 1>(ranges::begin
> + (std::get<_Nm + 1>(_M_parent->_M_views)));
> + _M_advance_fwd<_Nm + 1>(0, __offset + __steps - __n_size);
> + }
> + }
> + }
> +
> + template<size_t _Nm>
> + constexpr void
> + _M_advance_bwd(difference_type __offset, difference_type __steps)
> + {
> + using _Dt = iter_difference_t<variant_alternative_t<_Nm, __base_iter>>;
> + if constexpr (_Nm == 0)
> + std::get<_Nm>(_M_it) -= static_cast<_Dt>(__steps);
> + else {
> + if (__offset >= __steps)
> + std::get<_Nm>(_M_it) -= static_cast<_Dt>(__steps);
> + else
> + {
> + auto __prev_size = ranges::distance(std::get<_Nm - 1>(_M_parent->_M_views));
> + _M_it.template emplace<_Nm - 1>(ranges::end
> + (std::get<_Nm - 1>(_M_parent->_M_views)));
> + _M_advance_bwd<_Nm - 1>(__prev_size, __steps - __offset);
> + }
> + }
> + }
> +
> + // Invoke the function object __f, which has a call operator taking a size_t
> + // index template parameter (bounded by the number of underlying views),
> + // according the runtime value of __index.
> + template<typename _Fp>
> + static constexpr auto
> + _S_invoke_with_runtime_index(_Fp&& __f, size_t __index)
> + {
> + return [&__f, __index]<size_t _Idx>(this auto&& __self) {
> + if (_Idx == __index)
> + return __f.template operator()<_Idx>();
> + if constexpr (_Idx + 1 < sizeof...(_Vs))
> + return __self.template operator()<_Idx + 1>();
> + }.template operator()<0>();
> + }
> +
> + template<typename _Fp>
> + constexpr auto
> + _M_invoke_with_runtime_index(_Fp&& __f)
> + { return _S_invoke_with_runtime_index(std::forward<_Fp>(__f), _M_it.index()); }
> +
> + template<typename... _Args>
> + explicit constexpr
> + iterator(__maybe_const_t<_Const, concat_view>* __parent, _Args&&... __args)
> + requires constructible_from<__base_iter, _Args&&...>
> + : _M_parent(__parent), _M_it(std::forward<_Args>(__args)...)
> + { }
> +
> + public:
> + iterator() = default;
> +
> + constexpr
> + iterator(iterator<!_Const> __it)
> + requires _Const && (convertible_to<iterator_t<_Vs>, iterator_t<const _Vs>> && ...)
> + : _M_parent(__it._M_parent)
> + {
> + _M_invoke_with_runtime_index([this, &__it]<size_t _Idx>() {
> + _M_it.template emplace<_Idx>(std::get<_Idx>(std::move(__it._M_it)));
> + });
> + }
> +
> + constexpr decltype(auto)
> + operator*() const
> + {
> + __glibcxx_assert(!_M_it.valueless_by_exception());
> + using reference = __detail::__concat_reference_t<__maybe_const_t<_Const, _Vs>...>;
> + return std::visit([](auto&& __it) -> reference { return *__it; }, _M_it);
> + }
> +
> + constexpr iterator&
> + operator++()
> + {
> + _M_invoke_with_runtime_index([this]<size_t _Idx>() {
> + ++std::get<_Idx>(_M_it);
> + _M_satisfy<_Idx>();
> + });
> + return *this;
> + }
> +
> + constexpr void
> + operator++(int)
> + {
> + ++*this;
> + }
> +
> + constexpr iterator
> + operator++(int)
> + requires __detail::__all_forward<_Const, _Vs...>
> + {
> + auto __tmp = *this;
> + ++*this;
> + return __tmp;
> + }
> +
> + constexpr iterator&
> + operator--()
> + requires __detail::__concat_is_bidirectional<_Const, _Vs...>
> + {
> + __glibcxx_assert(!_M_it.valueless_by_exception());
> + _M_invoke_with_runtime_index([this]<size_t _Idx>() {
> + _M_prev<_Idx>();
> + });
> + return *this;
> + }
> +
> + constexpr iterator
> + operator--(int)
> + requires __detail::__concat_is_bidirectional<_Const, _Vs...>
> + {
> + auto __tmp = *this;
> + --*this;
> + return __tmp;
> + }
> +
> + constexpr iterator&
> + operator+=(difference_type __n)
> + requires __detail::__concat_is_random_access<_Const, _Vs...>
> + {
> + __glibcxx_assert(!_M_it.valueless_by_exception());
> + _M_invoke_with_runtime_index([this, __n]<size_t _Idx>() {
> + auto __begin = ranges::begin(std::get<_Idx>(_M_parent->_M_views));
> + if (__n > 0)
> + _M_advance_fwd<_Idx>(std::get<_Idx>(_M_it) - __begin, __n);
> + else if (__n < 0)
> + _M_advance_bwd<_Idx>(std::get<_Idx>(_M_it) - __begin, -__n);
> + });
> + return *this;
> + }
> +
> + constexpr iterator&
> + operator-=(difference_type __n)
> + requires __detail::__concat_is_random_access<_Const, _Vs...>
> + {
> + *this += -__n;
> + return *this;
> + }
> +
> + constexpr decltype(auto)
> + operator[](difference_type __n) const
> + requires __detail::__concat_is_random_access<_Const, _Vs...>
> + { return *((*this) + __n); }
> +
> + friend constexpr bool
> + operator==(const iterator& __x, const iterator& __y)
> + requires (equality_comparable<iterator_t<__maybe_const_t<_Const, _Vs>>> && ...)
> + {
> + __glibcxx_assert(!__x._M_it.valueless_by_exception());
> + __glibcxx_assert(!__y._M_it.valueless_by_exception());
> + return __x._M_it == __y._M_it;
> + }
> +
> + friend constexpr bool
> + operator==(const iterator& __it, default_sentinel_t)
> + {
> + __glibcxx_assert(!__it._M_it.valueless_by_exception());
> + constexpr auto __last_idx = sizeof...(_Vs) - 1;
> + return (__it._M_it.index() == __last_idx
> + && (std::get<__last_idx>(__it._M_it)
> + == ranges::end(std::get<__last_idx>(__it._M_parent->_M_views))));
> + }
> +
> + friend constexpr bool
> + operator<(const iterator& __x, const iterator& __y)
> + requires __detail::__all_random_access<_Const, _Vs...>
> + { return __x._M_it < __y._M_it; }
> +
> + friend constexpr bool
> + operator>(const iterator& __x, const iterator& __y)
> + requires __detail::__all_random_access<_Const, _Vs...>
> + { return __x._M_it > __y._M_it; }
> +
> + friend constexpr bool
> + operator<=(const iterator& __x, const iterator& __y)
> + requires __detail::__all_random_access<_Const, _Vs...>
> + { return __x._M_it <= __y._M_it; }
> +
> + friend constexpr bool
> + operator>=(const iterator& __x, const iterator& __y)
> + requires __detail::__all_random_access<_Const, _Vs...>
> + { return __x._M_it >= __y._M_it; }
> +
> + friend constexpr auto
> + operator<=>(const iterator& __x, const iterator& __y)
> + requires (__detail::__all_random_access<_Const, _Vs...>
> + && (three_way_comparable<iterator_t<__maybe_const_t<_Const, _Vs>>>
> + && ...))
> + { return __x._M_it <=> __y._M_it; }
> +
> + friend constexpr iterator
> + operator+(const iterator& __it, difference_type __n)
> + requires __detail::__concat_is_random_access<_Const, _Vs...>
> + { return auto(__it) += __n; }
> +
> + friend constexpr iterator
> + operator+(difference_type __n, const iterator& __it)
> + requires __detail::__concat_is_random_access<_Const, _Vs...>
> + { return __it + __n; }
> +
> + friend constexpr iterator
> + operator-(const iterator& __it, difference_type __n)
> + requires __detail::__concat_is_random_access<_Const, _Vs...>
> + { return auto(__it) -= __n; }
> +
> + friend constexpr difference_type
> + operator-(const iterator& __x, const iterator& __y)
> + requires __detail::__concat_is_random_access<_Const, _Vs...>
> + {
> + return _S_invoke_with_runtime_index([&]<size_t _Ix>() -> difference_type {
> + return _S_invoke_with_runtime_index([&]<size_t _Iy>() -> difference_type {
> + if constexpr (_Ix > _Iy)
> + {
> + auto __dy = ranges::distance(std::get<_Iy>(__y._M_it),
> + ranges::end(std::get<_Iy>(__y._M_parent
> + ->_M_views)));
> + auto __dx = ranges::distance(ranges::begin(std::get<_Ix>(__x._M_parent
> + ->_M_views)),
> + std::get<_Ix>(__x._M_it));
> + difference_type __sum = 0;
> + [&]<size_t _Idx = _Iy + 1>(this auto&& __self) {
> + if constexpr (_Idx < _Ix)
> + {
> + __sum += ranges::size(std::get<_Idx>(__x._M_parent->_M_views));
> + __self.template operator()<_Idx + 1>();
> + }
> + }();
> + return __dy + __sum + __dx;
> + }
> + else if constexpr (_Ix < _Iy)
> + return -(__y - __x);
> + else
> + return std::get<_Ix>(__x._M_it) - std::get<_Iy>(__y._M_it);
> + }, __y._M_it.index());
> + }, __x._M_it.index());
> + }
> +
> + friend constexpr difference_type
> + operator-(const iterator& __x, default_sentinel_t)
> + requires __detail::__concat_is_random_access<_Const, _Vs...>
> + && __detail::__last_is_common<__maybe_const_t<_Const, _Vs>...>::value
> + {
> + return _S_invoke_with_runtime_index([&]<size_t _Ix>() -> difference_type {
> + auto __dx = ranges::distance(std::get<_Ix>(__x._M_it),
> + ranges::end(std::get<_Ix>(__x._M_parent->_M_views)));
> + difference_type __sum = 0;
> + [&]<size_t _Idx = _Ix + 1>(this auto&& __self) {
> + if constexpr (_Idx < sizeof...(_Vs))
> + {
> + __sum += ranges::size(std::get<_Idx>(__x._M_parent->_M_views));
> + __self.template operator()<_Idx + 1>();
> + }
> + }();
> + return -(__dx + __sum);
> + }, __x._M_it.index());
> + }
> +
> + friend constexpr difference_type
> + operator-(default_sentinel_t, const iterator& __x)
> + requires __detail::__concat_is_random_access<_Const, _Vs...>
> + && __detail::__last_is_common<__maybe_const_t<_Const, _Vs>...>::value
> + { return -(__x - default_sentinel); }
> +
> + friend constexpr decltype(auto)
> + iter_move(const iterator& __it)
> + {
> + using _Res = __detail::__concat_rvalue_reference_t<__maybe_const_t<_Const, _Vs>...>;
> + return std::visit([](const auto& __i) -> _Res {
> + return ranges::iter_move(__i);
> + }, __it._M_it);
> + }
> +
> + friend constexpr void
> + iter_swap(const iterator& __x, const iterator& __y)
> + requires swappable_with<iter_reference_t<iterator>,
> + iter_reference_t<iterator>>
> + && (... && indirectly_swappable<iterator_t<__maybe_const_t<_Const, _Vs>>>)
> + {
> + std::visit([&](const auto& __it1, const auto& __it2) {
> + if constexpr (is_same_v<decltype(__it1), decltype(__it2)>)
> + ranges::iter_swap(__it1, __it2);
> + else
> + ranges::swap(*__it1, *__it2);
> + }, __x._M_it, __y._M_it);
> + }
> + };
> +
> + namespace views
> + {
> + namespace __detail
> + {
> + template<typename... _Ts>
> + concept __can_concat_view = requires { concat_view(std::declval<_Ts>()...); };
> + }
> +
> + struct _Concat
> + {
> + template<typename... _Ts>
> + requires __detail::__can_concat_view<_Ts...>
> + constexpr auto
> + operator() [[nodiscard]] (_Ts&&... __ts) const
> + {
> + if constexpr (sizeof...(_Ts) == 1)
> + return views::all(std::forward<_Ts>(__ts)...);
> + else
> + return concat_view(std::forward<_Ts>(__ts)...);
> + }
> + };
> +
> + inline constexpr _Concat concat;
> + }
> +
> +} // namespace ranges
> +#endif // __cpp_lib_ranges_concat
> +
> _GLIBCXX_END_NAMESPACE_VERSION
> } // namespace std
> #endif // library concepts
> diff --git a/libstdc++-v3/testsuite/std/ranges/concat/1.cc b/libstdc++-v3/testsuite/std/ranges/concat/1.cc
> new file mode 100644
> index 00000000000..91c8893cdc5
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/std/ranges/concat/1.cc
> @@ -0,0 +1,61 @@
> +// { dg-do run { target c++26 } }
> +// { dg-add-options no_pch }
> +
> +#include <ranges>
> +
> +#if __cpp_lib_ranges_concat != 202403L
> +# error "Feature-test macro __cpp_lib_ranges_concat has wrong value in <ranges>"
> +#endif
> +
> +#include <algorithm>
> +#include <vector>
> +#include <array>
> +#include <utility>
> +#include <testsuite_hooks.h>
> +#include <testsuite_iterators.h>
> +
> +namespace ranges = std::ranges;
> +namespace views = std::views;
> +
> +constexpr bool
> +test01()
> +{
> + std::vector<int> v1{1, 2, 3}, v2{4, 5}, v3{};
> + std::array a{6, 7, 8};
> + auto s = views::single(9);
> +
> + auto v = views::concat(v1, v2, v3, a, s);
> + VERIFY( ranges::size(v) == 9 );
> + VERIFY( ranges::equal(v, views::iota(1, 10)) );
> + VERIFY( ranges::equal(v, views::iota(1, 10)) );
> + VERIFY( ranges::equal(v | views::reverse,
> + views::iota(1, 10) | views::reverse) );
> +
> + auto it0 = v.begin();
> + auto cit = std::as_const(v).begin();
> + VERIFY( it0 == it0 );
> + VERIFY( cit == cit );
> + VERIFY( it0 == cit );
> + VERIFY( it0 - it0 == 0);
> + for (int i = 0; i < 10; i++)
> + {
> + VERIFY( it0 + i - it0 == i);
> + VERIFY( it0 + i - i + i == it0 + i );
> + VERIFY( it0 + i - (it0 + i) == 0 );
> + }
> + VERIFY( std::default_sentinel - it0 == 9 );
> + VERIFY( it0 + 9 == std::default_sentinel );
> +
> + auto it5 = it0+5;
> + ranges::iter_swap(it0, it5);
> + VERIFY( *it0 == 6 && *it5 == 1 );
> + ranges::iter_swap(it0, it5);
> + *it0 = ranges::iter_move(it0);
> + return true;
> +}
> +
> +int
> +main()
> +{
> + static_assert(test01());
> +}
> --
> 2.45.0.rc0
>
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2024-04-29 9:11 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-04-22 21:42 [PATCH] libstdc++: Implement ranges::concat_view from P2542R7 Patrick Palka
2024-04-29 9:11 ` Jonathan Wakely
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).