public inbox for libstdc++@gcc.gnu.org
 help / color / mirror / Atom feed
* [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).