public inbox for libstdc++@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views
@ 2021-06-17 15:22 Patrick Palka
  2021-06-17 15:22 ` [PATCH 2/5] libstdc++: Move ranges algos used by <ranges> into ranges_util.h Patrick Palka
                   ` (4 more replies)
  0 siblings, 5 replies; 11+ messages in thread
From: Patrick Palka @ 2021-06-17 15:22 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Patrick Palka

This implements the wording changes of P2325R3 "Views should not be
required to be default constructible".  Changes are relatively
straightforward, besides perhaps those to __box (which now stands
for copyable-box instead of semiregular-box) and __non_propagating_cache.

For __box, this patch implements the recommended practice to also avoid
std::optional when the boxed type is nothrow_move/copy_constructible.

For __non_propagating_cache, now that it's used by split_view::_M_current,
we need to add assignment from a value of the underlying type to the
subset of the std::optional API implemented for the cache (needed by
split_view::begin()).  Hence the new __non_propagating_cache::operator=
overload.

While we're changing __box, this fixes the undesirable list-init in
the constuctors of the partial specialization as reported in PR100475
comment #7.

libstdc++-v3/ChangeLog:

	* include/bits/iterator_concepts.h (weakly_incrementable): Remove
	default_initializable requirement.
	* include/bits/ranges_base.h (ranges::view): Likewise.
	* include/bits/ranges_util.h (subrange): Constrain the default
	ctor.
	* include/bits/stl_iterator.h (back_insert_iterator): Remove the
	default ctor.
	(front_insert_iterator): Likewise.
	(insert_iterator): Likewise.  Remove NSDMIs.
	(common_iterator): Constrain the default ctor.
	(counted_iterator): Likewise.
	* include/bits/stream_iterator.h (ostream_iterator): Remove the
	default ctor.
	* include/std/ranges (__detail::__box::operator=): Handle
	self-assignment in the primary template.
	(__detail::__box): In the partial specialization: adjust
	constraints as per P2325.  Add specialized operator= for the
	case when the wrapped type is not copyable.  Constrain the
	default ctor.  Avoid list-initialization.
	(single_view): Constraint the default ctor.
	(iota_view): Relax semiregular constraint to copyable.
	Constrain the default ctor.
	(iota_view::_Iterator): Constraint the default ctor.
	(basic_istream_view): Remove the default ctor.  Remove NSDMIs.
	Remove redundant checks for empty _M_stream.
	(basic_istream_view::_Iterator): Likewise.
	(ref_view): Remove the default ctor.  Remove NSDMIs.
	(ref_view::_Iterator): Constrain the default ctor.
	(__detail::__non_propagating_cache::operator=): Define overload
	for assigning from a value of the underlying type.
	(filter_view): Likewise.
	(filter_view::_Iterator): Likewise.
	(transform_view): Likewise.
	(transform_view::_Iterator): Likewise.
	(take_view): Likewise.
	(take_view::_Iterator): Likewise.
	(take_while_view): Likewise.
	(take_while_view::_Iterator): Likewise.
	(drop_while_view): Likewise.
	(drop_while_view::_Iterator): Likewise.
	(join_view): Likewise.
	(split_view::_OuterIter::__current): Adjust after changing the
	type of _M_current.
	(split_view::_M_current): Wrap it in a __non_propagating_cache.
	(split_view::split_view): Constrain the default ctor.
	(common_view): Constrain the default ctor.
	(reverse_view): Likewise.
	(elements_view): Likewise.
	* include/std/span (enable_view<span<_ElementType, _Extent>>):
	Define this partial specialization to true unconditionally.
	* include/std/version (__cpp_lib_ranges): Adjust value.
	* testsuite/24_iterators/back_insert_iterator/constexpr.cc:
	Don't attempt to default construct a back_insert_iterator.
	* testsuite/24_iterators/front_insert_iterator/constexpr.cc:
	Don't attempt to default construct a front_insert_iterator.
	* testsuite/24_iterators/insert_iterator/constexpr.cc:
	Don't attempt to default construct an insert_iterator.
	* testsuite/24_iterators/ostream_iterator/requirements/constexpr.cc:
	Remove this test for default constructibility of ostream_iterator.
	* testsuite/std/ranges/97600.cc: Don't attempt to default
	construct a basic_istream_view.
	* testsuite/std/ranges/adaptors/detail/semiregular_box.cc:
	Rename to ...
	* testsuite/std/ranges/adaptors/detail/copyable_box.cc: ... this.
	(test02): Adjust now that __box is copyable-box not
	semiregular-box.
	(test03): New test.
	* testsuite/std/ranges/p2325.cc: New test.
	* testsuite/std/ranges/single_view.cc (test06): New test.
	* testsuite/std/ranges/view.cc: Adjust now that view doesn't
	require default_initializable.
---
 libstdc++-v3/include/bits/iterator_concepts.h |   3 +-
 libstdc++-v3/include/bits/ranges_base.h       |   3 +-
 libstdc++-v3/include/bits/ranges_util.h       |   2 +-
 libstdc++-v3/include/bits/stl_iterator.h      |  16 +-
 libstdc++-v3/include/bits/stream_iterator.h   |   5 -
 libstdc++-v3/include/std/ranges               | 160 ++++++++++++------
 libstdc++-v3/include/std/span                 |   3 +-
 libstdc++-v3/include/std/version              |   2 +-
 .../back_insert_iterator/constexpr.cc         |   3 +-
 .../front_insert_iterator/constexpr.cc        |   3 +-
 .../24_iterators/insert_iterator/constexpr.cc |   3 +-
 .../requirements/constexpr.cc                 |  24 ---
 libstdc++-v3/testsuite/std/ranges/97600.cc    |   3 +-
 .../{semiregular_box.cc => copyable_box.cc}   |  51 +++++-
 libstdc++-v3/testsuite/std/ranges/p2325.cc    | 155 +++++++++++++++++
 .../testsuite/std/ranges/single_view.cc       |  15 ++
 libstdc++-v3/testsuite/std/ranges/view.cc     |   2 +-
 17 files changed, 335 insertions(+), 118 deletions(-)
 delete mode 100644 libstdc++-v3/testsuite/24_iterators/ostream_iterator/requirements/constexpr.cc
 rename libstdc++-v3/testsuite/std/ranges/adaptors/detail/{semiregular_box.cc => copyable_box.cc} (70%)
 create mode 100644 libstdc++-v3/testsuite/std/ranges/p2325.cc

diff --git a/libstdc++-v3/include/bits/iterator_concepts.h b/libstdc++-v3/include/bits/iterator_concepts.h
index 11748e5ed7b..c273056c204 100644
--- a/libstdc++-v3/include/bits/iterator_concepts.h
+++ b/libstdc++-v3/include/bits/iterator_concepts.h
@@ -594,8 +594,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   /// Requirements on types that can be incremented with ++.
   template<typename _Iter>
-    concept weakly_incrementable = default_initializable<_Iter>
-      && movable<_Iter>
+    concept weakly_incrementable = movable<_Iter>
       && requires(_Iter __i)
       {
 	typename iter_difference_t<_Iter>;
diff --git a/libstdc++-v3/include/bits/ranges_base.h b/libstdc++-v3/include/bits/ranges_base.h
index 25af4b742a6..9d749c8d9b7 100644
--- a/libstdc++-v3/include/bits/ranges_base.h
+++ b/libstdc++-v3/include/bits/ranges_base.h
@@ -619,8 +619,7 @@ namespace ranges
   /// [range.view] The ranges::view concept.
   template<typename _Tp>
     concept view
-      = range<_Tp> && movable<_Tp> && default_initializable<_Tp>
-	&& enable_view<_Tp>;
+      = range<_Tp> && movable<_Tp> && enable_view<_Tp>;
 
   // [range.refinements]
 
diff --git a/libstdc++-v3/include/bits/ranges_util.h b/libstdc++-v3/include/bits/ranges_util.h
index dd829ed957f..d7b12b3d985 100644
--- a/libstdc++-v3/include/bits/ranges_util.h
+++ b/libstdc++-v3/include/bits/ranges_util.h
@@ -241,7 +241,7 @@ namespace ranges
       [[no_unique_address]] _Size<__size_type> _M_size = {};
 
     public:
-      subrange() = default;
+      subrange() requires default_initializable<_It> = default;
 
       constexpr
       subrange(__detail::__convertible_to_non_slicing<_It> auto __i, _Sent __s)
diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h
index 8768624b7d1..6ec046b597b 100644
--- a/libstdc++-v3/include/bits/stl_iterator.h
+++ b/libstdc++-v3/include/bits/stl_iterator.h
@@ -639,8 +639,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       typedef _Container          container_type;
 #if __cplusplus > 201703L
       using difference_type = ptrdiff_t;
-
-      constexpr back_insert_iterator() noexcept : container(nullptr) { }
 #endif
 
       /// The only way to create this %iterator is with a container.
@@ -742,8 +740,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       typedef _Container          container_type;
 #if __cplusplus > 201703L
       using difference_type = ptrdiff_t;
-
-      constexpr front_insert_iterator() noexcept : container(nullptr) { }
 #endif
 
       /// The only way to create this %iterator is with a container.
@@ -843,17 +839,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     {
 #if __cplusplus > 201703L && defined __cpp_lib_concepts
       using _Iter = std::__detail::__range_iter_t<_Container>;
-
-    protected:
-      _Container* container = nullptr;
-      _Iter iter = _Iter();
 #else
       typedef typename _Container::iterator		_Iter;
-
+#endif
     protected:
       _Container* container;
       _Iter iter;
-#endif
 
     public:
       /// A nested typedef for the type of whatever container you used.
@@ -861,8 +852,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
 #if __cplusplus > 201703L && defined __cpp_lib_concepts
       using difference_type = ptrdiff_t;
-
-      insert_iterator() = default;
 #endif
 
       /**
@@ -1740,6 +1729,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     constexpr
     common_iterator()
     noexcept(is_nothrow_default_constructible_v<_It>)
+    requires default_initializable<_It>
     : _M_it(), _M_index(0)
     { }
 
@@ -2117,7 +2107,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // iterator_concept defined in __counted_iter_concept
       // iterator_category defined in __counted_iter_cat
 
-      constexpr counted_iterator() = default;
+      constexpr counted_iterator() requires default_initializable<_It> = default;
 
       constexpr
       counted_iterator(_It __i, iter_difference_t<_It> __n)
diff --git a/libstdc++-v3/include/bits/stream_iterator.h b/libstdc++-v3/include/bits/stream_iterator.h
index fd8920b8d01..d07474d4996 100644
--- a/libstdc++-v3/include/bits/stream_iterator.h
+++ b/libstdc++-v3/include/bits/stream_iterator.h
@@ -192,11 +192,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       const _CharT*	_M_string;
 
     public:
-#if __cplusplus > 201703L
-      constexpr ostream_iterator() noexcept
-      : _M_stream(nullptr), _M_string(nullptr) { }
-#endif
-
       /// Construct from an ostream.
       ostream_iterator(ostream_type& __s)
       : _M_stream(std::__addressof(__s)), _M_string(0) {}
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index 73c35dafd29..f96adf63d10 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -113,10 +113,13 @@ namespace ranges
 	noexcept(is_nothrow_copy_constructible_v<_Tp>)
 	requires (!copyable<_Tp>)
 	{
-	  if ((bool)__that)
-	    this->emplace(*__that);
-	  else
-	    this->reset();
+	  if (this != std::__addressof(__that))
+	    {
+	      if ((bool)__that)
+		this->emplace(*__that);
+	      else
+		this->reset();
+	    }
 	  return *this;
 	}
 
@@ -125,37 +128,42 @@ namespace ranges
 	noexcept(is_nothrow_move_constructible_v<_Tp>)
 	requires (!movable<_Tp>)
 	{
-	  if ((bool)__that)
-	    this->emplace(std::move(*__that));
-	  else
-	    this->reset();
+	  if (this != std::__addressof(__that))
+	    {
+	      if ((bool)__that)
+		this->emplace(std::move(*__that));
+	      else
+		this->reset();
+	    }
 	  return *this;
 	}
       };
 
-    // For types which are already semiregular, this specialization of the
-    // semiregular wrapper stores the object directly without going through
+    // For types which are already copyable, this specialization of the
+    // copyable wrapper stores the object directly without going through
     // std::optional.  It provides just the subset of the primary template's
     // API that we currently use.
-    template<__boxable _Tp> requires semiregular<_Tp>
+    template<__boxable _Tp>
+      requires copyable<_Tp> || (is_nothrow_move_constructible_v<_Tp>
+				 && is_nothrow_copy_constructible_v<_Tp>)
       struct __box<_Tp>
       {
       private:
 	[[no_unique_address]] _Tp _M_value = _Tp();
 
       public:
-	__box() = default;
+	__box() requires default_initializable<_Tp> = default;
 
 	constexpr explicit
 	__box(const _Tp& __t)
 	noexcept(is_nothrow_copy_constructible_v<_Tp>)
-	: _M_value{__t}
+	: _M_value(__t)
 	{ }
 
 	constexpr explicit
 	__box(_Tp&& __t)
 	noexcept(is_nothrow_move_constructible_v<_Tp>)
-	: _M_value{std::move(__t)}
+	: _M_value(std::move(__t))
 	{ }
 
 	template<typename... _Args>
@@ -166,6 +174,38 @@ namespace ranges
 	  : _M_value(std::forward<_Args>(__args)...)
 	  { }
 
+	__box(const __box&) = default;
+	__box(__box&&) = default;
+	__box& operator=(const __box&) requires copyable<_Tp> = default;
+	__box& operator=(__box&&) requires copyable<_Tp> = default;
+
+	// When _Tp is nothrow_copy_constructible but not copy_assignable,
+	// copy assignment is implemented via destroy-then-copy-construct.
+	constexpr __box&
+	operator=(const __box& __that) noexcept
+	{
+	  static_assert(is_nothrow_copy_constructible_v<_Tp>);
+	  if (this != std::__addressof(__that))
+	    {
+	      _M_value.~_Tp();
+	      std::construct_at(std::__addressof(_M_value), *__that);
+	    }
+	  return *this;
+	}
+
+	// Likewise for move assignment.
+	constexpr __box&
+	operator=(__box&& __that) noexcept
+	{
+	  static_assert(is_nothrow_move_constructible_v<_Tp>);
+	  if (this != std::__addressof(__that))
+	    {
+	      _M_value.~_Tp();
+	      std::construct_at(std::__addressof(_M_value), std::move(*__that));
+	    }
+	  return *this;
+	}
+
 	constexpr bool
 	has_value() const noexcept
 	{ return true; };
@@ -193,7 +233,7 @@ namespace ranges
     class single_view : public view_interface<single_view<_Tp>>
     {
     public:
-      single_view() = default;
+      single_view() requires default_initializable<_Tp> = default;
 
       constexpr explicit
       single_view(const _Tp& __t)
@@ -308,7 +348,7 @@ namespace ranges
   template<weakly_incrementable _Winc,
 	   semiregular _Bound = unreachable_sentinel_t>
     requires std::__detail::__weakly_eq_cmp_with<_Winc, _Bound>
-      && semiregular<_Winc>
+      && copyable<_Winc>
     class iota_view : public view_interface<iota_view<_Winc, _Bound>>
     {
     private:
@@ -337,7 +377,7 @@ namespace ranges
 	using value_type = _Winc;
 	using difference_type = __detail::__iota_diff_t<_Winc>;
 
-	_Iterator() = default;
+	_Iterator() requires default_initializable<_Winc> = default;
 
 	constexpr explicit
 	_Iterator(_Winc __value)
@@ -534,7 +574,7 @@ namespace ranges
       [[no_unique_address]] _Bound _M_bound = _Bound();
 
     public:
-      iota_view() = default;
+      iota_view() requires default_initializable<_Winc> = default;
 
       constexpr explicit
       iota_view(_Winc __value)
@@ -643,8 +683,6 @@ namespace views
     : public view_interface<basic_istream_view<_Val, _CharT, _Traits>>
     {
     public:
-      basic_istream_view() = default;
-
       constexpr explicit
       basic_istream_view(basic_istream<_CharT, _Traits>& __stream)
 	: _M_stream(std::__addressof(__stream))
@@ -653,8 +691,7 @@ namespace views
       constexpr auto
       begin()
       {
-	if (_M_stream != nullptr)
-	  *_M_stream >> _M_object;
+	*_M_stream >> _M_object;
 	return _Iterator{this};
       }
 
@@ -663,8 +700,8 @@ namespace views
       { return default_sentinel; }
 
     private:
-      basic_istream<_CharT, _Traits>* _M_stream = nullptr;
-      _Val _M_object = _Val();
+      basic_istream<_CharT, _Traits>* _M_stream;
+      _Val _M_object;
 
       struct _Iterator
       {
@@ -673,8 +710,6 @@ namespace views
 	using difference_type = ptrdiff_t;
 	using value_type = _Val;
 
-	_Iterator() = default;
-
 	constexpr explicit
 	_Iterator(basic_istream_view* __parent) noexcept
 	  : _M_parent(__parent)
@@ -688,7 +723,6 @@ namespace views
 	_Iterator&
 	operator++()
 	{
-	  __glibcxx_assert(_M_parent->_M_stream != nullptr);
 	  *_M_parent->_M_stream >> _M_parent->_M_object;
 	  return *this;
 	}
@@ -699,21 +733,18 @@ namespace views
 
 	_Val&
 	operator*() const
-	{
-	  __glibcxx_assert(_M_parent->_M_stream != nullptr);
-	  return _M_parent->_M_object;
-	}
+	{ return _M_parent->_M_object; }
 
 	friend bool
 	operator==(const _Iterator& __x, default_sentinel_t)
 	{ return __x._M_at_end(); }
 
       private:
-	basic_istream_view* _M_parent = nullptr;
+	basic_istream_view* _M_parent;
 
 	bool
 	_M_at_end() const
-	{ return _M_parent == nullptr || !*_M_parent->_M_stream; }
+	{ return !*_M_parent->_M_stream; }
       };
 
       friend _Iterator;
@@ -1017,15 +1048,12 @@ namespace views::__adaptor
     class ref_view : public view_interface<ref_view<_Range>>
     {
     private:
-      _Range* _M_r = nullptr;
+      _Range* _M_r;
 
       static void _S_fun(_Range&); // not defined
       static void _S_fun(_Range&&) = delete;
 
     public:
-      constexpr
-      ref_view() noexcept = default;
-
       template<__detail::__not_same_as<ref_view> _Tp>
 	requires convertible_to<_Tp, _Range&>
 	  && requires { _S_fun(declval<_Tp>()); }
@@ -1205,6 +1233,16 @@ namespace views::__adaptor
 	  return *this;
 	}
 
+	constexpr __non_propagating_cache&
+	operator=(_Tp __val)
+	{
+	  this->_M_reset();
+	  std::construct_at(std::__addressof(this->_M_payload._M_payload),
+			    std::in_place, std::move(__val));
+	  this->_M_payload._M_engaged = true;
+	  return *this;
+	}
+
 	constexpr _Tp&
 	operator*() noexcept
 	{ return this->_M_get(); }
@@ -1382,7 +1420,7 @@ namespace views::__adaptor
 	using value_type = range_value_t<_Vp>;
 	using difference_type = range_difference_t<_Vp>;
 
-	_Iterator() = default;
+	_Iterator() requires default_initializable<_Vp_iter> = default;
 
 	constexpr
 	_Iterator(filter_view* __parent, _Vp_iter __current)
@@ -1494,7 +1532,9 @@ namespace views::__adaptor
       _Vp _M_base = _Vp();
 
     public:
-      filter_view() = default;
+      filter_view() requires (default_initializable<_Vp>
+			      && default_initializable<_Pred>)
+	= default;
 
       constexpr
       filter_view(_Vp __base, _Pred __pred)
@@ -1643,7 +1683,7 @@ namespace views::__adaptor
 	    = remove_cvref_t<invoke_result_t<_Fp&, range_reference_t<_Base>>>;
 	  using difference_type = range_difference_t<_Base>;
 
-	  _Iterator() = default;
+	  _Iterator() requires default_initializable<_Base_iter> = default;
 
 	  constexpr
 	  _Iterator(_Parent* __parent, _Base_iter __current)
@@ -1858,7 +1898,9 @@ namespace views::__adaptor
       _Vp _M_base = _Vp();
 
     public:
-      transform_view() = default;
+      transform_view() requires (default_initializable<_Vp>
+				 && default_initializable<_Fp>)
+	= default;
 
       constexpr
       transform_view(_Vp __base, _Fp __fun)
@@ -1993,7 +2035,7 @@ namespace views::__adaptor
       _Vp _M_base = _Vp();
 
     public:
-      take_view() = default;
+      take_view() requires default_initializable<_Vp> = default;
 
       constexpr
       take_view(_Vp base, range_difference_t<_Vp> __count)
@@ -2177,7 +2219,9 @@ namespace views::__adaptor
       _Vp _M_base = _Vp();
 
     public:
-      take_while_view() = default;
+      take_while_view() requires (default_initializable<_Vp>
+				  && default_initializable<_Pred>)
+	= default;
 
       constexpr
       take_while_view(_Vp base, _Pred __pred)
@@ -2265,7 +2309,7 @@ namespace views::__adaptor
 				      _M_cached_begin;
 
     public:
-      drop_view() = default;
+      drop_view() requires default_initializable<_Vp> = default;
 
       constexpr
       drop_view(_Vp __base, range_difference_t<_Vp> __count)
@@ -2381,7 +2425,9 @@ namespace views::__adaptor
       _Vp _M_base = _Vp();
 
     public:
-      drop_while_view() = default;
+      drop_while_view() requires (default_initializable<_Vp>
+				  && default_initializable<_Pred>)
+	= default;
 
       constexpr
       drop_while_view(_Vp __base, _Pred __pred)
@@ -2571,7 +2617,9 @@ namespace views::__adaptor
 	    = common_type_t<range_difference_t<_Base>,
 			    range_difference_t<range_reference_t<_Base>>>;
 
-	  _Iterator() = default;
+	  _Iterator() requires (default_initializable<_Outer_iter>
+				&& default_initializable<_Inner_iter>)
+	    = default;
 
 	  constexpr
 	  _Iterator(_Parent* __parent, _Outer_iter __outer)
@@ -2724,7 +2772,7 @@ namespace views::__adaptor
       _Vp _M_base = _Vp();
 
     public:
-      join_view() = default;
+      join_view() requires default_initializable<_Vp> = default;
 
       constexpr explicit
       join_view(_Vp __base)
@@ -2891,7 +2939,7 @@ namespace views::__adaptor
 	    if constexpr (forward_range<_Vp>)
 	      return _M_current;
 	    else
-	      return _M_parent->_M_current;
+	      return *_M_parent->_M_current;
 	  }
 
 	  constexpr auto&
@@ -2900,7 +2948,7 @@ namespace views::__adaptor
 	    if constexpr (forward_range<_Vp>)
 	      return _M_current;
 	    else
-	      return _M_parent->_M_current;
+	      return *_M_parent->_M_current;
 	  }
 
 	  _Parent* _M_parent = nullptr;
@@ -3146,12 +3194,14 @@ namespace views::__adaptor
       // XXX: _M_current is "present only if !forward_range<V>"
       [[no_unique_address]]
 	__detail::__maybe_present_t<!forward_range<_Vp>,
-				    iterator_t<_Vp>> _M_current;
+	  __detail::__non_propagating_cache<iterator_t<_Vp>>> _M_current;
       _Vp _M_base = _Vp();
 
 
     public:
-      split_view() = default;
+      split_view() requires (default_initializable<_Vp>
+			     && default_initializable<_Pattern>)
+	= default;
 
       constexpr
       split_view(_Vp __base, _Pattern __pattern)
@@ -3282,7 +3332,7 @@ namespace views::__adaptor
       _Vp _M_base = _Vp();
 
     public:
-      common_view() = default;
+      common_view() requires default_initializable<_Vp> = default;
 
       constexpr explicit
       common_view(_Vp __r)
@@ -3413,7 +3463,7 @@ namespace views::__adaptor
       _Vp _M_base = _Vp();
 
     public:
-      reverse_view() = default;
+      reverse_view() requires default_initializable<_Vp> = default;
 
       constexpr explicit
       reverse_view(_Vp __r)
@@ -3555,7 +3605,7 @@ namespace views::__adaptor
     class elements_view : public view_interface<elements_view<_Vp, _Nm>>
     {
     public:
-      elements_view() = default;
+      elements_view() requires default_initializable<_Vp> = default;
 
       constexpr explicit
       elements_view(_Vp base)
@@ -3676,7 +3726,7 @@ namespace views::__adaptor
 	    = remove_cvref_t<tuple_element_t<_Nm, range_value_t<_Base>>>;
 	  using difference_type = range_difference_t<_Base>;
 
-	  _Iterator() = default;
+	  _Iterator() requires default_initializable<iterator_t<_Base>> = default;
 
 	  constexpr explicit
 	  _Iterator(iterator_t<_Base> current)
diff --git a/libstdc++-v3/include/std/span b/libstdc++-v3/include/std/span
index 09bdcd69afb..63f0a8f6279 100644
--- a/libstdc++-v3/include/std/span
+++ b/libstdc++-v3/include/std/span
@@ -447,8 +447,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     // Opt-in to view concept
     template<typename _ElementType, size_t _Extent>
       inline constexpr bool
-	enable_view<span<_ElementType, _Extent>>
-	  = _Extent == 0 || _Extent == dynamic_extent;
+	enable_view<span<_ElementType, _Extent>> = true;
   }
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index 8d0b2b95f34..e3ab9b4c7c0 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -240,7 +240,7 @@
 #define __cpp_lib_math_constants 201907L
 #define __cpp_lib_polymorphic_allocator 201902L
 #if __cpp_lib_concepts
-# define __cpp_lib_ranges 201911L
+# define __cpp_lib_ranges 202106L
 #endif
 #if __cpp_lib_atomic_wait || _GLIBCXX_HAVE_POSIX_SEMAPHORE
 # define __cpp_lib_semaphore 201907L
diff --git a/libstdc++-v3/testsuite/24_iterators/back_insert_iterator/constexpr.cc b/libstdc++-v3/testsuite/24_iterators/back_insert_iterator/constexpr.cc
index bef2289ba79..27acd071df1 100644
--- a/libstdc++-v3/testsuite/24_iterators/back_insert_iterator/constexpr.cc
+++ b/libstdc++-v3/testsuite/24_iterators/back_insert_iterator/constexpr.cc
@@ -42,8 +42,7 @@ constexpr bool
 test01()
 {
   container c;
-  std::back_insert_iterator<container> iter;
-  iter = std::back_inserter(c);
+  std::back_insert_iterator<container> iter = std::back_inserter(c);
   *iter++ = 1;
   int i = 2;
   *iter = i;
diff --git a/libstdc++-v3/testsuite/24_iterators/front_insert_iterator/constexpr.cc b/libstdc++-v3/testsuite/24_iterators/front_insert_iterator/constexpr.cc
index 7b4c990b107..cff7f6a4524 100644
--- a/libstdc++-v3/testsuite/24_iterators/front_insert_iterator/constexpr.cc
+++ b/libstdc++-v3/testsuite/24_iterators/front_insert_iterator/constexpr.cc
@@ -42,8 +42,7 @@ constexpr bool
 test01()
 {
   container c;
-  std::front_insert_iterator<container> iter;
-  iter = std::front_inserter(c);
+  std::front_insert_iterator<container> iter = std::front_inserter(c);
   *iter++ = 1;
   int i = 2;
   *iter = i;
diff --git a/libstdc++-v3/testsuite/24_iterators/insert_iterator/constexpr.cc b/libstdc++-v3/testsuite/24_iterators/insert_iterator/constexpr.cc
index e74df3eb5d5..e326b01d534 100644
--- a/libstdc++-v3/testsuite/24_iterators/insert_iterator/constexpr.cc
+++ b/libstdc++-v3/testsuite/24_iterators/insert_iterator/constexpr.cc
@@ -51,8 +51,7 @@ constexpr bool
 test01()
 {
   container c;
-  std::insert_iterator<container> iter;
-  iter = std::inserter(c, c.begin());
+  std::insert_iterator<container> iter = std::inserter(c, c.begin());
   *iter++ = 1;
   int i = 2;
   *iter = i;
diff --git a/libstdc++-v3/testsuite/24_iterators/ostream_iterator/requirements/constexpr.cc b/libstdc++-v3/testsuite/24_iterators/ostream_iterator/requirements/constexpr.cc
deleted file mode 100644
index 4edaaa8aebb..00000000000
--- a/libstdc++-v3/testsuite/24_iterators/ostream_iterator/requirements/constexpr.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (C) 2019-2021 Free Software Foundation, Inc.
-//
-// This file is part of the GNU ISO C++ Library.  This library is free
-// software; you can redistribute it and/or modify it under the
-// terms of the GNU General Public License as published by the
-// Free Software Foundation; either version 3, or (at your option)
-// any later version.
-
-// This library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License along
-// with this library; see the file COPYING3.  If not see
-// <http://www.gnu.org/licenses/>.
-
-// { dg-options "-std=gnu++2a" }
-// { dg-do compile { target c++2a } }
-
-#include <iterator>
-
-constexpr std::ostream_iterator<int> iter1;
-constexpr std::ostream_iterator<int> iter2{};
diff --git a/libstdc++-v3/testsuite/std/ranges/97600.cc b/libstdc++-v3/testsuite/std/ranges/97600.cc
index 7435de022cd..c642b9d22d0 100644
--- a/libstdc++-v3/testsuite/std/ranges/97600.cc
+++ b/libstdc++-v3/testsuite/std/ranges/97600.cc
@@ -24,9 +24,8 @@
 #include <ranges>
 
 void
-test01()
+test01(std::ranges::basic_istream_view<int, char, std::char_traits<char>> v)
 {
-  std::ranges::basic_istream_view<int, char, std::char_traits<char>> v;
   v.begin();
   static_assert(std::ranges::range<decltype(v)>);
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/detail/semiregular_box.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/detail/copyable_box.cc
similarity index 70%
rename from libstdc++-v3/testsuite/std/ranges/adaptors/detail/semiregular_box.cc
rename to libstdc++-v3/testsuite/std/ranges/adaptors/detail/copyable_box.cc
index ed694e04fd1..fa6d4d56816 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/detail/semiregular_box.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/detail/copyable_box.cc
@@ -82,9 +82,10 @@ test01()
 }
 static_assert(test01());
 
-template<bool make_semiregular>
+template<bool make_copyable>
   struct A {
-    A() requires make_semiregular;
+    A(const A&) = default;
+    A& operator=(const A&) requires make_copyable;
     A(int, int);
     A(std::initializer_list<int>) = delete;
   };
@@ -93,9 +94,51 @@ void
 test02()
 {
   // PR libstdc++/100475
-  static_assert(std::semiregular<A<true>>);
+  static_assert(std::copyable<A<true>>);
   __box<A<true>> x2(std::in_place, 0, 0);
 
-  static_assert(!std::semiregular<A<false>>);
+  static_assert(!std::copyable<A<false>>);
   __box<A<false>> x1(std::in_place, 0, 0);
 }
+
+constexpr bool
+test03()
+{
+  // Verify correctness of the non-defaulted operator= for the partial
+  // specialization of __box.
+  struct B {
+    constexpr B(int* p) : p(p) { }
+    constexpr ~B() { ++*p; };
+    B(const B&) = default;
+    B& operator=(const B&) = delete;
+    int* p;
+  };
+  static_assert(!std::copyable<B>);
+  static_assert(std::is_nothrow_copy_constructible_v<B>);
+  static_assert(sizeof(__box<B>) == sizeof(B));
+
+  int m = 0;
+  __box<B> x(std::in_place, &m);
+  __glibcxx_assert(m == 0);
+  x = x;
+  __glibcxx_assert(m == 0);
+  x = std::move(x);
+  __glibcxx_assert(m == 0);
+
+  int n = 0;
+  __box<B> y(std::in_place, &n);
+  auto z = x;
+  x = y;
+  __glibcxx_assert(m == 1);
+  __glibcxx_assert(n == 0);
+  __glibcxx_assert(x->p == &n);
+  __glibcxx_assert(y->p == &n);
+  y = std::move(z);
+  __glibcxx_assert(m == 1);
+  __glibcxx_assert(n == 1);
+  __glibcxx_assert(y->p == &m);
+  __glibcxx_assert(z->p == &m);
+
+  return true;
+}
+static_assert(test03());
diff --git a/libstdc++-v3/testsuite/std/ranges/p2325.cc b/libstdc++-v3/testsuite/std/ranges/p2325.cc
new file mode 100644
index 00000000000..df6cde29e4d
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/p2325.cc
@@ -0,0 +1,155 @@
+// { dg-options "-std=gnu++20" }
+// { dg-do compile { target c++20 } }
+// P2325R3 "Views should not be required to be default constructible"
+
+#include <ranges>
+#include <iterator>
+#include <span>
+#include <sstream>
+#include <vector>
+#include <testsuite_iterators.h>
+
+using namespace std;
+
+template<default_initializable T> void f();
+template<typename T> requires weakly_incrementable<T> || ranges::view<T> void f();
+
+void
+test01()
+{
+  // Verify neither std::weakly_incrementable nor ranges::view require
+  // default_initializable.
+  f<int>(); // { dg-error "ambiguous" }
+}
+
+void
+test02()
+{
+  // Verify these iterators are not default constructible.
+  static_assert(!default_initializable<insert_iterator<vector<int>>>);
+  static_assert(!default_initializable<front_insert_iterator<vector<int>>>);
+  static_assert(!default_initializable<back_insert_iterator<vector<int>>>);
+  static_assert(!default_initializable<ostream_iterator<int>>);
+
+  using iter = ostream_iterator<int>;
+
+  // Verify common_iterator is conditionally default constructible.
+  static_assert(!default_initializable<common_iterator<iter, unreachable_sentinel_t>>);
+  static_assert(default_initializable<common_iterator<int*, unreachable_sentinel_t>>);
+
+  // Verify counted_iterator is conditionally default constructible.
+  static_assert(!default_initializable<counted_iterator<iter>>);
+  static_assert(default_initializable<counted_iterator<int*>>);
+}
+
+void
+test03()
+{
+  using iter = ostream_iterator<int>;
+
+  // Verify iota_view is conditionally default constructible.
+  static_assert(!default_initializable<ranges::iota_view<iter>>);
+  static_assert(!default_initializable<decltype(declval<ranges::iota_view<iter>>().begin())>);
+  static_assert(default_initializable<ranges::iota_view<int>>);
+  static_assert(default_initializable<decltype(declval<ranges::iota_view<int>>().begin())>);
+
+  // Verify subrange is conditionally default constructible.
+  static_assert(!default_initializable<ranges::subrange<iter, unreachable_sentinel_t>>);
+  static_assert(default_initializable<ranges::subrange<int*, unreachable_sentinel_t>>);
+
+  // Verify single_view is conditionally default constructible.
+  static_assert(!default_initializable<ranges::single_view<iter>>);
+  static_assert(default_initializable<ranges::single_view<int*>>);
+}
+
+void
+test04()
+{
+  // Verify basic_istream_view is not default constructible.
+  using type = ranges::basic_istream_view<int, char, char_traits<char>>;
+  static_assert(!default_initializable<type>);
+  static_assert(!default_initializable<decltype(declval<type>().begin())>);
+}
+
+void
+test05()
+{
+  // Verify ref_view is not default constructible.
+  static_assert(!default_initializable<ranges::ref_view<int[5]>>);
+}
+
+template<auto adaptor>
+void
+test06()
+{
+  auto f1 = [] (auto) { return true; };
+  auto f2 = [i=0] (auto) { return true; };
+  static_assert(default_initializable<decltype(views::single(0) | adaptor(f1))>);
+  static_assert(!default_initializable<decltype(views::single(0) | adaptor(f2))>);
+
+  struct S { S() = delete; };
+  static_assert(!default_initializable<decltype(views::single(declval<S>()) | adaptor(f1))>);
+  static_assert(!default_initializable<decltype(views::single(declval<S>()) | adaptor(f2))>);
+}
+
+// Verify filter_view, transform_view, take_while_view and drop_while_view are
+// conditionally default constructible.
+template void test06<views::filter>();
+template void test06<views::transform>();
+template void test06<views::take_while>();
+template void test06<views::drop_while>();
+
+void
+test07()
+{
+  // Verify join_view is conditionally default constructible.
+  struct S { S() = delete; };
+  using type1 = ranges::join_view<ranges::single_view<ranges::single_view<S>>>;
+  static_assert(!default_initializable<type1>);
+  using type2 = ranges::join_view<ranges::single_view<ranges::single_view<int>>>;
+  static_assert(default_initializable<type2>);
+}
+
+void
+test08()
+{
+  // Verify split_view is conditionally default constructible.
+  using type1 = ranges::split_view<ranges::ref_view<int[2]>, ranges::single_view<int>>;
+  static_assert(!default_initializable<type1>);
+  using type2 = ranges::split_view<ranges::single_view<int>, ranges::ref_view<int[2]>>;
+  static_assert(!default_initializable<type2>);
+  using type3 = ranges::split_view<ranges::ref_view<int[2]>, ranges::ref_view<int[2]>>;
+  static_assert(!default_initializable<type3>);
+  using type4 = ranges::split_view<ranges::single_view<int>, ranges::single_view<int>>;
+  static_assert(default_initializable<type4>);
+}
+
+void
+test09()
+{
+  // Verify common_view is conditionally default constructible.
+  using type1 = ranges::common_view<ranges::iota_view<ostream_iterator<int>>>;
+  static_assert(!default_initializable<type1>);
+  using type2 = ranges::common_view<ranges::iota_view<int*>>;
+  static_assert(default_initializable<type2>);
+}
+
+void
+test10()
+{
+  // Verify reverse_view is conditionally default constructible.
+  using type1 = ranges::reverse_view<ranges::ref_view<int[2]>>;
+  static_assert(!default_initializable<type1>);
+  using type2 = ranges::reverse_view<ranges::single_view<int>>;
+  static_assert(default_initializable<type2>);
+}
+
+void
+test11()
+{
+  // Verify elements_view is conditionally default constructible.
+  using type1 = ranges::elements_view<ranges::ref_view<pair<int,int>[2]>, 0>;
+  static_assert(!default_initializable<type1>);
+  using type2 = ranges::elements_view<ranges::single_view<pair<int,int>>, 0>;
+  static_assert(default_initializable<type2>);
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/single_view.cc b/libstdc++-v3/testsuite/std/ranges/single_view.cc
index c036fc8976a..f1d8e103715 100644
--- a/libstdc++-v3/testsuite/std/ranges/single_view.cc
+++ b/libstdc++-v3/testsuite/std/ranges/single_view.cc
@@ -96,6 +96,20 @@ test05()
   static_assert(noexcept(cs.empty())); // view_interface::empty()
 }
 
+void
+test06()
+{
+  // PR libstdc++/100475 comment #7
+  struct S {
+    S() = default;
+    S(std::initializer_list<S>) = delete;
+    S(const S&) {}
+  };
+  S obj;
+  auto x = std::views::single(obj);
+  auto y = std::views::single(std::move(obj));
+}
+
 int main()
 {
   test01();
@@ -103,4 +117,5 @@ int main()
   test03();
   test04();
   test05();
+  test06();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/view.cc b/libstdc++-v3/testsuite/std/ranges/view.cc
index d8972ab3e46..dd8258220ed 100644
--- a/libstdc++-v3/testsuite/std/ranges/view.cc
+++ b/libstdc++-v3/testsuite/std/ranges/view.cc
@@ -31,7 +31,7 @@
 
 static_assert(std::ranges::view<std::span<int>>);
 static_assert(std::ranges::view<std::span<int, 0>>);
-static_assert(!std::ranges::view<std::span<int, 1>>);
+static_assert(std::ranges::view<std::span<int, 1>>); // Changed with P2325R3
 static_assert(std::ranges::view<std::string_view>);
 static_assert(std::ranges::view<std::experimental::string_view>);
 
-- 
2.32.0.93.g670b81a890


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 2/5] libstdc++: Move ranges algos used by <ranges> into ranges_util.h
  2021-06-17 15:22 [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Patrick Palka
@ 2021-06-17 15:22 ` Patrick Palka
  2021-06-17 18:14   ` Jonathan Wakely
  2021-06-17 15:22 ` [PATCH 3/5] libstdc++: Rename views::split to views::lazy_split as per P2210 Patrick Palka
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 11+ messages in thread
From: Patrick Palka @ 2021-06-17 15:22 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Patrick Palka

The <ranges> header defines simplified copies of some ranges algorithms
in order to avoid including the entirety of ranges_algo.h.  A subsequent
patch is going to want to use ranges::search in <ranges> as well, but
that algorithm is more complicated compared to the other copied ones.

So rather than additionally copying ranges::search into <ranges>, this
patch splits out all the ranges algos used by <ranges> (including
ranges::search) from ranges_algo.h to ranges_util.h, and deletes the
simplified copies in <ranges>.  This seems like the best place for
these algorithms, as ranges_util.h is included only from <ranges> and
ranges_algo.h.

libstdc++-v3/ChangeLog:

	* include/bits/ranges_algo.h (__find_fn, find, __find_if_fn)
	(find_if, __find_if_not_fn, find_if_not, _in_in_result)
	(__mismatch_fn, mismatch, __search_fn, search): Move to ...
	* include/bits/ranges_util.h: ... here.
	* include/std/ranges (__detail::find, __detail::find_if)
	(__detail::find_if_not, __detail::mismatch): Remove.
	(filter_view): Use ranges::find_if instead.
	(drop_while_view): Use ranges::find_if_not instead.
	(split_view): Use ranges::find and ranges::mismatch instead.
---
 libstdc++-v3/include/bits/ranges_algo.h | 215 +----------------------
 libstdc++-v3/include/bits/ranges_util.h | 219 ++++++++++++++++++++++++
 libstdc++-v3/include/std/ranges         |  72 ++------
 3 files changed, 233 insertions(+), 273 deletions(-)

diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h
index ecf1378742d..9eeebff6525 100644
--- a/libstdc++-v3/include/bits/ranges_algo.h
+++ b/libstdc++-v3/include/bits/ranges_algo.h
@@ -234,91 +234,7 @@ namespace ranges
 
   inline constexpr __for_each_n_fn for_each_n{};
 
-  struct __find_fn
-  {
-    template<input_iterator _Iter, sentinel_for<_Iter> _Sent, typename _Tp,
-	     typename _Proj = identity>
-      requires indirect_binary_predicate<ranges::equal_to,
-					 projected<_Iter, _Proj>, const _Tp*>
-      constexpr _Iter
-      operator()(_Iter __first, _Sent __last,
-		 const _Tp& __value, _Proj __proj = {}) const
-      {
-	while (__first != __last
-	    && !(std::__invoke(__proj, *__first) == __value))
-	  ++__first;
-	return __first;
-      }
-
-    template<input_range _Range, typename _Tp, typename _Proj = identity>
-      requires indirect_binary_predicate<ranges::equal_to,
-					 projected<iterator_t<_Range>, _Proj>,
-					 const _Tp*>
-      constexpr borrowed_iterator_t<_Range>
-      operator()(_Range&& __r, const _Tp& __value, _Proj __proj = {}) const
-      {
-	return (*this)(ranges::begin(__r), ranges::end(__r),
-		       __value, std::move(__proj));
-      }
-  };
-
-  inline constexpr __find_fn find{};
-
-  struct __find_if_fn
-  {
-    template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
-	     typename _Proj = identity,
-	     indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
-      constexpr _Iter
-      operator()(_Iter __first, _Sent __last,
-		 _Pred __pred, _Proj __proj = {}) const
-      {
-	while (__first != __last
-	    && !(bool)std::__invoke(__pred, std::__invoke(__proj, *__first)))
-	  ++__first;
-	return __first;
-      }
-
-    template<input_range _Range, typename _Proj = identity,
-	     indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>>
-	       _Pred>
-      constexpr borrowed_iterator_t<_Range>
-      operator()(_Range&& __r, _Pred __pred, _Proj __proj = {}) const
-      {
-	return (*this)(ranges::begin(__r), ranges::end(__r),
-		       std::move(__pred), std::move(__proj));
-      }
-  };
-
-  inline constexpr __find_if_fn find_if{};
-
-  struct __find_if_not_fn
-  {
-    template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
-	     typename _Proj = identity,
-	     indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
-      constexpr _Iter
-      operator()(_Iter __first, _Sent __last,
-		 _Pred __pred, _Proj __proj = {}) const
-      {
-	while (__first != __last
-	    && (bool)std::__invoke(__pred, std::__invoke(__proj, *__first)))
-	  ++__first;
-	return __first;
-      }
-
-    template<input_range _Range, typename _Proj = identity,
-	     indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>>
-	       _Pred>
-      constexpr borrowed_iterator_t<_Range>
-      operator()(_Range&& __r, _Pred __pred, _Proj __proj = {}) const
-      {
-	return (*this)(ranges::begin(__r), ranges::end(__r),
-		       std::move(__pred), std::move(__proj));
-      }
-  };
-
-  inline constexpr __find_if_not_fn find_if_not{};
+  // find, find_if and find_if_not are defined in <bits/ranges_util.h>.
 
   struct __find_first_of_fn
   {
@@ -421,134 +337,7 @@ namespace ranges
 
   inline constexpr __count_if_fn count_if{};
 
-  template<typename _Iter1, typename _Iter2>
-    struct in_in_result
-    {
-      [[no_unique_address]] _Iter1 in1;
-      [[no_unique_address]] _Iter2 in2;
-
-      template<typename _IIter1, typename _IIter2>
-	requires convertible_to<const _Iter1&, _IIter1>
-	  && convertible_to<const _Iter2&, _IIter2>
-	constexpr
-	operator in_in_result<_IIter1, _IIter2>() const &
-	{ return {in1, in2}; }
-
-      template<typename _IIter1, typename _IIter2>
-	requires convertible_to<_Iter1, _IIter1>
-	  && convertible_to<_Iter2, _IIter2>
-	constexpr
-	operator in_in_result<_IIter1, _IIter2>() &&
-	{ return {std::move(in1), std::move(in2)}; }
-    };
-
-  template<typename _Iter1, typename _Iter2>
-    using mismatch_result = in_in_result<_Iter1, _Iter2>;
-
-  struct __mismatch_fn
-  {
-    template<input_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
-	     input_iterator _Iter2, sentinel_for<_Iter2> _Sent2,
-	     typename _Pred = ranges::equal_to,
-	     typename _Proj1 = identity, typename _Proj2 = identity>
-      requires indirectly_comparable<_Iter1, _Iter2, _Pred, _Proj1, _Proj2>
-      constexpr mismatch_result<_Iter1, _Iter2>
-      operator()(_Iter1 __first1, _Sent1 __last1,
-		 _Iter2 __first2, _Sent2 __last2, _Pred __pred = {},
-		 _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const
-      {
-	while (__first1 != __last1 && __first2 != __last2
-	       && (bool)std::__invoke(__pred,
-				      std::__invoke(__proj1, *__first1),
-				      std::__invoke(__proj2, *__first2)))
-	{
-	  ++__first1;
-	  ++__first2;
-	}
-	return { std::move(__first1), std::move(__first2) };
-      }
-
-    template<input_range _Range1, input_range _Range2,
-	     typename _Pred = ranges::equal_to,
-	     typename _Proj1 = identity, typename _Proj2 = identity>
-      requires indirectly_comparable<iterator_t<_Range1>, iterator_t<_Range2>,
-				     _Pred, _Proj1, _Proj2>
-      constexpr mismatch_result<iterator_t<_Range1>, iterator_t<_Range2>>
-      operator()(_Range1&& __r1, _Range2&& __r2, _Pred __pred = {},
-		 _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const
-      {
-	return (*this)(ranges::begin(__r1), ranges::end(__r1),
-		       ranges::begin(__r2), ranges::end(__r2),
-		       std::move(__pred),
-		       std::move(__proj1), std::move(__proj2));
-      }
-  };
-
-  inline constexpr __mismatch_fn mismatch{};
-
-  struct __search_fn
-  {
-    template<forward_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
-	     forward_iterator _Iter2, sentinel_for<_Iter2> _Sent2,
-	     typename _Pred = ranges::equal_to,
-	     typename _Proj1 = identity, typename _Proj2 = identity>
-      requires indirectly_comparable<_Iter1, _Iter2, _Pred, _Proj1, _Proj2>
-      constexpr subrange<_Iter1>
-      operator()(_Iter1 __first1, _Sent1 __last1,
-		 _Iter2 __first2, _Sent2 __last2, _Pred __pred = {},
-		 _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const
-      {
-	if (__first1 == __last1 || __first2 == __last2)
-	  return {__first1, __first1};
-
-	for (;;)
-	  {
-	    for (;;)
-	      {
-		if (__first1 == __last1)
-		  return {__first1, __first1};
-		if (std::__invoke(__pred,
-				  std::__invoke(__proj1, *__first1),
-				  std::__invoke(__proj2, *__first2)))
-		  break;
-		++__first1;
-	      }
-	    auto __cur1 = __first1;
-	    auto __cur2 = __first2;
-	    for (;;)
-	      {
-		if (++__cur2 == __last2)
-		  return {__first1, ++__cur1};
-		if (++__cur1 == __last1)
-		  return {__cur1, __cur1};
-		if (!(bool)std::__invoke(__pred,
-					 std::__invoke(__proj1, *__cur1),
-					 std::__invoke(__proj2, *__cur2)))
-		  {
-		    ++__first1;
-		    break;
-		  }
-	      }
-	  }
-      }
-
-    template<forward_range _Range1, forward_range _Range2,
-	     typename _Pred = ranges::equal_to,
-	     typename _Proj1 = identity, typename _Proj2 = identity>
-      requires indirectly_comparable<iterator_t<_Range1>, iterator_t<_Range2>,
-				     _Pred, _Proj1, _Proj2>
-      constexpr borrowed_subrange_t<_Range1>
-      operator()(_Range1&& __r1, _Range2&& __r2, _Pred __pred = {},
-		 _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const
-      {
-	return (*this)(ranges::begin(__r1), ranges::end(__r1),
-		       ranges::begin(__r2), ranges::end(__r2),
-		       std::move(__pred),
-		       std::move(__proj1), std::move(__proj2));
-      }
-  };
-
-  inline constexpr __search_fn search{};
+  // in_in_result, mismatch and search are defined in <bits/ranges_util.h>.
 
   struct __search_n_fn
   {
diff --git a/libstdc++-v3/include/bits/ranges_util.h b/libstdc++-v3/include/bits/ranges_util.h
index d7b12b3d985..9a07079ac13 100644
--- a/libstdc++-v3/include/bits/ranges_util.h
+++ b/libstdc++-v3/include/bits/ranges_util.h
@@ -420,7 +420,226 @@ namespace ranges
     using borrowed_subrange_t = conditional_t<borrowed_range<_Range>,
 					      subrange<iterator_t<_Range>>,
 					      dangling>;
+} // namespace ranges
+
+// The following ranges algorithms are used by <ranges>, and are defined here
+// so that <ranges> can avoid including all of <bits/ranges_algo.h>.
+namespace ranges
+{
+  struct __find_fn
+  {
+    template<input_iterator _Iter, sentinel_for<_Iter> _Sent, typename _Tp,
+	     typename _Proj = identity>
+      requires indirect_binary_predicate<ranges::equal_to,
+					 projected<_Iter, _Proj>, const _Tp*>
+      constexpr _Iter
+      operator()(_Iter __first, _Sent __last,
+		 const _Tp& __value, _Proj __proj = {}) const
+      {
+	while (__first != __last
+	    && !(std::__invoke(__proj, *__first) == __value))
+	  ++__first;
+	return __first;
+      }
+
+    template<input_range _Range, typename _Tp, typename _Proj = identity>
+      requires indirect_binary_predicate<ranges::equal_to,
+					 projected<iterator_t<_Range>, _Proj>,
+					 const _Tp*>
+      constexpr borrowed_iterator_t<_Range>
+      operator()(_Range&& __r, const _Tp& __value, _Proj __proj = {}) const
+      {
+	return (*this)(ranges::begin(__r), ranges::end(__r),
+		       __value, std::move(__proj));
+      }
+  };
+
+  inline constexpr __find_fn find{};
+
+  struct __find_if_fn
+  {
+    template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	     typename _Proj = identity,
+	     indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+      constexpr _Iter
+      operator()(_Iter __first, _Sent __last,
+		 _Pred __pred, _Proj __proj = {}) const
+      {
+	while (__first != __last
+	    && !(bool)std::__invoke(__pred, std::__invoke(__proj, *__first)))
+	  ++__first;
+	return __first;
+      }
+
+    template<input_range _Range, typename _Proj = identity,
+	     indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>>
+	       _Pred>
+      constexpr borrowed_iterator_t<_Range>
+      operator()(_Range&& __r, _Pred __pred, _Proj __proj = {}) const
+      {
+	return (*this)(ranges::begin(__r), ranges::end(__r),
+		       std::move(__pred), std::move(__proj));
+      }
+  };
+
+  inline constexpr __find_if_fn find_if{};
+
+  struct __find_if_not_fn
+  {
+    template<input_iterator _Iter, sentinel_for<_Iter> _Sent,
+	     typename _Proj = identity,
+	     indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+      constexpr _Iter
+      operator()(_Iter __first, _Sent __last,
+		 _Pred __pred, _Proj __proj = {}) const
+      {
+	while (__first != __last
+	    && (bool)std::__invoke(__pred, std::__invoke(__proj, *__first)))
+	  ++__first;
+	return __first;
+      }
+
+    template<input_range _Range, typename _Proj = identity,
+	     indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>>
+	       _Pred>
+      constexpr borrowed_iterator_t<_Range>
+      operator()(_Range&& __r, _Pred __pred, _Proj __proj = {}) const
+      {
+	return (*this)(ranges::begin(__r), ranges::end(__r),
+		       std::move(__pred), std::move(__proj));
+      }
+  };
+
+  inline constexpr __find_if_not_fn find_if_not{};
+
+  template<typename _Iter1, typename _Iter2>
+    struct in_in_result
+    {
+      [[no_unique_address]] _Iter1 in1;
+      [[no_unique_address]] _Iter2 in2;
+
+      template<typename _IIter1, typename _IIter2>
+	requires convertible_to<const _Iter1&, _IIter1>
+	  && convertible_to<const _Iter2&, _IIter2>
+	constexpr
+	operator in_in_result<_IIter1, _IIter2>() const &
+	{ return {in1, in2}; }
+
+      template<typename _IIter1, typename _IIter2>
+	requires convertible_to<_Iter1, _IIter1>
+	  && convertible_to<_Iter2, _IIter2>
+	constexpr
+	operator in_in_result<_IIter1, _IIter2>() &&
+	{ return {std::move(in1), std::move(in2)}; }
+    };
+
+  template<typename _Iter1, typename _Iter2>
+    using mismatch_result = in_in_result<_Iter1, _Iter2>;
+
+  struct __mismatch_fn
+  {
+    template<input_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
+	     input_iterator _Iter2, sentinel_for<_Iter2> _Sent2,
+	     typename _Pred = ranges::equal_to,
+	     typename _Proj1 = identity, typename _Proj2 = identity>
+      requires indirectly_comparable<_Iter1, _Iter2, _Pred, _Proj1, _Proj2>
+      constexpr mismatch_result<_Iter1, _Iter2>
+      operator()(_Iter1 __first1, _Sent1 __last1,
+		 _Iter2 __first2, _Sent2 __last2, _Pred __pred = {},
+		 _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const
+      {
+	while (__first1 != __last1 && __first2 != __last2
+	       && (bool)std::__invoke(__pred,
+				      std::__invoke(__proj1, *__first1),
+				      std::__invoke(__proj2, *__first2)))
+	{
+	  ++__first1;
+	  ++__first2;
+	}
+	return { std::move(__first1), std::move(__first2) };
+      }
+
+    template<input_range _Range1, input_range _Range2,
+	     typename _Pred = ranges::equal_to,
+	     typename _Proj1 = identity, typename _Proj2 = identity>
+      requires indirectly_comparable<iterator_t<_Range1>, iterator_t<_Range2>,
+				     _Pred, _Proj1, _Proj2>
+      constexpr mismatch_result<iterator_t<_Range1>, iterator_t<_Range2>>
+      operator()(_Range1&& __r1, _Range2&& __r2, _Pred __pred = {},
+		 _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const
+      {
+	return (*this)(ranges::begin(__r1), ranges::end(__r1),
+		       ranges::begin(__r2), ranges::end(__r2),
+		       std::move(__pred),
+		       std::move(__proj1), std::move(__proj2));
+      }
+  };
+
+  inline constexpr __mismatch_fn mismatch{};
+
+  struct __search_fn
+  {
+    template<forward_iterator _Iter1, sentinel_for<_Iter1> _Sent1,
+	     forward_iterator _Iter2, sentinel_for<_Iter2> _Sent2,
+	     typename _Pred = ranges::equal_to,
+	     typename _Proj1 = identity, typename _Proj2 = identity>
+      requires indirectly_comparable<_Iter1, _Iter2, _Pred, _Proj1, _Proj2>
+      constexpr subrange<_Iter1>
+      operator()(_Iter1 __first1, _Sent1 __last1,
+		 _Iter2 __first2, _Sent2 __last2, _Pred __pred = {},
+		 _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const
+      {
+	if (__first1 == __last1 || __first2 == __last2)
+	  return {__first1, __first1};
+
+	for (;;)
+	  {
+	    for (;;)
+	      {
+		if (__first1 == __last1)
+		  return {__first1, __first1};
+		if (std::__invoke(__pred,
+				  std::__invoke(__proj1, *__first1),
+				  std::__invoke(__proj2, *__first2)))
+		  break;
+		++__first1;
+	      }
+	    auto __cur1 = __first1;
+	    auto __cur2 = __first2;
+	    for (;;)
+	      {
+		if (++__cur2 == __last2)
+		  return {__first1, ++__cur1};
+		if (++__cur1 == __last1)
+		  return {__cur1, __cur1};
+		if (!(bool)std::__invoke(__pred,
+					 std::__invoke(__proj1, *__cur1),
+					 std::__invoke(__proj2, *__cur2)))
+		  {
+		    ++__first1;
+		    break;
+		  }
+	      }
+	  }
+      }
+
+    template<forward_range _Range1, forward_range _Range2,
+	     typename _Pred = ranges::equal_to,
+	     typename _Proj1 = identity, typename _Proj2 = identity>
+      requires indirectly_comparable<iterator_t<_Range1>, iterator_t<_Range2>,
+				     _Pred, _Proj1, _Proj2>
+      constexpr borrowed_subrange_t<_Range1>
+      operator()(_Range1&& __r1, _Range2&& __r2, _Pred __pred = {},
+		 _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const
+      {
+	return (*this)(ranges::begin(__r1), ranges::end(__r1),
+		       ranges::begin(__r2), ranges::end(__r2),
+		       std::move(__pred),
+		       std::move(__proj1), std::move(__proj2));
+      }
+  };
 
+  inline constexpr __search_fn search{};
 } // namespace ranges
 
   using ranges::get;
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index f96adf63d10..f93a880ff8a 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -1144,54 +1144,6 @@ namespace views::__adaptor
       using all_t = decltype(all(std::declval<_Range>()));
   } // namespace views
 
-  // The following simple algos are transcribed from ranges_algo.h to avoid
-  // having to include that entire header.
-  namespace __detail
-  {
-    template<typename _Iter, typename _Sent, typename _Tp>
-      constexpr _Iter
-      find(_Iter __first, _Sent __last, const _Tp& __value)
-      {
-	while (__first != __last
-	       && !(bool)(*__first == __value))
-	  ++__first;
-	return __first;
-      }
-
-    template<typename _Iter, typename _Sent, typename _Pred>
-      constexpr _Iter
-      find_if(_Iter __first, _Sent __last, _Pred __pred)
-      {
-	while (__first != __last
-	       && !(bool)std::__invoke(__pred, *__first))
-	  ++__first;
-	return __first;
-      }
-
-    template<typename _Iter, typename _Sent, typename _Pred>
-      constexpr _Iter
-      find_if_not(_Iter __first, _Sent __last, _Pred __pred)
-      {
-	while (__first != __last
-	       && (bool)std::__invoke(__pred, *__first))
-	  ++__first;
-	return __first;
-      }
-
-    template<typename _Iter1, typename _Sent1, typename _Iter2, typename _Sent2>
-      constexpr pair<_Iter1, _Iter2>
-      mismatch(_Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Sent2 __last2)
-      {
-	while (__first1 != __last1 && __first2 != __last2
-	       && (bool)ranges::equal_to{}(*__first1, *__first2))
-	  {
-	    ++__first1;
-	    ++__first2;
-	  }
-	return { std::move(__first1), std::move(__first2) };
-      }
-  } // namespace __detail
-
   namespace __detail
   {
     template<typename _Tp>
@@ -1449,9 +1401,9 @@ namespace views::__adaptor
 	constexpr _Iterator&
 	operator++()
 	{
-	  _M_current = __detail::find_if(std::move(++_M_current),
-					 ranges::end(_M_parent->_M_base),
-					 std::ref(*_M_parent->_M_pred));
+	  _M_current = ranges::find_if(std::move(++_M_current),
+				       ranges::end(_M_parent->_M_base),
+				       std::ref(*_M_parent->_M_pred));
 	  return *this;
 	}
 
@@ -1560,9 +1512,9 @@ namespace views::__adaptor
 	  return {this, _M_cached_begin._M_get(_M_base)};
 
 	__glibcxx_assert(_M_pred.has_value());
-	auto __it = __detail::find_if(ranges::begin(_M_base),
-				      ranges::end(_M_base),
-				      std::ref(*_M_pred));
+	auto __it = ranges::find_if(ranges::begin(_M_base),
+				    ranges::end(_M_base),
+				    std::ref(*_M_pred));
 	_M_cached_begin._M_set(_M_base, __it);
 	return {this, std::move(__it)};
       }
@@ -2453,9 +2405,9 @@ namespace views::__adaptor
 	  return _M_cached_begin._M_get(_M_base);
 
 	__glibcxx_assert(_M_pred.has_value());
-	auto __it = __detail::find_if_not(ranges::begin(_M_base),
-					  ranges::end(_M_base),
-					  std::cref(*_M_pred));
+	auto __it = ranges::find_if_not(ranges::begin(_M_base),
+					ranges::end(_M_base),
+					std::cref(*_M_pred));
 	_M_cached_begin._M_set(_M_base, __it);
 	return __it;
       }
@@ -3031,8 +2983,8 @@ namespace views::__adaptor
 	      ++__current();
 	    else if constexpr (__detail::__tiny_range<_Pattern>)
 	      {
-		__current() = __detail::find(std::move(__current()), __end,
-					     *__pbegin);
+		__current() = ranges::find(std::move(__current()), __end,
+					   *__pbegin);
 		if (__current() != __end)
 		  ++__current();
 	      }
@@ -3040,7 +2992,7 @@ namespace views::__adaptor
 	      do
 		{
 		  auto [__b, __p]
-		    = __detail::mismatch(__current(), __end, __pbegin, __pend);
+		    = ranges::mismatch(__current(), __end, __pbegin, __pend);
 		  if (__p == __pend)
 		    {
 		      __current() = __b;
-- 
2.32.0.93.g670b81a890


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 3/5] libstdc++: Rename views::split to views::lazy_split as per P2210
  2021-06-17 15:22 [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Patrick Palka
  2021-06-17 15:22 ` [PATCH 2/5] libstdc++: Move ranges algos used by <ranges> into ranges_util.h Patrick Palka
@ 2021-06-17 15:22 ` Patrick Palka
  2021-06-18 21:56   ` Jonathan Wakely
  2021-06-17 15:22 ` [PATCH 4/5] libstdc++: Implement resolution of LWG 3478 " Patrick Palka
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 11+ messages in thread
From: Patrick Palka @ 2021-06-17 15:22 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Patrick Palka

This mostly mechanical patch performs the renaming part of P2210R3
"Superior string splitting".  It also defines _InnerIter::base()
overloads.

libstdc++-v3/ChangeLog:

	* include/std/ranges: Rename views::split to views::lazy_split,
	split_view to lazy_split_view, etc. throughout.
	(lazy_split_view::_InnerIter::base): Define as per P2210.
	* testsuite/std/ranges/*: Likewise.
---
 libstdc++-v3/include/std/ranges               | 68 +++++++++++--------
 .../testsuite/std/ranges/adaptors/100479.cc   |  2 +-
 .../testsuite/std/ranges/adaptors/100577.cc   | 20 +++---
 .../testsuite/std/ranges/adaptors/join.cc     |  2 +-
 .../adaptors/{split.cc => lazy_split.cc}      | 54 +++++++--------
 .../{split_neg.cc => lazy_split_neg.cc}       |  6 +-
 .../testsuite/std/ranges/adaptors/p2281.cc    | 18 ++---
 .../testsuite/std/ranges/adaptors/sizeof.cc   |  2 +-
 libstdc++-v3/testsuite/std/ranges/p2259.cc    |  6 +-
 libstdc++-v3/testsuite/std/ranges/p2325.cc    | 10 +--
 libstdc++-v3/testsuite/std/ranges/p2367.cc    |  4 +-
 11 files changed, 100 insertions(+), 92 deletions(-)
 rename libstdc++-v3/testsuite/std/ranges/adaptors/{split.cc => lazy_split.cc} (76%)
 rename libstdc++-v3/testsuite/std/ranges/adaptors/{split_neg.cc => lazy_split_neg.cc} (79%)

diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index f93a880ff8a..cc1ef112ff1 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -2826,19 +2826,19 @@ namespace views::__adaptor
 	&& (remove_reference_t<_Range>::size() <= 1);
 
     template<typename _Base>
-      struct __split_view_outer_iter_cat
+      struct __lazy_split_view_outer_iter_cat
       { };
 
     template<forward_range _Base>
-      struct __split_view_outer_iter_cat<_Base>
+      struct __lazy_split_view_outer_iter_cat<_Base>
       { using iterator_category = input_iterator_tag; };
 
     template<typename _Base>
-      struct __split_view_inner_iter_cat
+      struct __lazy_split_view_inner_iter_cat
       { };
 
     template<forward_range _Base>
-      struct __split_view_inner_iter_cat<_Base>
+      struct __lazy_split_view_inner_iter_cat<_Base>
       {
       private:
 	static constexpr auto
@@ -2860,7 +2860,7 @@ namespace views::__adaptor
       && indirectly_comparable<iterator_t<_Vp>, iterator_t<_Pattern>,
 			       ranges::equal_to>
       && (forward_range<_Vp> || __detail::__tiny_range<_Pattern>)
-    class split_view : public view_interface<split_view<_Vp, _Pattern>>
+    class lazy_split_view : public view_interface<lazy_split_view<_Vp, _Pattern>>
     {
     private:
       template<bool _Const>
@@ -2871,17 +2871,17 @@ namespace views::__adaptor
 
       template<bool _Const>
 	struct _OuterIter
-	  : __detail::__split_view_outer_iter_cat<_Base<_Const>>
+	  : __detail::__lazy_split_view_outer_iter_cat<_Base<_Const>>
 	{
 	private:
-	  using _Parent = __detail::__maybe_const_t<_Const, split_view>;
-	  using _Base = split_view::_Base<_Const>;
+	  using _Parent = __detail::__maybe_const_t<_Const, lazy_split_view>;
+	  using _Base = lazy_split_view::_Base<_Const>;
 
 	  constexpr bool
 	  __at_end() const
 	  { return __current() == ranges::end(_M_parent->_M_base); }
 
-	  // [range.split.outer] p1
+	  // [range.lazy.split.outer] p1
 	  //  Many of the following specifications refer to the notional member
 	  //  current of outer-iterator.  current is equivalent to current_ if
 	  //  V models forward_range, and parent_->current_ otherwise.
@@ -2914,7 +2914,7 @@ namespace views::__adaptor
 	  using iterator_concept = conditional_t<forward_range<_Base>,
 						 forward_iterator_tag,
 						 input_iterator_tag>;
-	  // iterator_category defined in __split_view_outer_iter_cat
+	  // iterator_category defined in __lazy_split_view_outer_iter_cat
 	  using difference_type = range_difference_t<_Base>;
 
 	  struct value_type : view_interface<value_type>
@@ -2974,7 +2974,7 @@ namespace views::__adaptor
 	  operator++()
 	  {
 	    // _GLIBCXX_RESOLVE_LIB_DEFECTS
-	    // 3505. split_view::outer-iterator::operator++ misspecified
+	    // 3505. lazy_split_view::outer-iterator::operator++ misspecified
 	    const auto __end = ranges::end(_M_parent->_M_base);
 	    if (__current() == __end)
 	      return *this;
@@ -3030,10 +3030,10 @@ namespace views::__adaptor
 
       template<bool _Const>
 	struct _InnerIter
-	  : __detail::__split_view_inner_iter_cat<_Base<_Const>>
+	  : __detail::__lazy_split_view_inner_iter_cat<_Base<_Const>>
 	{
 	private:
-	  using _Base = split_view::_Base<_Const>;
+	  using _Base = lazy_split_view::_Base<_Const>;
 
 	  constexpr bool
 	  __at_end() const
@@ -3081,7 +3081,7 @@ namespace views::__adaptor
 	public:
 	  using iterator_concept
 	    = typename _OuterIter<_Const>::iterator_concept;
-	  // iterator_category defined in __split_view_inner_iter_cat
+	  // iterator_category defined in __lazy_split_view_inner_iter_cat
 	  using value_type = range_value_t<_Base>;
 	  using difference_type = range_difference_t<_Base>;
 
@@ -3092,6 +3092,14 @@ namespace views::__adaptor
 	    : _M_i(std::move(__i))
 	  { }
 
+	  constexpr iterator_t<_Base>
+	  base() const& requires copyable<iterator_t<_Base>>
+	  { return _M_i_current(); }
+
+	  constexpr iterator_t<_Base>
+	  base() &&
+	  { return std::move(_M_i_current()); }
+
 	  constexpr decltype(auto)
 	  operator*() const
 	  { return *_M_i_current(); }
@@ -3151,12 +3159,12 @@ namespace views::__adaptor
 
 
     public:
-      split_view() requires (default_initializable<_Vp>
-			     && default_initializable<_Pattern>)
+      lazy_split_view() requires (default_initializable<_Vp>
+				  && default_initializable<_Pattern>)
 	= default;
 
       constexpr
-      split_view(_Vp __base, _Pattern __pattern)
+      lazy_split_view(_Vp __base, _Pattern __pattern)
 	: _M_pattern(std::move(__pattern)), _M_base(std::move(__base))
       { }
 
@@ -3164,7 +3172,7 @@ namespace views::__adaptor
 	requires constructible_from<_Vp, views::all_t<_Range>>
 	  && constructible_from<_Pattern, single_view<range_value_t<_Range>>>
 	constexpr
-	split_view(_Range&& __r, range_value_t<_Range> __e)
+	lazy_split_view(_Range&& __r, range_value_t<_Range> __e)
 	  : _M_pattern(views::single(std::move(__e))),
 	    _M_base(views::all(std::forward<_Range>(__r)))
 	{ }
@@ -3216,35 +3224,35 @@ namespace views::__adaptor
     };
 
   template<typename _Range, typename _Pattern>
-    split_view(_Range&&, _Pattern&&)
-      -> split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
+    lazy_split_view(_Range&&, _Pattern&&)
+      -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
 
   template<input_range _Range>
-    split_view(_Range&&, range_value_t<_Range>)
-      -> split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
+    lazy_split_view(_Range&&, range_value_t<_Range>)
+      -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
 
   namespace views
   {
     namespace __detail
     {
       template<typename _Range, typename _Pattern>
-	concept __can_split_view
-	  = requires { split_view(std::declval<_Range>(), std::declval<_Pattern>()); };
+	concept __can_lazy_split_view
+	  = requires { lazy_split_view(std::declval<_Range>(), std::declval<_Pattern>()); };
     } // namespace __detail
 
-    struct _Split : __adaptor::_RangeAdaptor<_Split>
+    struct _LazySplit : __adaptor::_RangeAdaptor<_LazySplit>
     {
       template<viewable_range _Range, typename _Pattern>
-	requires __detail::__can_split_view<_Range, _Pattern>
+	requires __detail::__can_lazy_split_view<_Range, _Pattern>
 	constexpr auto
 	operator()(_Range&& __r, _Pattern&& __f) const
 	{
-	  return split_view(std::forward<_Range>(__r), std::forward<_Pattern>(__f));
+	  return lazy_split_view(std::forward<_Range>(__r), std::forward<_Pattern>(__f));
 	}
 
-      using _RangeAdaptor<_Split>::operator();
+      using _RangeAdaptor<_LazySplit>::operator();
       static constexpr int _S_arity = 2;
-      // The pattern argument of views::split is not always simple -- it can be
+      // The pattern argument of views::lazy_split is not always simple -- it can be
       // a non-view range, the value category of which affects whether the call
       // is well-formed.  But a scalar or a view pattern argument is surely
       // simple.
@@ -3254,7 +3262,7 @@ namespace views::__adaptor
 				      && copy_constructible<_Pattern>);
     };
 
-    inline constexpr _Split split;
+    inline constexpr _LazySplit lazy_split;
   } // namespace views
 
   namespace views
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc
index ba10b7baf3f..9899ff92c0b 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc
@@ -90,7 +90,7 @@ test03()
   // Propagating cached iterators during copy/move would cause these asserts
   // to fail here.
   auto v = views::single(1)
-    | views::split(1)
+    | views::lazy_split(1)
     | views::drop(0)
     | views::drop_while([](auto) { return false; })
     | views::filter([](auto) { return true; });
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
index 06be4980ddb..5ef7f3f59a7 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
@@ -38,11 +38,11 @@ test01()
   static_assert(__adaptor_has_simple_extra_args<decltype(views::take), int>);
   static_assert(__adaptor_has_simple_extra_args<decltype(views::take_while), identity>);
   static_assert(__adaptor_has_simple_extra_args<decltype(views::drop_while), identity>);
-  static_assert(__adaptor_has_simple_extra_args<decltype(views::split), std::string_view>);
-  static_assert(__adaptor_has_simple_extra_args<decltype(views::split), char>);
-  static_assert(!__adaptor_has_simple_extra_args<decltype(views::split), std::string>);
+  static_assert(__adaptor_has_simple_extra_args<decltype(views::lazy_split), std::string_view>);
+  static_assert(__adaptor_has_simple_extra_args<decltype(views::lazy_split), char>);
+  static_assert(!__adaptor_has_simple_extra_args<decltype(views::lazy_split), std::string>);
 
-  // Verify all adaptor closures except for views::split(pattern) have a simple
+  // Verify all adaptor closures except for views::lazy_split(pattern) have a simple
   // operator().
   using views::__adaptor::__closure_has_simple_call_op;
   __closure_has_simple_call_op auto a00 = views::all;
@@ -56,14 +56,14 @@ test01()
   __closure_has_simple_call_op auto a08 = views::common;
   __closure_has_simple_call_op auto a09 = views::reverse;
   __closure_has_simple_call_op auto a10 = views::keys;
-  __closure_has_simple_call_op auto a11 = views::split(' ');
+  __closure_has_simple_call_op auto a11 = views::lazy_split(' ');
   // Verify composition of simple closures is simple.
   __closure_has_simple_call_op auto b
     = (a00 | a01) | (a02 | a03) | (a04 | a05 | a06) | (a07 | a08 | a09 | a10) | a11;
 
-  // Verify views::split(non_view_range) is an exception.
+  // Verify views::lazy_split(non_view_range) is an exception.
   extern std::string s;
-  auto a12 = views::split(s);
+  auto a12 = views::lazy_split(s);
   static_assert(!__closure_has_simple_call_op<decltype(a12)>);
   static_assert(!__closure_has_simple_call_op<decltype(a12 | a00)>);
   static_assert(!__closure_has_simple_call_op<decltype(a00 | a12)>);
@@ -91,9 +91,9 @@ test02()
   // implemented using a fallback deleted overload, so when a call is
   // ill-formed overload resolution succeeds but selects the deleted overload
   // (but only when the closure is invoked as an rvalue).
-  views::split(badarg)(x); // { dg-error "deleted function" }
-  (views::split(badarg) | views::all)(x); // { dg-error "deleted function" }
-  auto a0 = views::split(badarg);
+  views::lazy_split(badarg)(x); // { dg-error "deleted function" }
+  (views::lazy_split(badarg) | views::all)(x); // { dg-error "deleted function" }
+  auto a0 = views::lazy_split(badarg);
   a0(x); // { dg-error "no match" };
   auto a1 = a0 | views::all;
   a1(x); // { dg-error "no match" }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
index d774e8d9385..50af3fdf729 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
@@ -93,7 +93,7 @@ test05()
 {
   using namespace std::literals;
   std::vector<std::string> x = {"the", " ", "quick", " ", "brown", " ", "fox"};
-  auto v = x | views::join | views::split(' ');
+  auto v = x | views::join | views::lazy_split(' ');
   auto i = v.begin();
   VERIFY( ranges::equal(*i++, "the"sv) );
   VERIFY( ranges::equal(*i++, "quick"sv) );
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
similarity index 76%
rename from libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
rename to libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
index 9d2cfa8632a..12844525d86 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
@@ -39,7 +39,7 @@ test01()
 {
   auto x = "the  quick  brown  fox"sv;
   auto p = std::string{"  "};
-  auto v = x | views::split(views::all(p)); // views::all is needed here after P2281.
+  auto v = x | views::lazy_split(views::all(p)); // views::all is needed here after P2281.
   auto i = v.begin();
   VERIFY( ranges::equal(*i++, "the"sv) );
   VERIFY( ranges::equal(*i++, "quick"sv) );
@@ -52,7 +52,7 @@ void
 test02()
 {
   auto x = "the quick brown fox"sv;
-  auto v = x | views::split(' ');
+  auto v = x | views::lazy_split(' ');
   auto i = v.begin();
   VERIFY( ranges::equal(*i++, "the"sv) );
   VERIFY( ranges::equal(*i++, "quick"sv) );
@@ -66,7 +66,7 @@ test03()
 {
   char x[] = "the quick brown fox";
   test_range<char, forward_iterator_wrapper> rx(x, x+sizeof(x)-1);
-  auto v = rx | views::split(' ');
+  auto v = rx | views::lazy_split(' ');
   auto i = v.begin();
   VERIFY( ranges::equal(*i++, "the"sv) );
   VERIFY( ranges::equal(*i++, "quick"sv) );
@@ -83,7 +83,7 @@ test04()
   static_assert(!ranges::view<decltype(p)>);
   static_assert(std::same_as<decltype(p | views::all),
 			     ranges::ref_view<decltype(p)>>);
-  auto v = x | views::split(views::all(p)); // views::all is needed here after P2281.
+  auto v = x | views::lazy_split(views::all(p)); // views::all is needed here after P2281.
   auto i = v.begin();
   VERIFY( ranges::equal(*i++, "the"sv) );
   VERIFY( ranges::equal(*i++, "quick"sv) );
@@ -102,7 +102,7 @@ test05()
   std::string str
     = "Now is the time for all good men to come to the aid of their county.";
   auto rng
-    = str | views::split(' ') | views::transform(as_string) | views::common;
+    = str | views::lazy_split(' ') | views::transform(as_string) | views::common;
   std::vector<std::string> words(rng.begin(), rng.end());
   auto not_space_p = [](char c) { return c != ' '; };
   VERIFY( ranges::equal(words | views::join,
@@ -113,7 +113,7 @@ void
 test06()
 {
   std::string str = "hello world";
-  auto v = str | views::transform(std::identity{}) | views::split(' ');
+  auto v = str | views::transform(std::identity{}) | views::lazy_split(' ');
 
   // Verify that _Iterator<false> is implicitly convertible to _Iterator<true>.
   static_assert(!std::same_as<decltype(ranges::begin(v)),
@@ -126,7 +126,7 @@ void
 test07()
 {
   char str[] = "banana split";
-  auto split = str | views::split(' ');
+  auto split = str | views::lazy_split(' ');
   auto val = *split.begin();
   auto b = val.begin();
   auto b2 = b++;
@@ -139,7 +139,7 @@ test08()
 {
   char x[] = "the quick brown fox";
   test_range<char, input_iterator_wrapper> rx(x, x+sizeof(x)-1);
-  auto v = rx | views::split(' ');
+  auto v = rx | views::lazy_split(' ');
   auto i = v.begin();
   VERIFY( ranges::equal(*i, "the"sv) );
   ++i;
@@ -152,32 +152,32 @@ test08()
   VERIFY( i == v.end() );
 }
 
-template<auto split = views::split>
+template<auto lazy_split = views::lazy_split>
 void
 test09()
 {
   // Verify SFINAE behavior.
   std::string s, p;
-  static_assert(!requires { split(); });
-  static_assert(!requires { split(s, p, 0); });
-  static_assert(!requires { split(p)(); });
-  static_assert(!requires { s | split; });
-
-  static_assert(!requires { s | split(p); });
-  static_assert(!requires { split(p)(s); });
-  static_assert(!requires { s | (split(p) | views::all); });
-  static_assert(!requires { (split(p) | views::all)(s); });
-
-  static_assert(requires { s | split(views::all(p)); });
-  static_assert(requires { split(views::all(p))(s); });
-  static_assert(requires { s | (split(views::all(p)) | views::all); });
-  static_assert(requires { (split(views::all(p)) | views::all)(s); });
-
-  auto adapt = split(p);
+  static_assert(!requires { lazy_split(); });
+  static_assert(!requires { lazy_split(s, p, 0); });
+  static_assert(!requires { lazy_split(p)(); });
+  static_assert(!requires { s | lazy_split; });
+
+  static_assert(!requires { s | lazy_split(p); });
+  static_assert(!requires { lazy_split(p)(s); });
+  static_assert(!requires { s | (lazy_split(p) | views::all); });
+  static_assert(!requires { (lazy_split(p) | views::all)(s); });
+
+  static_assert(requires { s | lazy_split(views::all(p)); });
+  static_assert(requires { lazy_split(views::all(p))(s); });
+  static_assert(requires { s | (lazy_split(views::all(p)) | views::all); });
+  static_assert(requires { (lazy_split(views::all(p)) | views::all)(s); });
+
+  auto adapt = lazy_split(p);
   static_assert(requires { s | adapt; });
   static_assert(requires { adapt(s); });
 
-  auto adapt2 = split(p) | views::all;
+  auto adapt2 = lazy_split(p) | views::all;
   static_assert(requires { s | adapt2; });
   static_assert(requires { adapt2(s); });
 }
@@ -189,7 +189,7 @@ test10()
   auto to_string = [] (auto r) {
     return std::string(r.begin(), ranges::next(r.begin(), r.end()));
   };
-  auto v = "xxyx"sv | views::split("xy"sv) | views::transform(to_string);
+  auto v = "xxyx"sv | views::lazy_split("xy"sv) | views::transform(to_string);
   VERIFY( ranges::equal(v, (std::string_view[]){"x", "x"}) );
 }
 
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split_neg.cc
similarity index 79%
rename from libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc
rename to libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split_neg.cc
index 4229314a9dc..c59f828ee56 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split_neg.cc
@@ -30,7 +30,7 @@ test01()
 {
   using namespace std::literals;
   auto x = "the  quick  brown  fox"sv;
-  auto v = views::split(x, std::initializer_list<char>{' ', ' '}); // { dg-error "no match" }
+  auto v = views::lazy_split(x, std::initializer_list<char>{' ', ' '}); // { dg-error "no match" }
 }
 
 void
@@ -38,8 +38,8 @@ test02()
 {
   using namespace std::literals;
   auto x = "the  quick  brown  fox"sv;
-  auto v1 = views::split(std::initializer_list<char>{' ', ' '})(x); // { dg-error "deleted" }
-  auto v2 = x | views::split(std::initializer_list<char>{' ', ' '}); // { dg-error "no match" }
+  auto v1 = views::lazy_split(std::initializer_list<char>{' ', ' '})(x); // { dg-error "deleted" }
+  auto v2 = x | views::lazy_split(std::initializer_list<char>{' ', ' '}); // { dg-error "no match" }
 }
 
 // { dg-prune-output "in requirements" }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc
index c916a5ea8b7..7950c43576d 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc
@@ -34,7 +34,7 @@ void
 test01()
 {
   auto split_into_strings = [] (auto p) {
-    return views::split(p) | views::transform([](auto r){
+    return views::lazy_split(p) | views::transform([](auto r){
       return std::string(r.begin(), ranges::next(r.begin(), r.end()));
     });
   };
@@ -60,19 +60,19 @@ struct move_only_range
 template<>
   inline constexpr bool std::ranges::enable_view<move_only_range> = true;
 
-template<auto split = views::split>
+template<auto lazy_split = views::lazy_split>
 void
 test02()
 {
   std::string_view s;
   move_only_range p;
-  static_assert(requires { s | split(std::move(p)); });
-  static_assert(requires { split(std::move(p))(s); });
-  static_assert(requires { split(std::move(p)) | views::all; });
-  static_assert(requires { views::all | split(std::move(p)); });
-  static_assert(!requires { split(p); });
-  static_assert(!requires { split(p) | views::all; });
-  static_assert(!requires { views::all | split(p); });
+  static_assert(requires { s | lazy_split(std::move(p)); });
+  static_assert(requires { lazy_split(std::move(p))(s); });
+  static_assert(requires { lazy_split(std::move(p)) | views::all; });
+  static_assert(requires { views::all | lazy_split(std::move(p)); });
+  static_assert(!requires { lazy_split(p); });
+  static_assert(!requires { lazy_split(p) | views::all; });
+  static_assert(!requires { views::all | lazy_split(p); });
 }
 
 int
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc
index 80326f8bf21..219e2a61f07 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc
@@ -46,7 +46,7 @@ static_assert(sizeof(ranges::take_while_view<V, decltype(pred_l)>) == 3*ptr);
 static_assert(sizeof(ranges::drop_while_view<V, decltype(pred_l)>) == 3*ptr);
 static_assert(sizeof(ranges::transform_view<V, decltype(func_l)>) == 3*ptr);
 
-static_assert(sizeof(ranges::split_view<V, std::string_view>) == 4*ptr);
+static_assert(sizeof(ranges::lazy_split_view<V, std::string_view>) == 4*ptr);
 
 static_assert
  (sizeof(ranges::reverse_view<ranges::filter_view<V, decltype(pred_l)>>) == 4*ptr);
diff --git a/libstdc++-v3/testsuite/std/ranges/p2259.cc b/libstdc++-v3/testsuite/std/ranges/p2259.cc
index 1b422e44f16..0ec7e21f657 100644
--- a/libstdc++-v3/testsuite/std/ranges/p2259.cc
+++ b/libstdc++-v3/testsuite/std/ranges/p2259.cc
@@ -49,12 +49,12 @@ test01()
   // Verify the changes to transform_view.
   only_cxx20_input_range auto v2 = v0 | views::transform([](int& c) -> auto& { return c; });
 
-  // Verify the changes to split_view.
-  only_cxx20_input_range auto v3 = v0 | views::split(12);
+  // Verify the changes to lazy_split_view.
+  only_cxx20_input_range auto v3 = v0 | views::lazy_split(12);
   static_assert(only_cxx20_input_range<decltype(*v3.begin())>);
 
   // Verify the changes to join_view.
-  only_cxx20_input_range auto v4 = v0 | views::split(12) | views::join;
+  only_cxx20_input_range auto v4 = v0 | views::lazy_split(12) | views::join;
 
   // Verify the changes to elements_view.
   only_cxx20_input_range auto v5
diff --git a/libstdc++-v3/testsuite/std/ranges/p2325.cc b/libstdc++-v3/testsuite/std/ranges/p2325.cc
index df6cde29e4d..4d075409026 100644
--- a/libstdc++-v3/testsuite/std/ranges/p2325.cc
+++ b/libstdc++-v3/testsuite/std/ranges/p2325.cc
@@ -113,14 +113,14 @@ test07()
 void
 test08()
 {
-  // Verify split_view is conditionally default constructible.
-  using type1 = ranges::split_view<ranges::ref_view<int[2]>, ranges::single_view<int>>;
+  // Verify lazy_split_view is conditionally default constructible.
+  using type1 = ranges::lazy_split_view<ranges::ref_view<int[2]>, ranges::single_view<int>>;
   static_assert(!default_initializable<type1>);
-  using type2 = ranges::split_view<ranges::single_view<int>, ranges::ref_view<int[2]>>;
+  using type2 = ranges::lazy_split_view<ranges::single_view<int>, ranges::ref_view<int[2]>>;
   static_assert(!default_initializable<type2>);
-  using type3 = ranges::split_view<ranges::ref_view<int[2]>, ranges::ref_view<int[2]>>;
+  using type3 = ranges::lazy_split_view<ranges::ref_view<int[2]>, ranges::ref_view<int[2]>>;
   static_assert(!default_initializable<type3>);
-  using type4 = ranges::split_view<ranges::single_view<int>, ranges::single_view<int>>;
+  using type4 = ranges::lazy_split_view<ranges::single_view<int>, ranges::single_view<int>>;
   static_assert(default_initializable<type4>);
 }
 
diff --git a/libstdc++-v3/testsuite/std/ranges/p2367.cc b/libstdc++-v3/testsuite/std/ranges/p2367.cc
index 40875233d88..5228b021602 100644
--- a/libstdc++-v3/testsuite/std/ranges/p2367.cc
+++ b/libstdc++-v3/testsuite/std/ranges/p2367.cc
@@ -43,6 +43,6 @@ test01()
   // Verify changes to views::drop.
   auto v5 = views::drop(x, 0ull);
 
-  // Verify changes to views::split.
-  auto v6 = views::split(x, 5u);
+  // Verify changes to views::lazy_split.
+  auto v6 = views::lazy_split(x, 5u);
 }
-- 
2.32.0.93.g670b81a890


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 4/5] libstdc++: Implement resolution of LWG 3478 as per P2210
  2021-06-17 15:22 [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Patrick Palka
  2021-06-17 15:22 ` [PATCH 2/5] libstdc++: Move ranges algos used by <ranges> into ranges_util.h Patrick Palka
  2021-06-17 15:22 ` [PATCH 3/5] libstdc++: Rename views::split to views::lazy_split as per P2210 Patrick Palka
@ 2021-06-17 15:22 ` Patrick Palka
  2021-06-18 21:57   ` Jonathan Wakely
  2021-06-17 15:22 ` [PATCH 5/5] libstdc++: Implement new views::split " Patrick Palka
  2021-06-17 18:19 ` [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Jonathan Wakely
  4 siblings, 1 reply; 11+ messages in thread
From: Patrick Palka @ 2021-06-17 15:22 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Patrick Palka

This implements the part of P2210R2 "Superior String Splitting" that
resolves LWG 3478 for split_view (now named lazy_split_view).

libstdc++-v3/ChangeLog:

	* include/std/ranges (lazy_split_view::_OuterIter::__at_end):
	Check _M_trailing_empty.
	(lazy_split_view::_OuterIter::_M_trailing_empty): Define this
	data member.
	(lazy_split_view::_OuterIter::operator++): Set _M_trailing_empty
	appropriately.
	(lazy_split_view::_OuterIter::operator==): Compare
	_M_trailing_empty.
	* testsuite/std/ranges/adaptors/100479.cc (test03): Expect two
	split parts instead of one.
	* testsuite/std/ranges/adaptors/lazy_split.cc (test11): New test.
---
 libstdc++-v3/include/std/ranges               | 21 +++++++++++++++----
 .../testsuite/std/ranges/adaptors/100479.cc   |  6 +++---
 .../std/ranges/adaptors/lazy_split.cc         | 15 +++++++++++++
 3 files changed, 35 insertions(+), 7 deletions(-)

diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index cc1ef112ff1..78562924bee 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -2879,7 +2879,7 @@ namespace views::__adaptor
 
 	  constexpr bool
 	  __at_end() const
-	  { return __current() == ranges::end(_M_parent->_M_base); }
+	  { return __current() == ranges::end(_M_parent->_M_base) && !_M_trailing_empty; }
 
 	  // [range.lazy.split.outer] p1
 	  //  Many of the following specifications refer to the notional member
@@ -2909,6 +2909,7 @@ namespace views::__adaptor
 	  [[no_unique_address]]
 	    __detail::__maybe_present_t<forward_range<_Vp>,
 					iterator_t<_Base>> _M_current;
+	  bool _M_trailing_empty = false;
 
 	public:
 	  using iterator_concept = conditional_t<forward_range<_Base>,
@@ -2977,7 +2978,10 @@ namespace views::__adaptor
 	    // 3505. lazy_split_view::outer-iterator::operator++ misspecified
 	    const auto __end = ranges::end(_M_parent->_M_base);
 	    if (__current() == __end)
-	      return *this;
+	      {
+		_M_trailing_empty = false;
+		return *this;
+	      }
 	    const auto [__pbegin, __pend] = subrange{_M_parent->_M_pattern};
 	    if (__pbegin == __pend)
 	      ++__current();
@@ -2986,7 +2990,11 @@ namespace views::__adaptor
 		__current() = ranges::find(std::move(__current()), __end,
 					   *__pbegin);
 		if (__current() != __end)
-		  ++__current();
+		  {
+		    ++__current();
+		    if (__current() == __end)
+		      _M_trailing_empty = true;
+		  }
 	      }
 	    else
 	      do
@@ -2996,6 +3004,8 @@ namespace views::__adaptor
 		  if (__p == __pend)
 		    {
 		      __current() = __b;
+		      if (__current() == __end)
+			_M_trailing_empty = true;
 		      break;
 		    }
 		} while (++__current() != __end);
@@ -3018,7 +3028,10 @@ namespace views::__adaptor
 	  friend constexpr bool
 	  operator==(const _OuterIter& __x, const _OuterIter& __y)
 	    requires forward_range<_Base>
-	  { return __x._M_current == __y._M_current; }
+	  {
+	    return __x._M_current == __y._M_current
+	      && __x._M_trailing_empty == __y._M_trailing_empty;
+	  }
 
 	  friend constexpr bool
 	  operator==(const _OuterIter& __x, default_sentinel_t)
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc
index 9899ff92c0b..b8c1e6f4f57 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc
@@ -95,11 +95,11 @@ test03()
     | views::drop_while([](auto) { return false; })
     | views::filter([](auto) { return true; });
   static_assert(ranges::forward_range<decltype(v)>);
-  VERIFY( ranges::next(v.begin()) == v.end() );
+  VERIFY( ranges::distance(v) == 2 );
   auto w = v;
-  VERIFY( ranges::next(w.begin()) == w.end() );
+  VERIFY( ranges::distance(v) == 2 );
   auto z = std::move(w);
-  VERIFY( ranges::next(z.begin()) == z.end() );
+  VERIFY( ranges::distance(v) == 2 );
   return true;
 }
 
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
index 12844525d86..133e9a7025b 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
@@ -193,6 +193,20 @@ test10()
   VERIFY( ranges::equal(v, (std::string_view[]){"x", "x"}) );
 }
 
+void
+test11()
+{
+  // LWG 3478
+  static_assert(ranges::distance(views::lazy_split("text"sv, "text"sv)) == 2);
+  static_assert(ranges::distance(views::lazy_split(" text "sv, ' ')) == 3);
+  static_assert(ranges::distance(views::lazy_split(" t e x t "sv, ' ')) == 6);
+  static_assert(ranges::distance(views::lazy_split("  text  "sv, "  "sv)) == 3);
+  static_assert(ranges::distance(views::lazy_split("  text    "sv, "  "sv)) == 4);
+  static_assert(ranges::distance(views::lazy_split("  text     "sv, "  "sv)) == 4);
+  static_assert(ranges::distance(views::lazy_split("t"sv, 't')) == 2);
+  static_assert(ranges::distance(views::lazy_split("text"sv, ""sv)) == 4);
+}
+
 int
 main()
 {
@@ -206,4 +220,5 @@ main()
   test08();
   test09();
   test10();
+  test11();
 }
-- 
2.32.0.93.g670b81a890


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 5/5] libstdc++: Implement new views::split as per P2210
  2021-06-17 15:22 [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Patrick Palka
                   ` (2 preceding siblings ...)
  2021-06-17 15:22 ` [PATCH 4/5] libstdc++: Implement resolution of LWG 3478 " Patrick Palka
@ 2021-06-17 15:22 ` Patrick Palka
  2021-06-18 21:58   ` Jonathan Wakely
  2021-06-17 18:19 ` [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Jonathan Wakely
  4 siblings, 1 reply; 11+ messages in thread
From: Patrick Palka @ 2021-06-17 15:22 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Patrick Palka

This implements the new views::split as specified by P2210R2 "Superior
string splitting".

libstdc++-v3/ChangeLog:

	* include/std/ranges (__non_propagating_cache::operator bool):
	Define.
	(split_view): Define as per P2210.
	(views::__detail::__can_split_view): Define.
	(views::_Split, views::Split): Define.
	* testsuite/std/ranges/adaptors/100577.cc (test01, test02):
	Test views::split.
	* testsuite/std/ranges/adaptors/split.cc: New test.
	* testsuite/std/ranges/p2325.cc (test08a): New test.
	* testsuite/std/ranges/p2367.cc (test01): Test views::split.
---
 libstdc++-v3/include/std/ranges               | 205 ++++++++++++++++++
 .../testsuite/std/ranges/adaptors/100577.cc   |  16 +-
 .../testsuite/std/ranges/adaptors/split.cc    | 196 +++++++++++++++++
 libstdc++-v3/testsuite/std/ranges/p2325.cc    |  14 ++
 libstdc++-v3/testsuite/std/ranges/p2367.cc    |   1 +
 5 files changed, 430 insertions(+), 2 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/split.cc

diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index 78562924bee..42278f128b8 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -1195,6 +1195,10 @@ namespace views::__adaptor
 	  return *this;
 	}
 
+	constexpr explicit
+	operator bool() const noexcept
+	{ return this->_M_is_engaged(); }
+
 	constexpr _Tp&
 	operator*() noexcept
 	{ return this->_M_get(); }
@@ -3278,6 +3282,207 @@ namespace views::__adaptor
     inline constexpr _LazySplit lazy_split;
   } // namespace views
 
+  template<forward_range _Vp, forward_range _Pattern>
+    requires view<_Vp> && view<_Pattern>
+      && indirectly_comparable<iterator_t<_Vp>, iterator_t<_Pattern>,
+			       ranges::equal_to>
+  class split_view : public view_interface<split_view<_Vp, _Pattern>>
+  {
+  private:
+    _Pattern _M_pattern = _Pattern();
+    __detail::__non_propagating_cache<subrange<iterator_t<_Vp>>> _M_cached_begin;
+    _Vp _M_base = _Vp();
+
+    struct _Iterator;
+    struct _Sentinel;
+
+  public:
+    split_view() requires (default_initializable<_Vp>
+			   && default_initializable<_Pattern>)
+      = default;
+
+    constexpr
+    split_view(_Vp __base, _Pattern __pattern)
+      : _M_pattern(std::move(__pattern)),
+	_M_base(std::move(__base))
+    { }
+
+    template<forward_range _Range>
+      requires constructible_from<_Vp, views::all_t<_Range>>
+	&& constructible_from<_Pattern, single_view<range_value_t<_Range>>>
+    constexpr
+    split_view(_Range&& __r, range_value_t<_Range> __e)
+      : _M_pattern(views::single(__e)),
+	_M_base(views::all(std::forward<_Range>(__r)))
+    { }
+
+    constexpr _Vp
+    base() const& requires copyable<_Vp>
+    { return _M_base; }
+
+    constexpr _Vp
+    base() &&
+    { return std::move(_M_base); }
+
+    constexpr _Iterator
+    begin()
+    {
+      if (!_M_cached_begin)
+	_M_cached_begin = _M_find_next(ranges::begin(_M_base));
+      return {this, ranges::begin(_M_base), *_M_cached_begin};
+    }
+
+    constexpr auto
+    end()
+    {
+      if constexpr (common_range<_Vp>)
+	return _Iterator{this, ranges::end(_M_base), {}};
+      else
+	return _Sentinel{this};
+    }
+
+    constexpr subrange<iterator_t<_Vp>>
+    _M_find_next(iterator_t<_Vp> __it)
+    {
+      auto [__b, __e] = ranges::search(subrange(__it, ranges::end(_M_base)), _M_pattern);
+      if (__b != ranges::end(_M_base) && ranges::empty(_M_pattern))
+	{
+	  ++__b;
+	  ++__e;
+	}
+      return {__b, __e};
+    }
+
+  private:
+    struct _Iterator
+    {
+    private:
+      split_view* _M_parent = nullptr;
+      iterator_t<_Vp> _M_cur = iterator_t<_Vp>();
+      subrange<iterator_t<_Vp>> _M_next = subrange<iterator_t<_Vp>>();
+      bool _M_trailing_empty = false;
+
+    public:
+      using iterator_concept = forward_iterator_tag;
+      using iterator_category = input_iterator_tag;
+      using value_type = subrange<iterator_t<_Vp>>;
+      using difference_type = range_difference_t<_Vp>;
+
+      _Iterator() requires default_initializable<iterator_t<_Vp>> = default;
+
+      constexpr
+      _Iterator(split_view* __parent,
+		iterator_t<_Vp> __current,
+		subrange<iterator_t<_Vp>> __next)
+	: _M_parent(__parent),
+	  _M_cur(std::move(__current)),
+	  _M_next(std::move(__next))
+      { }
+
+      constexpr iterator_t<_Vp>
+      base() const
+      { return _M_cur; }
+
+      constexpr value_type
+      operator*() const
+      { return {_M_cur, _M_next.begin()}; }
+
+      constexpr _Iterator&
+      operator++()
+      {
+	_M_cur = _M_next.begin();
+	if (_M_cur != ranges::end(_M_parent->_M_base))
+	  {
+	    _M_cur = _M_next.end();
+	    if (_M_cur == ranges::end(_M_parent->_M_base))
+	      {
+		_M_trailing_empty = true;
+		_M_next = {_M_cur, _M_cur};
+	      }
+	    else
+	      _M_next = _M_parent->_M_find_next(_M_cur);
+	  }
+	else
+	  _M_trailing_empty = false;
+	return *this;
+      }
+
+      constexpr _Iterator
+      operator++(int)
+      {
+	auto __tmp = *this;
+	++*this;
+	return __tmp;
+      }
+
+      friend constexpr bool
+      operator==(const _Iterator& __x, const _Iterator& __y)
+      {
+	return __x._M_cur == __y._M_cur
+	  && __x._M_trailing_empty == __y._M_trailing_empty;
+      }
+
+      friend struct _Sentinel;
+    };
+
+    struct _Sentinel
+    {
+    private:
+      sentinel_t<_Vp> _M_end = sentinel_t<_Vp>();
+
+      constexpr bool
+      _M_equal(const _Iterator& __x) const
+      { return __x._M_cur == _M_end && !__x._M_trailing_empty; }
+
+    public:
+      constexpr explicit
+      _Sentinel(split_view* __parent)
+	: _M_end(ranges::end(__parent->_M_base))
+      { }
+
+      friend constexpr bool
+      operator==(const _Iterator& __x, const _Sentinel& __y)
+      { return __y._M_equal(__x); }
+    };
+  };
+
+  template<typename _Range, typename _Pattern>
+    split_view(_Range&&, _Pattern&&)
+      -> split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
+
+  template<forward_range _Range>
+    split_view(_Range&&, range_value_t<_Range>)
+      -> split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
+
+  namespace views
+  {
+    namespace __detail
+    {
+      template<typename _Range, typename _Pattern>
+	concept __can_split_view
+	  = requires { split_view(std::declval<_Range>(), std::declval<_Pattern>()); };
+    } // namespace __detail
+
+    struct _Split : __adaptor::_RangeAdaptor<_Split>
+    {
+      template<viewable_range _Range, typename _Pattern>
+	requires __detail::__can_split_view<_Range, _Pattern>
+	constexpr auto
+	operator()(_Range&& __r, _Pattern&& __f) const
+	{
+	  return split_view(std::forward<_Range>(__r), std::forward<_Pattern>(__f));
+	}
+
+      using _RangeAdaptor<_Split>::operator();
+      static constexpr int _S_arity = 2;
+      template<typename _Pattern>
+	static constexpr bool _S_has_simple_extra_args
+	  = _LazySplit::_S_has_simple_extra_args<_Pattern>;
+    };
+
+    inline constexpr _Split split;
+  } // namespace views
+
   namespace views
   {
     struct _Counted
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
index 5ef7f3f59a7..81f2a62cfaa 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
@@ -42,8 +42,6 @@ test01()
   static_assert(__adaptor_has_simple_extra_args<decltype(views::lazy_split), char>);
   static_assert(!__adaptor_has_simple_extra_args<decltype(views::lazy_split), std::string>);
 
-  // Verify all adaptor closures except for views::lazy_split(pattern) have a simple
-  // operator().
   using views::__adaptor::__closure_has_simple_call_op;
   __closure_has_simple_call_op auto a00 = views::all;
   __closure_has_simple_call_op auto a01 = views::transform(std::identity{});
@@ -57,6 +55,7 @@ test01()
   __closure_has_simple_call_op auto a09 = views::reverse;
   __closure_has_simple_call_op auto a10 = views::keys;
   __closure_has_simple_call_op auto a11 = views::lazy_split(' ');
+  __closure_has_simple_call_op auto a11a = views::split(' ');
   // Verify composition of simple closures is simple.
   __closure_has_simple_call_op auto b
     = (a00 | a01) | (a02 | a03) | (a04 | a05 | a06) | (a07 | a08 | a09 | a10) | a11;
@@ -67,6 +66,12 @@ test01()
   static_assert(!__closure_has_simple_call_op<decltype(a12)>);
   static_assert(!__closure_has_simple_call_op<decltype(a12 | a00)>);
   static_assert(!__closure_has_simple_call_op<decltype(a00 | a12)>);
+
+  // Likewise views::split(non_view_range).
+  auto a12a = views::split(s);
+  static_assert(!__closure_has_simple_call_op<decltype(a12a)>);
+  static_assert(!__closure_has_simple_call_op<decltype(a12a | a00)>);
+  static_assert(!__closure_has_simple_call_op<decltype(a00 | a12a)>);
 }
 
 void
@@ -98,6 +103,13 @@ test02()
   auto a1 = a0 | views::all;
   a1(x); // { dg-error "no match" }
 
+  views::lazy_split(badarg)(x); // { dg-error "deleted function" }
+  (views::lazy_split(badarg) | views::all)(x); // { dg-error "deleted function" }
+  auto a0a = views::split(badarg);
+  a0a(x); // { dg-error "no match" };
+  auto a1a = a0a | views::all;
+  a1a(x); // { dg-error "no match" }
+
   views::take(badarg)(x); // { dg-error "deleted" }
   views::drop(badarg)(x); // { dg-error "deleted" }
   (views::take(badarg) | views::all)(x); // { dg-error "deleted" }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
new file mode 100644
index 00000000000..9e6726cd07f
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
@@ -0,0 +1,196 @@
+// Copyright (C) 2020-2021 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <string>
+#include <string_view>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+using __gnu_test::test_range;
+using __gnu_test::forward_iterator_wrapper;
+using __gnu_test::input_iterator_wrapper;
+
+namespace ranges = std::ranges;
+namespace views = std::ranges::views;
+
+using namespace std::literals;
+
+void
+test01()
+{
+  auto from_chars = [] (auto v) {
+    return std::stoi(std::string(v.data(), v.data() + v.size()));
+  };
+  auto ints = "1.2.3.4"sv
+    | views::split('.')
+    | views::transform(from_chars);
+  VERIFY( ranges::equal(ints, (int[]){1,2,3,4}) );
+}
+
+// The following testcases were adapted from lazy_split.cc.
+namespace from_lazy_split_cc
+{
+void
+test01()
+{
+  auto x = "the  quick  brown  fox"sv;
+  auto p = std::string{"  "};
+  auto v = x | views::split(views::all(p)); // views::all is needed here after P2281.
+  auto i = v.begin();
+  VERIFY( ranges::equal(*i++, "the"sv) );
+  VERIFY( ranges::equal(*i++, "quick"sv) );
+  VERIFY( ranges::equal(*i++, "brown"sv) );
+  VERIFY( ranges::equal(*i++, "fox"sv) );
+  VERIFY( i == v.end() );
+}
+
+void
+test02()
+{
+  auto x = "the quick brown fox"sv;
+  auto v = x | views::split(' ');
+  auto i = v.begin();
+  VERIFY( ranges::equal(*i++, "the"sv) );
+  VERIFY( ranges::equal(*i++, "quick"sv) );
+  VERIFY( ranges::equal(*i++, "brown"sv) );
+  VERIFY( ranges::equal(*i++, "fox"sv) );
+  VERIFY( i == v.end() );
+}
+
+void
+test03()
+{
+  char x[] = "the quick brown fox";
+  test_range<char, forward_iterator_wrapper> rx(x, x+sizeof(x)-1);
+  auto v = rx | views::split(' ');
+  auto i = v.begin();
+  VERIFY( ranges::equal(*i++, "the"sv) );
+  VERIFY( ranges::equal(*i++, "quick"sv) );
+  VERIFY( ranges::equal(*i++, "brown"sv) );
+  VERIFY( ranges::equal(*i++, "fox"sv) );
+  VERIFY( i == v.end() );
+}
+
+void
+test04()
+{
+  auto x = "the  quick  brown  fox"sv;
+  std::initializer_list<char> p = {' ', ' '};
+  static_assert(!ranges::view<decltype(p)>);
+  static_assert(std::same_as<decltype(p | views::all),
+			     ranges::ref_view<decltype(p)>>);
+  auto v = x | views::split(views::all(p)); // views::all is needed here after P2281.
+  auto i = v.begin();
+  VERIFY( ranges::equal(*i++, "the"sv) );
+  VERIFY( ranges::equal(*i++, "quick"sv) );
+  VERIFY( ranges::equal(*i++, "brown"sv) );
+  VERIFY( ranges::equal(*i++, "fox"sv) );
+  VERIFY( i == v.end() );
+}
+
+void
+test05()
+{
+  auto as_string = [](ranges::view auto rng) {
+    auto in = rng | views::common;
+    return std::string(in.begin(), in.end());
+  };
+  std::string str
+    = "Now is the time for all good men to come to the aid of their county.";
+  auto rng
+    = str | views::split(' ') | views::transform(as_string) | views::common;
+  std::vector<std::string> words(rng.begin(), rng.end());
+  auto not_space_p = [](char c) { return c != ' '; };
+  VERIFY( ranges::equal(words | views::join,
+			str | views::filter(not_space_p)) );
+}
+
+template<auto split = views::split>
+void
+test06()
+{
+  // Verify SFINAE behavior.
+  std::string s, p;
+  static_assert(!requires { split(); });
+  static_assert(!requires { split(s, p, 0); });
+  static_assert(!requires { split(p)(); });
+  static_assert(!requires { s | split; });
+
+  static_assert(!requires { s | split(p); });
+  static_assert(!requires { split(p)(s); });
+  static_assert(!requires { s | (split(p) | views::all); });
+  static_assert(!requires { (split(p) | views::all)(s); });
+
+  static_assert(requires { s | split(views::all(p)); });
+  static_assert(requires { split(views::all(p))(s); });
+  static_assert(requires { s | (split(views::all(p)) | views::all); });
+  static_assert(requires { (split(views::all(p)) | views::all)(s); });
+
+  auto adapt = split(p);
+  static_assert(requires { s | adapt; });
+  static_assert(requires { adapt(s); });
+
+  auto adapt2 = split(p) | views::all;
+  static_assert(requires { s | adapt2; });
+  static_assert(requires { adapt2(s); });
+}
+
+void
+test10()
+{
+  // LWG 3505
+  auto to_string = [] (auto r) {
+    return std::string(r.begin(), ranges::next(r.begin(), r.end()));
+  };
+  auto v = "xxyx"sv | views::split("xy"sv) | views::transform(to_string);
+  VERIFY( ranges::equal(v, (std::string_view[]){"x", "x"}) );
+}
+
+void
+test11()
+{
+  // LWG 3478
+  static_assert(ranges::distance(views::split("text"sv, "text"sv)) == 2);
+  static_assert(ranges::distance(views::split(" text "sv, ' ')) == 3);
+  static_assert(ranges::distance(views::split(" t e x t "sv, ' ')) == 6);
+  static_assert(ranges::distance(views::split("  text  "sv, "  "sv)) == 3);
+  static_assert(ranges::distance(views::split("  text    "sv, "  "sv)) == 4);
+  static_assert(ranges::distance(views::split("  text     "sv, "  "sv)) == 4);
+  static_assert(ranges::distance(views::split("t"sv, 't')) == 2);
+  static_assert(ranges::distance(views::split("text"sv, ""sv)) == 4);
+}
+} // namespace from_lazy_split_cc
+
+int
+main()
+{
+  test01();
+
+  from_lazy_split_cc::test01();
+  from_lazy_split_cc::test02();
+  from_lazy_split_cc::test03();
+  from_lazy_split_cc::test04();
+  from_lazy_split_cc::test05();
+  from_lazy_split_cc::test06();
+  from_lazy_split_cc::test10();
+  from_lazy_split_cc::test11();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/p2325.cc b/libstdc++-v3/testsuite/std/ranges/p2325.cc
index 4d075409026..d2ebe9af863 100644
--- a/libstdc++-v3/testsuite/std/ranges/p2325.cc
+++ b/libstdc++-v3/testsuite/std/ranges/p2325.cc
@@ -124,6 +124,20 @@ test08()
   static_assert(default_initializable<type4>);
 }
 
+void
+test08a()
+{
+  // Verify split_view is conditionally default constructible.
+  using type1 = ranges::split_view<ranges::ref_view<int[2]>, ranges::single_view<int>>;
+  static_assert(!default_initializable<type1>);
+  using type2 = ranges::split_view<ranges::single_view<int>, ranges::ref_view<int[2]>>;
+  static_assert(!default_initializable<type2>);
+  using type3 = ranges::split_view<ranges::ref_view<int[2]>, ranges::ref_view<int[2]>>;
+  static_assert(!default_initializable<type3>);
+  using type4 = ranges::split_view<ranges::single_view<int>, ranges::single_view<int>>;
+  static_assert(default_initializable<type4>);
+}
+
 void
 test09()
 {
diff --git a/libstdc++-v3/testsuite/std/ranges/p2367.cc b/libstdc++-v3/testsuite/std/ranges/p2367.cc
index 5228b021602..70a0304593f 100644
--- a/libstdc++-v3/testsuite/std/ranges/p2367.cc
+++ b/libstdc++-v3/testsuite/std/ranges/p2367.cc
@@ -45,4 +45,5 @@ test01()
 
   // Verify changes to views::lazy_split.
   auto v6 = views::lazy_split(x, 5u);
+  auto v7 = views::split(x, 5u);
 }
-- 
2.32.0.93.g670b81a890


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 2/5] libstdc++: Move ranges algos used by <ranges> into ranges_util.h
  2021-06-17 15:22 ` [PATCH 2/5] libstdc++: Move ranges algos used by <ranges> into ranges_util.h Patrick Palka
@ 2021-06-17 18:14   ` Jonathan Wakely
  0 siblings, 0 replies; 11+ messages in thread
From: Jonathan Wakely @ 2021-06-17 18:14 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches, libstdc++

On Thu, 17 Jun 2021, 16:47 Patrick Palka via Libstdc++, <
libstdc++@gcc.gnu.org> wrote:

> The <ranges> header defines simplified copies of some ranges algorithms
> in order to avoid including the entirety of ranges_algo.h.  A subsequent
> patch is going to want to use ranges::search in <ranges> as well, but
> that algorithm is more complicated compared to the other copied ones.
>
> So rather than additionally copying ranges::search into <ranges>, this
> patch splits out all the ranges algos used by <ranges> (including
> ranges::search) from ranges_algo.h to ranges_util.h, and deletes the
> simplified copies in <ranges>.  This seems like the best place for
> these algorithms, as ranges_util.h is included only from <ranges> and
> ranges_algo.h.
>


OK, thanks.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views
  2021-06-17 15:22 [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Patrick Palka
                   ` (3 preceding siblings ...)
  2021-06-17 15:22 ` [PATCH 5/5] libstdc++: Implement new views::split " Patrick Palka
@ 2021-06-17 18:19 ` Jonathan Wakely
  2021-06-17 18:28   ` Jonathan Wakely
  4 siblings, 1 reply; 11+ messages in thread
From: Jonathan Wakely @ 2021-06-17 18:19 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches, libstdc++

On Thu, 17 Jun 2021, 16:50 Patrick Palka via Libstdc++, <
libstdc++@gcc.gnu.org> wrote:

> This implements the wording changes of P2325R3 "Views should not be
> required to be default constructible".  Changes are relatively
> straightforward, besides perhaps those to __box (which now stands
> for copyable-box instead of semiregular-box) and __non_propagating_cache.
>
> For __box, this patch implements the recommended practice to also avoid
> std::optional when the boxed type is nothrow_move/copy_constructible.
>
> For __non_propagating_cache, now that it's used by split_view::_M_current,
> we need to add assignment from a value of the underlying type to the
> subset of the std::optional API implemented for the cache (needed by
> split_view::begin()).  Hence the new __non_propagating_cache::operator=
> overload.
>
> While we're changing __box, this fixes the undesirable list-init in
> the constuctors of the partial specialization as reported in PR100475
> comment #7.



As I said on IRC, I'm not sure why the defaulted default constructors need
to be constrained (rather than just letting them get deleted) but the patch
is OK.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views
  2021-06-17 18:19 ` [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Jonathan Wakely
@ 2021-06-17 18:28   ` Jonathan Wakely
  0 siblings, 0 replies; 11+ messages in thread
From: Jonathan Wakely @ 2021-06-17 18:28 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches, libstdc++

On Thu, 17 Jun 2021, 19:19 Jonathan Wakely, <jwakely.gcc@gmail.com> wrote:

>
>
> On Thu, 17 Jun 2021, 16:50 Patrick Palka via Libstdc++, <
> libstdc++@gcc.gnu.org> wrote:
>
>> This implements the wording changes of P2325R3 "Views should not be
>> required to be default constructible".  Changes are relatively
>> straightforward, besides perhaps those to __box (which now stands
>> for copyable-box instead of semiregular-box) and __non_propagating_cache.
>>
>> For __box, this patch implements the recommended practice to also avoid
>> std::optional when the boxed type is nothrow_move/copy_constructible.
>>
>> For __non_propagating_cache, now that it's used by split_view::_M_current,
>> we need to add assignment from a value of the underlying type to the
>> subset of the std::optional API implemented for the cache (needed by
>> split_view::begin()).  Hence the new __non_propagating_cache::operator=
>> overload.
>>
>> While we're changing __box, this fixes the undesirable list-init in
>> the constuctors of the partial specialization as reported in PR100475
>> comment #7.
>
>
>
> As I said on IRC, I'm not sure why the defaulted default constructors need
> to be constrained (rather than just letting them get deleted) but the patch
> is OK.
>

Because the default member initializers are in the immediate context, so
the constructor would look valid to is_default_constructible, and only get
deleted when instantiated. (Thanks to Barry for reminding me of that.)

>

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 3/5] libstdc++: Rename views::split to views::lazy_split as per P2210
  2021-06-17 15:22 ` [PATCH 3/5] libstdc++: Rename views::split to views::lazy_split as per P2210 Patrick Palka
@ 2021-06-18 21:56   ` Jonathan Wakely
  0 siblings, 0 replies; 11+ messages in thread
From: Jonathan Wakely @ 2021-06-18 21:56 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc Patches, libstdc++

On Thu, 17 Jun 2021 at 16:55, Patrick Palka via Libstdc++
<libstdc++@gcc.gnu.org> wrote:
>
> This mostly mechanical patch performs the renaming part of P2210R3
> "Superior string splitting".  It also defines _InnerIter::base()
> overloads.
>
> libstdc++-v3/ChangeLog:
>
>         * include/std/ranges: Rename views::split to views::lazy_split,
>         split_view to lazy_split_view, etc. throughout.
>         (lazy_split_view::_InnerIter::base): Define as per P2210.
>         * testsuite/std/ranges/*: Likewise.

The base() member could be noexcept, but that can be added later.

OK.


> ---
>  libstdc++-v3/include/std/ranges               | 68 +++++++++++--------
>  .../testsuite/std/ranges/adaptors/100479.cc   |  2 +-
>  .../testsuite/std/ranges/adaptors/100577.cc   | 20 +++---
>  .../testsuite/std/ranges/adaptors/join.cc     |  2 +-
>  .../adaptors/{split.cc => lazy_split.cc}      | 54 +++++++--------
>  .../{split_neg.cc => lazy_split_neg.cc}       |  6 +-
>  .../testsuite/std/ranges/adaptors/p2281.cc    | 18 ++---
>  .../testsuite/std/ranges/adaptors/sizeof.cc   |  2 +-
>  libstdc++-v3/testsuite/std/ranges/p2259.cc    |  6 +-
>  libstdc++-v3/testsuite/std/ranges/p2325.cc    | 10 +--
>  libstdc++-v3/testsuite/std/ranges/p2367.cc    |  4 +-
>  11 files changed, 100 insertions(+), 92 deletions(-)
>  rename libstdc++-v3/testsuite/std/ranges/adaptors/{split.cc => lazy_split.cc} (76%)
>  rename libstdc++-v3/testsuite/std/ranges/adaptors/{split_neg.cc => lazy_split_neg.cc} (79%)
>
> diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
> index f93a880ff8a..cc1ef112ff1 100644
> --- a/libstdc++-v3/include/std/ranges
> +++ b/libstdc++-v3/include/std/ranges
> @@ -2826,19 +2826,19 @@ namespace views::__adaptor
>         && (remove_reference_t<_Range>::size() <= 1);
>
>      template<typename _Base>
> -      struct __split_view_outer_iter_cat
> +      struct __lazy_split_view_outer_iter_cat
>        { };
>
>      template<forward_range _Base>
> -      struct __split_view_outer_iter_cat<_Base>
> +      struct __lazy_split_view_outer_iter_cat<_Base>
>        { using iterator_category = input_iterator_tag; };
>
>      template<typename _Base>
> -      struct __split_view_inner_iter_cat
> +      struct __lazy_split_view_inner_iter_cat
>        { };
>
>      template<forward_range _Base>
> -      struct __split_view_inner_iter_cat<_Base>
> +      struct __lazy_split_view_inner_iter_cat<_Base>
>        {
>        private:
>         static constexpr auto
> @@ -2860,7 +2860,7 @@ namespace views::__adaptor
>        && indirectly_comparable<iterator_t<_Vp>, iterator_t<_Pattern>,
>                                ranges::equal_to>
>        && (forward_range<_Vp> || __detail::__tiny_range<_Pattern>)
> -    class split_view : public view_interface<split_view<_Vp, _Pattern>>
> +    class lazy_split_view : public view_interface<lazy_split_view<_Vp, _Pattern>>
>      {
>      private:
>        template<bool _Const>
> @@ -2871,17 +2871,17 @@ namespace views::__adaptor
>
>        template<bool _Const>
>         struct _OuterIter
> -         : __detail::__split_view_outer_iter_cat<_Base<_Const>>
> +         : __detail::__lazy_split_view_outer_iter_cat<_Base<_Const>>
>         {
>         private:
> -         using _Parent = __detail::__maybe_const_t<_Const, split_view>;
> -         using _Base = split_view::_Base<_Const>;
> +         using _Parent = __detail::__maybe_const_t<_Const, lazy_split_view>;
> +         using _Base = lazy_split_view::_Base<_Const>;
>
>           constexpr bool
>           __at_end() const
>           { return __current() == ranges::end(_M_parent->_M_base); }
>
> -         // [range.split.outer] p1
> +         // [range.lazy.split.outer] p1
>           //  Many of the following specifications refer to the notional member
>           //  current of outer-iterator.  current is equivalent to current_ if
>           //  V models forward_range, and parent_->current_ otherwise.
> @@ -2914,7 +2914,7 @@ namespace views::__adaptor
>           using iterator_concept = conditional_t<forward_range<_Base>,
>                                                  forward_iterator_tag,
>                                                  input_iterator_tag>;
> -         // iterator_category defined in __split_view_outer_iter_cat
> +         // iterator_category defined in __lazy_split_view_outer_iter_cat
>           using difference_type = range_difference_t<_Base>;
>
>           struct value_type : view_interface<value_type>
> @@ -2974,7 +2974,7 @@ namespace views::__adaptor
>           operator++()
>           {
>             // _GLIBCXX_RESOLVE_LIB_DEFECTS
> -           // 3505. split_view::outer-iterator::operator++ misspecified
> +           // 3505. lazy_split_view::outer-iterator::operator++ misspecified
>             const auto __end = ranges::end(_M_parent->_M_base);
>             if (__current() == __end)
>               return *this;
> @@ -3030,10 +3030,10 @@ namespace views::__adaptor
>
>        template<bool _Const>
>         struct _InnerIter
> -         : __detail::__split_view_inner_iter_cat<_Base<_Const>>
> +         : __detail::__lazy_split_view_inner_iter_cat<_Base<_Const>>
>         {
>         private:
> -         using _Base = split_view::_Base<_Const>;
> +         using _Base = lazy_split_view::_Base<_Const>;
>
>           constexpr bool
>           __at_end() const
> @@ -3081,7 +3081,7 @@ namespace views::__adaptor
>         public:
>           using iterator_concept
>             = typename _OuterIter<_Const>::iterator_concept;
> -         // iterator_category defined in __split_view_inner_iter_cat
> +         // iterator_category defined in __lazy_split_view_inner_iter_cat
>           using value_type = range_value_t<_Base>;
>           using difference_type = range_difference_t<_Base>;
>
> @@ -3092,6 +3092,14 @@ namespace views::__adaptor
>             : _M_i(std::move(__i))
>           { }
>
> +         constexpr iterator_t<_Base>
> +         base() const& requires copyable<iterator_t<_Base>>
> +         { return _M_i_current(); }
> +
> +         constexpr iterator_t<_Base>
> +         base() &&
> +         { return std::move(_M_i_current()); }
> +
>           constexpr decltype(auto)
>           operator*() const
>           { return *_M_i_current(); }
> @@ -3151,12 +3159,12 @@ namespace views::__adaptor
>
>
>      public:
> -      split_view() requires (default_initializable<_Vp>
> -                            && default_initializable<_Pattern>)
> +      lazy_split_view() requires (default_initializable<_Vp>
> +                                 && default_initializable<_Pattern>)
>         = default;
>
>        constexpr
> -      split_view(_Vp __base, _Pattern __pattern)
> +      lazy_split_view(_Vp __base, _Pattern __pattern)
>         : _M_pattern(std::move(__pattern)), _M_base(std::move(__base))
>        { }
>
> @@ -3164,7 +3172,7 @@ namespace views::__adaptor
>         requires constructible_from<_Vp, views::all_t<_Range>>
>           && constructible_from<_Pattern, single_view<range_value_t<_Range>>>
>         constexpr
> -       split_view(_Range&& __r, range_value_t<_Range> __e)
> +       lazy_split_view(_Range&& __r, range_value_t<_Range> __e)
>           : _M_pattern(views::single(std::move(__e))),
>             _M_base(views::all(std::forward<_Range>(__r)))
>         { }
> @@ -3216,35 +3224,35 @@ namespace views::__adaptor
>      };
>
>    template<typename _Range, typename _Pattern>
> -    split_view(_Range&&, _Pattern&&)
> -      -> split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
> +    lazy_split_view(_Range&&, _Pattern&&)
> +      -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
>
>    template<input_range _Range>
> -    split_view(_Range&&, range_value_t<_Range>)
> -      -> split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
> +    lazy_split_view(_Range&&, range_value_t<_Range>)
> +      -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
>
>    namespace views
>    {
>      namespace __detail
>      {
>        template<typename _Range, typename _Pattern>
> -       concept __can_split_view
> -         = requires { split_view(std::declval<_Range>(), std::declval<_Pattern>()); };
> +       concept __can_lazy_split_view
> +         = requires { lazy_split_view(std::declval<_Range>(), std::declval<_Pattern>()); };
>      } // namespace __detail
>
> -    struct _Split : __adaptor::_RangeAdaptor<_Split>
> +    struct _LazySplit : __adaptor::_RangeAdaptor<_LazySplit>
>      {
>        template<viewable_range _Range, typename _Pattern>
> -       requires __detail::__can_split_view<_Range, _Pattern>
> +       requires __detail::__can_lazy_split_view<_Range, _Pattern>
>         constexpr auto
>         operator()(_Range&& __r, _Pattern&& __f) const
>         {
> -         return split_view(std::forward<_Range>(__r), std::forward<_Pattern>(__f));
> +         return lazy_split_view(std::forward<_Range>(__r), std::forward<_Pattern>(__f));
>         }
>
> -      using _RangeAdaptor<_Split>::operator();
> +      using _RangeAdaptor<_LazySplit>::operator();
>        static constexpr int _S_arity = 2;
> -      // The pattern argument of views::split is not always simple -- it can be
> +      // The pattern argument of views::lazy_split is not always simple -- it can be
>        // a non-view range, the value category of which affects whether the call
>        // is well-formed.  But a scalar or a view pattern argument is surely
>        // simple.
> @@ -3254,7 +3262,7 @@ namespace views::__adaptor
>                                       && copy_constructible<_Pattern>);
>      };
>
> -    inline constexpr _Split split;
> +    inline constexpr _LazySplit lazy_split;
>    } // namespace views
>
>    namespace views
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc
> index ba10b7baf3f..9899ff92c0b 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc
> @@ -90,7 +90,7 @@ test03()
>    // Propagating cached iterators during copy/move would cause these asserts
>    // to fail here.
>    auto v = views::single(1)
> -    | views::split(1)
> +    | views::lazy_split(1)
>      | views::drop(0)
>      | views::drop_while([](auto) { return false; })
>      | views::filter([](auto) { return true; });
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
> index 06be4980ddb..5ef7f3f59a7 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
> @@ -38,11 +38,11 @@ test01()
>    static_assert(__adaptor_has_simple_extra_args<decltype(views::take), int>);
>    static_assert(__adaptor_has_simple_extra_args<decltype(views::take_while), identity>);
>    static_assert(__adaptor_has_simple_extra_args<decltype(views::drop_while), identity>);
> -  static_assert(__adaptor_has_simple_extra_args<decltype(views::split), std::string_view>);
> -  static_assert(__adaptor_has_simple_extra_args<decltype(views::split), char>);
> -  static_assert(!__adaptor_has_simple_extra_args<decltype(views::split), std::string>);
> +  static_assert(__adaptor_has_simple_extra_args<decltype(views::lazy_split), std::string_view>);
> +  static_assert(__adaptor_has_simple_extra_args<decltype(views::lazy_split), char>);
> +  static_assert(!__adaptor_has_simple_extra_args<decltype(views::lazy_split), std::string>);
>
> -  // Verify all adaptor closures except for views::split(pattern) have a simple
> +  // Verify all adaptor closures except for views::lazy_split(pattern) have a simple
>    // operator().
>    using views::__adaptor::__closure_has_simple_call_op;
>    __closure_has_simple_call_op auto a00 = views::all;
> @@ -56,14 +56,14 @@ test01()
>    __closure_has_simple_call_op auto a08 = views::common;
>    __closure_has_simple_call_op auto a09 = views::reverse;
>    __closure_has_simple_call_op auto a10 = views::keys;
> -  __closure_has_simple_call_op auto a11 = views::split(' ');
> +  __closure_has_simple_call_op auto a11 = views::lazy_split(' ');
>    // Verify composition of simple closures is simple.
>    __closure_has_simple_call_op auto b
>      = (a00 | a01) | (a02 | a03) | (a04 | a05 | a06) | (a07 | a08 | a09 | a10) | a11;
>
> -  // Verify views::split(non_view_range) is an exception.
> +  // Verify views::lazy_split(non_view_range) is an exception.
>    extern std::string s;
> -  auto a12 = views::split(s);
> +  auto a12 = views::lazy_split(s);
>    static_assert(!__closure_has_simple_call_op<decltype(a12)>);
>    static_assert(!__closure_has_simple_call_op<decltype(a12 | a00)>);
>    static_assert(!__closure_has_simple_call_op<decltype(a00 | a12)>);
> @@ -91,9 +91,9 @@ test02()
>    // implemented using a fallback deleted overload, so when a call is
>    // ill-formed overload resolution succeeds but selects the deleted overload
>    // (but only when the closure is invoked as an rvalue).
> -  views::split(badarg)(x); // { dg-error "deleted function" }
> -  (views::split(badarg) | views::all)(x); // { dg-error "deleted function" }
> -  auto a0 = views::split(badarg);
> +  views::lazy_split(badarg)(x); // { dg-error "deleted function" }
> +  (views::lazy_split(badarg) | views::all)(x); // { dg-error "deleted function" }
> +  auto a0 = views::lazy_split(badarg);
>    a0(x); // { dg-error "no match" };
>    auto a1 = a0 | views::all;
>    a1(x); // { dg-error "no match" }
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
> index d774e8d9385..50af3fdf729 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
> @@ -93,7 +93,7 @@ test05()
>  {
>    using namespace std::literals;
>    std::vector<std::string> x = {"the", " ", "quick", " ", "brown", " ", "fox"};
> -  auto v = x | views::join | views::split(' ');
> +  auto v = x | views::join | views::lazy_split(' ');
>    auto i = v.begin();
>    VERIFY( ranges::equal(*i++, "the"sv) );
>    VERIFY( ranges::equal(*i++, "quick"sv) );
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
> similarity index 76%
> rename from libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
> rename to libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
> index 9d2cfa8632a..12844525d86 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
> @@ -39,7 +39,7 @@ test01()
>  {
>    auto x = "the  quick  brown  fox"sv;
>    auto p = std::string{"  "};
> -  auto v = x | views::split(views::all(p)); // views::all is needed here after P2281.
> +  auto v = x | views::lazy_split(views::all(p)); // views::all is needed here after P2281.
>    auto i = v.begin();
>    VERIFY( ranges::equal(*i++, "the"sv) );
>    VERIFY( ranges::equal(*i++, "quick"sv) );
> @@ -52,7 +52,7 @@ void
>  test02()
>  {
>    auto x = "the quick brown fox"sv;
> -  auto v = x | views::split(' ');
> +  auto v = x | views::lazy_split(' ');
>    auto i = v.begin();
>    VERIFY( ranges::equal(*i++, "the"sv) );
>    VERIFY( ranges::equal(*i++, "quick"sv) );
> @@ -66,7 +66,7 @@ test03()
>  {
>    char x[] = "the quick brown fox";
>    test_range<char, forward_iterator_wrapper> rx(x, x+sizeof(x)-1);
> -  auto v = rx | views::split(' ');
> +  auto v = rx | views::lazy_split(' ');
>    auto i = v.begin();
>    VERIFY( ranges::equal(*i++, "the"sv) );
>    VERIFY( ranges::equal(*i++, "quick"sv) );
> @@ -83,7 +83,7 @@ test04()
>    static_assert(!ranges::view<decltype(p)>);
>    static_assert(std::same_as<decltype(p | views::all),
>                              ranges::ref_view<decltype(p)>>);
> -  auto v = x | views::split(views::all(p)); // views::all is needed here after P2281.
> +  auto v = x | views::lazy_split(views::all(p)); // views::all is needed here after P2281.
>    auto i = v.begin();
>    VERIFY( ranges::equal(*i++, "the"sv) );
>    VERIFY( ranges::equal(*i++, "quick"sv) );
> @@ -102,7 +102,7 @@ test05()
>    std::string str
>      = "Now is the time for all good men to come to the aid of their county.";
>    auto rng
> -    = str | views::split(' ') | views::transform(as_string) | views::common;
> +    = str | views::lazy_split(' ') | views::transform(as_string) | views::common;
>    std::vector<std::string> words(rng.begin(), rng.end());
>    auto not_space_p = [](char c) { return c != ' '; };
>    VERIFY( ranges::equal(words | views::join,
> @@ -113,7 +113,7 @@ void
>  test06()
>  {
>    std::string str = "hello world";
> -  auto v = str | views::transform(std::identity{}) | views::split(' ');
> +  auto v = str | views::transform(std::identity{}) | views::lazy_split(' ');
>
>    // Verify that _Iterator<false> is implicitly convertible to _Iterator<true>.
>    static_assert(!std::same_as<decltype(ranges::begin(v)),
> @@ -126,7 +126,7 @@ void
>  test07()
>  {
>    char str[] = "banana split";
> -  auto split = str | views::split(' ');
> +  auto split = str | views::lazy_split(' ');
>    auto val = *split.begin();
>    auto b = val.begin();
>    auto b2 = b++;
> @@ -139,7 +139,7 @@ test08()
>  {
>    char x[] = "the quick brown fox";
>    test_range<char, input_iterator_wrapper> rx(x, x+sizeof(x)-1);
> -  auto v = rx | views::split(' ');
> +  auto v = rx | views::lazy_split(' ');
>    auto i = v.begin();
>    VERIFY( ranges::equal(*i, "the"sv) );
>    ++i;
> @@ -152,32 +152,32 @@ test08()
>    VERIFY( i == v.end() );
>  }
>
> -template<auto split = views::split>
> +template<auto lazy_split = views::lazy_split>
>  void
>  test09()
>  {
>    // Verify SFINAE behavior.
>    std::string s, p;
> -  static_assert(!requires { split(); });
> -  static_assert(!requires { split(s, p, 0); });
> -  static_assert(!requires { split(p)(); });
> -  static_assert(!requires { s | split; });
> -
> -  static_assert(!requires { s | split(p); });
> -  static_assert(!requires { split(p)(s); });
> -  static_assert(!requires { s | (split(p) | views::all); });
> -  static_assert(!requires { (split(p) | views::all)(s); });
> -
> -  static_assert(requires { s | split(views::all(p)); });
> -  static_assert(requires { split(views::all(p))(s); });
> -  static_assert(requires { s | (split(views::all(p)) | views::all); });
> -  static_assert(requires { (split(views::all(p)) | views::all)(s); });
> -
> -  auto adapt = split(p);
> +  static_assert(!requires { lazy_split(); });
> +  static_assert(!requires { lazy_split(s, p, 0); });
> +  static_assert(!requires { lazy_split(p)(); });
> +  static_assert(!requires { s | lazy_split; });
> +
> +  static_assert(!requires { s | lazy_split(p); });
> +  static_assert(!requires { lazy_split(p)(s); });
> +  static_assert(!requires { s | (lazy_split(p) | views::all); });
> +  static_assert(!requires { (lazy_split(p) | views::all)(s); });
> +
> +  static_assert(requires { s | lazy_split(views::all(p)); });
> +  static_assert(requires { lazy_split(views::all(p))(s); });
> +  static_assert(requires { s | (lazy_split(views::all(p)) | views::all); });
> +  static_assert(requires { (lazy_split(views::all(p)) | views::all)(s); });
> +
> +  auto adapt = lazy_split(p);
>    static_assert(requires { s | adapt; });
>    static_assert(requires { adapt(s); });
>
> -  auto adapt2 = split(p) | views::all;
> +  auto adapt2 = lazy_split(p) | views::all;
>    static_assert(requires { s | adapt2; });
>    static_assert(requires { adapt2(s); });
>  }
> @@ -189,7 +189,7 @@ test10()
>    auto to_string = [] (auto r) {
>      return std::string(r.begin(), ranges::next(r.begin(), r.end()));
>    };
> -  auto v = "xxyx"sv | views::split("xy"sv) | views::transform(to_string);
> +  auto v = "xxyx"sv | views::lazy_split("xy"sv) | views::transform(to_string);
>    VERIFY( ranges::equal(v, (std::string_view[]){"x", "x"}) );
>  }
>
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split_neg.cc
> similarity index 79%
> rename from libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc
> rename to libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split_neg.cc
> index 4229314a9dc..c59f828ee56 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split_neg.cc
> @@ -30,7 +30,7 @@ test01()
>  {
>    using namespace std::literals;
>    auto x = "the  quick  brown  fox"sv;
> -  auto v = views::split(x, std::initializer_list<char>{' ', ' '}); // { dg-error "no match" }
> +  auto v = views::lazy_split(x, std::initializer_list<char>{' ', ' '}); // { dg-error "no match" }
>  }
>
>  void
> @@ -38,8 +38,8 @@ test02()
>  {
>    using namespace std::literals;
>    auto x = "the  quick  brown  fox"sv;
> -  auto v1 = views::split(std::initializer_list<char>{' ', ' '})(x); // { dg-error "deleted" }
> -  auto v2 = x | views::split(std::initializer_list<char>{' ', ' '}); // { dg-error "no match" }
> +  auto v1 = views::lazy_split(std::initializer_list<char>{' ', ' '})(x); // { dg-error "deleted" }
> +  auto v2 = x | views::lazy_split(std::initializer_list<char>{' ', ' '}); // { dg-error "no match" }
>  }
>
>  // { dg-prune-output "in requirements" }
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc
> index c916a5ea8b7..7950c43576d 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc
> @@ -34,7 +34,7 @@ void
>  test01()
>  {
>    auto split_into_strings = [] (auto p) {
> -    return views::split(p) | views::transform([](auto r){
> +    return views::lazy_split(p) | views::transform([](auto r){
>        return std::string(r.begin(), ranges::next(r.begin(), r.end()));
>      });
>    };
> @@ -60,19 +60,19 @@ struct move_only_range
>  template<>
>    inline constexpr bool std::ranges::enable_view<move_only_range> = true;
>
> -template<auto split = views::split>
> +template<auto lazy_split = views::lazy_split>
>  void
>  test02()
>  {
>    std::string_view s;
>    move_only_range p;
> -  static_assert(requires { s | split(std::move(p)); });
> -  static_assert(requires { split(std::move(p))(s); });
> -  static_assert(requires { split(std::move(p)) | views::all; });
> -  static_assert(requires { views::all | split(std::move(p)); });
> -  static_assert(!requires { split(p); });
> -  static_assert(!requires { split(p) | views::all; });
> -  static_assert(!requires { views::all | split(p); });
> +  static_assert(requires { s | lazy_split(std::move(p)); });
> +  static_assert(requires { lazy_split(std::move(p))(s); });
> +  static_assert(requires { lazy_split(std::move(p)) | views::all; });
> +  static_assert(requires { views::all | lazy_split(std::move(p)); });
> +  static_assert(!requires { lazy_split(p); });
> +  static_assert(!requires { lazy_split(p) | views::all; });
> +  static_assert(!requires { views::all | lazy_split(p); });
>  }
>
>  int
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc
> index 80326f8bf21..219e2a61f07 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc
> @@ -46,7 +46,7 @@ static_assert(sizeof(ranges::take_while_view<V, decltype(pred_l)>) == 3*ptr);
>  static_assert(sizeof(ranges::drop_while_view<V, decltype(pred_l)>) == 3*ptr);
>  static_assert(sizeof(ranges::transform_view<V, decltype(func_l)>) == 3*ptr);
>
> -static_assert(sizeof(ranges::split_view<V, std::string_view>) == 4*ptr);
> +static_assert(sizeof(ranges::lazy_split_view<V, std::string_view>) == 4*ptr);
>
>  static_assert
>   (sizeof(ranges::reverse_view<ranges::filter_view<V, decltype(pred_l)>>) == 4*ptr);
> diff --git a/libstdc++-v3/testsuite/std/ranges/p2259.cc b/libstdc++-v3/testsuite/std/ranges/p2259.cc
> index 1b422e44f16..0ec7e21f657 100644
> --- a/libstdc++-v3/testsuite/std/ranges/p2259.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/p2259.cc
> @@ -49,12 +49,12 @@ test01()
>    // Verify the changes to transform_view.
>    only_cxx20_input_range auto v2 = v0 | views::transform([](int& c) -> auto& { return c; });
>
> -  // Verify the changes to split_view.
> -  only_cxx20_input_range auto v3 = v0 | views::split(12);
> +  // Verify the changes to lazy_split_view.
> +  only_cxx20_input_range auto v3 = v0 | views::lazy_split(12);
>    static_assert(only_cxx20_input_range<decltype(*v3.begin())>);
>
>    // Verify the changes to join_view.
> -  only_cxx20_input_range auto v4 = v0 | views::split(12) | views::join;
> +  only_cxx20_input_range auto v4 = v0 | views::lazy_split(12) | views::join;
>
>    // Verify the changes to elements_view.
>    only_cxx20_input_range auto v5
> diff --git a/libstdc++-v3/testsuite/std/ranges/p2325.cc b/libstdc++-v3/testsuite/std/ranges/p2325.cc
> index df6cde29e4d..4d075409026 100644
> --- a/libstdc++-v3/testsuite/std/ranges/p2325.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/p2325.cc
> @@ -113,14 +113,14 @@ test07()
>  void
>  test08()
>  {
> -  // Verify split_view is conditionally default constructible.
> -  using type1 = ranges::split_view<ranges::ref_view<int[2]>, ranges::single_view<int>>;
> +  // Verify lazy_split_view is conditionally default constructible.
> +  using type1 = ranges::lazy_split_view<ranges::ref_view<int[2]>, ranges::single_view<int>>;
>    static_assert(!default_initializable<type1>);
> -  using type2 = ranges::split_view<ranges::single_view<int>, ranges::ref_view<int[2]>>;
> +  using type2 = ranges::lazy_split_view<ranges::single_view<int>, ranges::ref_view<int[2]>>;
>    static_assert(!default_initializable<type2>);
> -  using type3 = ranges::split_view<ranges::ref_view<int[2]>, ranges::ref_view<int[2]>>;
> +  using type3 = ranges::lazy_split_view<ranges::ref_view<int[2]>, ranges::ref_view<int[2]>>;
>    static_assert(!default_initializable<type3>);
> -  using type4 = ranges::split_view<ranges::single_view<int>, ranges::single_view<int>>;
> +  using type4 = ranges::lazy_split_view<ranges::single_view<int>, ranges::single_view<int>>;
>    static_assert(default_initializable<type4>);
>  }
>
> diff --git a/libstdc++-v3/testsuite/std/ranges/p2367.cc b/libstdc++-v3/testsuite/std/ranges/p2367.cc
> index 40875233d88..5228b021602 100644
> --- a/libstdc++-v3/testsuite/std/ranges/p2367.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/p2367.cc
> @@ -43,6 +43,6 @@ test01()
>    // Verify changes to views::drop.
>    auto v5 = views::drop(x, 0ull);
>
> -  // Verify changes to views::split.
> -  auto v6 = views::split(x, 5u);
> +  // Verify changes to views::lazy_split.
> +  auto v6 = views::lazy_split(x, 5u);
>  }
> --
> 2.32.0.93.g670b81a890
>


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 4/5] libstdc++: Implement resolution of LWG 3478 as per P2210
  2021-06-17 15:22 ` [PATCH 4/5] libstdc++: Implement resolution of LWG 3478 " Patrick Palka
@ 2021-06-18 21:57   ` Jonathan Wakely
  0 siblings, 0 replies; 11+ messages in thread
From: Jonathan Wakely @ 2021-06-18 21:57 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc Patches, libstdc++

On Thu, 17 Jun 2021 at 16:51, Patrick Palka via Libstdc++
<libstdc++@gcc.gnu.org> wrote:
>
> This implements the part of P2210R2 "Superior String Splitting" that
> resolves LWG 3478 for split_view (now named lazy_split_view).
>
> libstdc++-v3/ChangeLog:
>
>         * include/std/ranges (lazy_split_view::_OuterIter::__at_end):
>         Check _M_trailing_empty.
>         (lazy_split_view::_OuterIter::_M_trailing_empty): Define this
>         data member.
>         (lazy_split_view::_OuterIter::operator++): Set _M_trailing_empty
>         appropriately.
>         (lazy_split_view::_OuterIter::operator==): Compare
>         _M_trailing_empty.
>         * testsuite/std/ranges/adaptors/100479.cc (test03): Expect two
>         split parts instead of one.
>         * testsuite/std/ranges/adaptors/lazy_split.cc (test11): New test.

OK (for trunk only, I think).



> ---
>  libstdc++-v3/include/std/ranges               | 21 +++++++++++++++----
>  .../testsuite/std/ranges/adaptors/100479.cc   |  6 +++---
>  .../std/ranges/adaptors/lazy_split.cc         | 15 +++++++++++++
>  3 files changed, 35 insertions(+), 7 deletions(-)
>
> diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
> index cc1ef112ff1..78562924bee 100644
> --- a/libstdc++-v3/include/std/ranges
> +++ b/libstdc++-v3/include/std/ranges
> @@ -2879,7 +2879,7 @@ namespace views::__adaptor
>
>           constexpr bool
>           __at_end() const
> -         { return __current() == ranges::end(_M_parent->_M_base); }
> +         { return __current() == ranges::end(_M_parent->_M_base) && !_M_trailing_empty; }
>
>           // [range.lazy.split.outer] p1
>           //  Many of the following specifications refer to the notional member
> @@ -2909,6 +2909,7 @@ namespace views::__adaptor
>           [[no_unique_address]]
>             __detail::__maybe_present_t<forward_range<_Vp>,
>                                         iterator_t<_Base>> _M_current;
> +         bool _M_trailing_empty = false;
>
>         public:
>           using iterator_concept = conditional_t<forward_range<_Base>,
> @@ -2977,7 +2978,10 @@ namespace views::__adaptor
>             // 3505. lazy_split_view::outer-iterator::operator++ misspecified
>             const auto __end = ranges::end(_M_parent->_M_base);
>             if (__current() == __end)
> -             return *this;
> +             {
> +               _M_trailing_empty = false;
> +               return *this;
> +             }
>             const auto [__pbegin, __pend] = subrange{_M_parent->_M_pattern};
>             if (__pbegin == __pend)
>               ++__current();
> @@ -2986,7 +2990,11 @@ namespace views::__adaptor
>                 __current() = ranges::find(std::move(__current()), __end,
>                                            *__pbegin);
>                 if (__current() != __end)
> -                 ++__current();
> +                 {
> +                   ++__current();
> +                   if (__current() == __end)
> +                     _M_trailing_empty = true;
> +                 }
>               }
>             else
>               do
> @@ -2996,6 +3004,8 @@ namespace views::__adaptor
>                   if (__p == __pend)
>                     {
>                       __current() = __b;
> +                     if (__current() == __end)
> +                       _M_trailing_empty = true;
>                       break;
>                     }
>                 } while (++__current() != __end);
> @@ -3018,7 +3028,10 @@ namespace views::__adaptor
>           friend constexpr bool
>           operator==(const _OuterIter& __x, const _OuterIter& __y)
>             requires forward_range<_Base>
> -         { return __x._M_current == __y._M_current; }
> +         {
> +           return __x._M_current == __y._M_current
> +             && __x._M_trailing_empty == __y._M_trailing_empty;
> +         }
>
>           friend constexpr bool
>           operator==(const _OuterIter& __x, default_sentinel_t)
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc
> index 9899ff92c0b..b8c1e6f4f57 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc
> @@ -95,11 +95,11 @@ test03()
>      | views::drop_while([](auto) { return false; })
>      | views::filter([](auto) { return true; });
>    static_assert(ranges::forward_range<decltype(v)>);
> -  VERIFY( ranges::next(v.begin()) == v.end() );
> +  VERIFY( ranges::distance(v) == 2 );
>    auto w = v;
> -  VERIFY( ranges::next(w.begin()) == w.end() );
> +  VERIFY( ranges::distance(v) == 2 );
>    auto z = std::move(w);
> -  VERIFY( ranges::next(z.begin()) == z.end() );
> +  VERIFY( ranges::distance(v) == 2 );
>    return true;
>  }
>
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
> index 12844525d86..133e9a7025b 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc
> @@ -193,6 +193,20 @@ test10()
>    VERIFY( ranges::equal(v, (std::string_view[]){"x", "x"}) );
>  }
>
> +void
> +test11()
> +{
> +  // LWG 3478
> +  static_assert(ranges::distance(views::lazy_split("text"sv, "text"sv)) == 2);
> +  static_assert(ranges::distance(views::lazy_split(" text "sv, ' ')) == 3);
> +  static_assert(ranges::distance(views::lazy_split(" t e x t "sv, ' ')) == 6);
> +  static_assert(ranges::distance(views::lazy_split("  text  "sv, "  "sv)) == 3);
> +  static_assert(ranges::distance(views::lazy_split("  text    "sv, "  "sv)) == 4);
> +  static_assert(ranges::distance(views::lazy_split("  text     "sv, "  "sv)) == 4);
> +  static_assert(ranges::distance(views::lazy_split("t"sv, 't')) == 2);
> +  static_assert(ranges::distance(views::lazy_split("text"sv, ""sv)) == 4);
> +}
> +
>  int
>  main()
>  {
> @@ -206,4 +220,5 @@ main()
>    test08();
>    test09();
>    test10();
> +  test11();
>  }
> --
> 2.32.0.93.g670b81a890
>


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 5/5] libstdc++: Implement new views::split as per P2210
  2021-06-17 15:22 ` [PATCH 5/5] libstdc++: Implement new views::split " Patrick Palka
@ 2021-06-18 21:58   ` Jonathan Wakely
  0 siblings, 0 replies; 11+ messages in thread
From: Jonathan Wakely @ 2021-06-18 21:58 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc Patches, libstdc++

On Thu, 17 Jun 2021 at 16:59, Patrick Palka via Libstdc++
<libstdc++@gcc.gnu.org> wrote:
>
> This implements the new views::split as specified by P2210R2 "Superior
> string splitting".
>
> libstdc++-v3/ChangeLog:
>
>         * include/std/ranges (__non_propagating_cache::operator bool):
>         Define.
>         (split_view): Define as per P2210.
>         (views::__detail::__can_split_view): Define.
>         (views::_Split, views::Split): Define.
>         * testsuite/std/ranges/adaptors/100577.cc (test01, test02):
>         Test views::split.
>         * testsuite/std/ranges/adaptors/split.cc: New test.
>         * testsuite/std/ranges/p2325.cc (test08a): New test.
>         * testsuite/std/ranges/p2367.cc (test01): Test views::split.


OK



> ---
>  libstdc++-v3/include/std/ranges               | 205 ++++++++++++++++++
>  .../testsuite/std/ranges/adaptors/100577.cc   |  16 +-
>  .../testsuite/std/ranges/adaptors/split.cc    | 196 +++++++++++++++++
>  libstdc++-v3/testsuite/std/ranges/p2325.cc    |  14 ++
>  libstdc++-v3/testsuite/std/ranges/p2367.cc    |   1 +
>  5 files changed, 430 insertions(+), 2 deletions(-)
>  create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
>
> diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
> index 78562924bee..42278f128b8 100644
> --- a/libstdc++-v3/include/std/ranges
> +++ b/libstdc++-v3/include/std/ranges
> @@ -1195,6 +1195,10 @@ namespace views::__adaptor
>           return *this;
>         }
>
> +       constexpr explicit
> +       operator bool() const noexcept
> +       { return this->_M_is_engaged(); }
> +
>         constexpr _Tp&
>         operator*() noexcept
>         { return this->_M_get(); }
> @@ -3278,6 +3282,207 @@ namespace views::__adaptor
>      inline constexpr _LazySplit lazy_split;
>    } // namespace views
>
> +  template<forward_range _Vp, forward_range _Pattern>
> +    requires view<_Vp> && view<_Pattern>
> +      && indirectly_comparable<iterator_t<_Vp>, iterator_t<_Pattern>,
> +                              ranges::equal_to>
> +  class split_view : public view_interface<split_view<_Vp, _Pattern>>
> +  {
> +  private:
> +    _Pattern _M_pattern = _Pattern();
> +    __detail::__non_propagating_cache<subrange<iterator_t<_Vp>>> _M_cached_begin;
> +    _Vp _M_base = _Vp();
> +
> +    struct _Iterator;
> +    struct _Sentinel;
> +
> +  public:
> +    split_view() requires (default_initializable<_Vp>
> +                          && default_initializable<_Pattern>)
> +      = default;
> +
> +    constexpr
> +    split_view(_Vp __base, _Pattern __pattern)
> +      : _M_pattern(std::move(__pattern)),
> +       _M_base(std::move(__base))
> +    { }
> +
> +    template<forward_range _Range>
> +      requires constructible_from<_Vp, views::all_t<_Range>>
> +       && constructible_from<_Pattern, single_view<range_value_t<_Range>>>
> +    constexpr
> +    split_view(_Range&& __r, range_value_t<_Range> __e)
> +      : _M_pattern(views::single(__e)),
> +       _M_base(views::all(std::forward<_Range>(__r)))
> +    { }
> +
> +    constexpr _Vp
> +    base() const& requires copyable<_Vp>
> +    { return _M_base; }
> +
> +    constexpr _Vp
> +    base() &&
> +    { return std::move(_M_base); }
> +
> +    constexpr _Iterator
> +    begin()
> +    {
> +      if (!_M_cached_begin)
> +       _M_cached_begin = _M_find_next(ranges::begin(_M_base));
> +      return {this, ranges::begin(_M_base), *_M_cached_begin};
> +    }
> +
> +    constexpr auto
> +    end()
> +    {
> +      if constexpr (common_range<_Vp>)
> +       return _Iterator{this, ranges::end(_M_base), {}};
> +      else
> +       return _Sentinel{this};
> +    }
> +
> +    constexpr subrange<iterator_t<_Vp>>
> +    _M_find_next(iterator_t<_Vp> __it)
> +    {
> +      auto [__b, __e] = ranges::search(subrange(__it, ranges::end(_M_base)), _M_pattern);
> +      if (__b != ranges::end(_M_base) && ranges::empty(_M_pattern))
> +       {
> +         ++__b;
> +         ++__e;
> +       }
> +      return {__b, __e};
> +    }
> +
> +  private:
> +    struct _Iterator
> +    {
> +    private:
> +      split_view* _M_parent = nullptr;
> +      iterator_t<_Vp> _M_cur = iterator_t<_Vp>();
> +      subrange<iterator_t<_Vp>> _M_next = subrange<iterator_t<_Vp>>();
> +      bool _M_trailing_empty = false;
> +
> +    public:
> +      using iterator_concept = forward_iterator_tag;
> +      using iterator_category = input_iterator_tag;
> +      using value_type = subrange<iterator_t<_Vp>>;
> +      using difference_type = range_difference_t<_Vp>;
> +
> +      _Iterator() requires default_initializable<iterator_t<_Vp>> = default;
> +
> +      constexpr
> +      _Iterator(split_view* __parent,
> +               iterator_t<_Vp> __current,
> +               subrange<iterator_t<_Vp>> __next)
> +       : _M_parent(__parent),
> +         _M_cur(std::move(__current)),
> +         _M_next(std::move(__next))
> +      { }
> +
> +      constexpr iterator_t<_Vp>
> +      base() const
> +      { return _M_cur; }
> +
> +      constexpr value_type
> +      operator*() const
> +      { return {_M_cur, _M_next.begin()}; }
> +
> +      constexpr _Iterator&
> +      operator++()
> +      {
> +       _M_cur = _M_next.begin();
> +       if (_M_cur != ranges::end(_M_parent->_M_base))
> +         {
> +           _M_cur = _M_next.end();
> +           if (_M_cur == ranges::end(_M_parent->_M_base))
> +             {
> +               _M_trailing_empty = true;
> +               _M_next = {_M_cur, _M_cur};
> +             }
> +           else
> +             _M_next = _M_parent->_M_find_next(_M_cur);
> +         }
> +       else
> +         _M_trailing_empty = false;
> +       return *this;
> +      }
> +
> +      constexpr _Iterator
> +      operator++(int)
> +      {
> +       auto __tmp = *this;
> +       ++*this;
> +       return __tmp;
> +      }
> +
> +      friend constexpr bool
> +      operator==(const _Iterator& __x, const _Iterator& __y)
> +      {
> +       return __x._M_cur == __y._M_cur
> +         && __x._M_trailing_empty == __y._M_trailing_empty;
> +      }
> +
> +      friend struct _Sentinel;
> +    };
> +
> +    struct _Sentinel
> +    {
> +    private:
> +      sentinel_t<_Vp> _M_end = sentinel_t<_Vp>();
> +
> +      constexpr bool
> +      _M_equal(const _Iterator& __x) const
> +      { return __x._M_cur == _M_end && !__x._M_trailing_empty; }
> +
> +    public:
> +      constexpr explicit
> +      _Sentinel(split_view* __parent)
> +       : _M_end(ranges::end(__parent->_M_base))
> +      { }
> +
> +      friend constexpr bool
> +      operator==(const _Iterator& __x, const _Sentinel& __y)
> +      { return __y._M_equal(__x); }
> +    };
> +  };
> +
> +  template<typename _Range, typename _Pattern>
> +    split_view(_Range&&, _Pattern&&)
> +      -> split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
> +
> +  template<forward_range _Range>
> +    split_view(_Range&&, range_value_t<_Range>)
> +      -> split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>;
> +
> +  namespace views
> +  {
> +    namespace __detail
> +    {
> +      template<typename _Range, typename _Pattern>
> +       concept __can_split_view
> +         = requires { split_view(std::declval<_Range>(), std::declval<_Pattern>()); };
> +    } // namespace __detail
> +
> +    struct _Split : __adaptor::_RangeAdaptor<_Split>
> +    {
> +      template<viewable_range _Range, typename _Pattern>
> +       requires __detail::__can_split_view<_Range, _Pattern>
> +       constexpr auto
> +       operator()(_Range&& __r, _Pattern&& __f) const
> +       {
> +         return split_view(std::forward<_Range>(__r), std::forward<_Pattern>(__f));
> +       }
> +
> +      using _RangeAdaptor<_Split>::operator();
> +      static constexpr int _S_arity = 2;
> +      template<typename _Pattern>
> +       static constexpr bool _S_has_simple_extra_args
> +         = _LazySplit::_S_has_simple_extra_args<_Pattern>;
> +    };
> +
> +    inline constexpr _Split split;
> +  } // namespace views
> +
>    namespace views
>    {
>      struct _Counted
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
> index 5ef7f3f59a7..81f2a62cfaa 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc
> @@ -42,8 +42,6 @@ test01()
>    static_assert(__adaptor_has_simple_extra_args<decltype(views::lazy_split), char>);
>    static_assert(!__adaptor_has_simple_extra_args<decltype(views::lazy_split), std::string>);
>
> -  // Verify all adaptor closures except for views::lazy_split(pattern) have a simple
> -  // operator().
>    using views::__adaptor::__closure_has_simple_call_op;
>    __closure_has_simple_call_op auto a00 = views::all;
>    __closure_has_simple_call_op auto a01 = views::transform(std::identity{});
> @@ -57,6 +55,7 @@ test01()
>    __closure_has_simple_call_op auto a09 = views::reverse;
>    __closure_has_simple_call_op auto a10 = views::keys;
>    __closure_has_simple_call_op auto a11 = views::lazy_split(' ');
> +  __closure_has_simple_call_op auto a11a = views::split(' ');
>    // Verify composition of simple closures is simple.
>    __closure_has_simple_call_op auto b
>      = (a00 | a01) | (a02 | a03) | (a04 | a05 | a06) | (a07 | a08 | a09 | a10) | a11;
> @@ -67,6 +66,12 @@ test01()
>    static_assert(!__closure_has_simple_call_op<decltype(a12)>);
>    static_assert(!__closure_has_simple_call_op<decltype(a12 | a00)>);
>    static_assert(!__closure_has_simple_call_op<decltype(a00 | a12)>);
> +
> +  // Likewise views::split(non_view_range).
> +  auto a12a = views::split(s);
> +  static_assert(!__closure_has_simple_call_op<decltype(a12a)>);
> +  static_assert(!__closure_has_simple_call_op<decltype(a12a | a00)>);
> +  static_assert(!__closure_has_simple_call_op<decltype(a00 | a12a)>);
>  }
>
>  void
> @@ -98,6 +103,13 @@ test02()
>    auto a1 = a0 | views::all;
>    a1(x); // { dg-error "no match" }
>
> +  views::lazy_split(badarg)(x); // { dg-error "deleted function" }
> +  (views::lazy_split(badarg) | views::all)(x); // { dg-error "deleted function" }
> +  auto a0a = views::split(badarg);
> +  a0a(x); // { dg-error "no match" };
> +  auto a1a = a0a | views::all;
> +  a1a(x); // { dg-error "no match" }
> +
>    views::take(badarg)(x); // { dg-error "deleted" }
>    views::drop(badarg)(x); // { dg-error "deleted" }
>    (views::take(badarg) | views::all)(x); // { dg-error "deleted" }
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
> new file mode 100644
> index 00000000000..9e6726cd07f
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
> @@ -0,0 +1,196 @@
> +// Copyright (C) 2020-2021 Free Software Foundation, Inc.
> +//
> +// This file is part of the GNU ISO C++ Library.  This library is free
> +// software; you can redistribute it and/or modify it under the
> +// terms of the GNU General Public License as published by the
> +// Free Software Foundation; either version 3, or (at your option)
> +// any later version.
> +
> +// This library is distributed in the hope that it will be useful,
> +// but WITHOUT ANY WARRANTY; without even the implied warranty of
> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +// GNU General Public License for more details.
> +
> +// You should have received a copy of the GNU General Public License along
> +// with this library; see the file COPYING3.  If not see
> +// <http://www.gnu.org/licenses/>.
> +
> +// { dg-options "-std=gnu++2a" }
> +// { dg-do run { target c++2a } }
> +
> +#include <algorithm>
> +#include <ranges>
> +#include <string>
> +#include <string_view>
> +#include <testsuite_hooks.h>
> +#include <testsuite_iterators.h>
> +
> +using __gnu_test::test_range;
> +using __gnu_test::forward_iterator_wrapper;
> +using __gnu_test::input_iterator_wrapper;
> +
> +namespace ranges = std::ranges;
> +namespace views = std::ranges::views;
> +
> +using namespace std::literals;
> +
> +void
> +test01()
> +{
> +  auto from_chars = [] (auto v) {
> +    return std::stoi(std::string(v.data(), v.data() + v.size()));
> +  };
> +  auto ints = "1.2.3.4"sv
> +    | views::split('.')
> +    | views::transform(from_chars);
> +  VERIFY( ranges::equal(ints, (int[]){1,2,3,4}) );
> +}
> +
> +// The following testcases were adapted from lazy_split.cc.
> +namespace from_lazy_split_cc
> +{
> +void
> +test01()
> +{
> +  auto x = "the  quick  brown  fox"sv;
> +  auto p = std::string{"  "};
> +  auto v = x | views::split(views::all(p)); // views::all is needed here after P2281.
> +  auto i = v.begin();
> +  VERIFY( ranges::equal(*i++, "the"sv) );
> +  VERIFY( ranges::equal(*i++, "quick"sv) );
> +  VERIFY( ranges::equal(*i++, "brown"sv) );
> +  VERIFY( ranges::equal(*i++, "fox"sv) );
> +  VERIFY( i == v.end() );
> +}
> +
> +void
> +test02()
> +{
> +  auto x = "the quick brown fox"sv;
> +  auto v = x | views::split(' ');
> +  auto i = v.begin();
> +  VERIFY( ranges::equal(*i++, "the"sv) );
> +  VERIFY( ranges::equal(*i++, "quick"sv) );
> +  VERIFY( ranges::equal(*i++, "brown"sv) );
> +  VERIFY( ranges::equal(*i++, "fox"sv) );
> +  VERIFY( i == v.end() );
> +}
> +
> +void
> +test03()
> +{
> +  char x[] = "the quick brown fox";
> +  test_range<char, forward_iterator_wrapper> rx(x, x+sizeof(x)-1);
> +  auto v = rx | views::split(' ');
> +  auto i = v.begin();
> +  VERIFY( ranges::equal(*i++, "the"sv) );
> +  VERIFY( ranges::equal(*i++, "quick"sv) );
> +  VERIFY( ranges::equal(*i++, "brown"sv) );
> +  VERIFY( ranges::equal(*i++, "fox"sv) );
> +  VERIFY( i == v.end() );
> +}
> +
> +void
> +test04()
> +{
> +  auto x = "the  quick  brown  fox"sv;
> +  std::initializer_list<char> p = {' ', ' '};
> +  static_assert(!ranges::view<decltype(p)>);
> +  static_assert(std::same_as<decltype(p | views::all),
> +                            ranges::ref_view<decltype(p)>>);
> +  auto v = x | views::split(views::all(p)); // views::all is needed here after P2281.
> +  auto i = v.begin();
> +  VERIFY( ranges::equal(*i++, "the"sv) );
> +  VERIFY( ranges::equal(*i++, "quick"sv) );
> +  VERIFY( ranges::equal(*i++, "brown"sv) );
> +  VERIFY( ranges::equal(*i++, "fox"sv) );
> +  VERIFY( i == v.end() );
> +}
> +
> +void
> +test05()
> +{
> +  auto as_string = [](ranges::view auto rng) {
> +    auto in = rng | views::common;
> +    return std::string(in.begin(), in.end());
> +  };
> +  std::string str
> +    = "Now is the time for all good men to come to the aid of their county.";
> +  auto rng
> +    = str | views::split(' ') | views::transform(as_string) | views::common;
> +  std::vector<std::string> words(rng.begin(), rng.end());
> +  auto not_space_p = [](char c) { return c != ' '; };
> +  VERIFY( ranges::equal(words | views::join,
> +                       str | views::filter(not_space_p)) );
> +}
> +
> +template<auto split = views::split>
> +void
> +test06()
> +{
> +  // Verify SFINAE behavior.
> +  std::string s, p;
> +  static_assert(!requires { split(); });
> +  static_assert(!requires { split(s, p, 0); });
> +  static_assert(!requires { split(p)(); });
> +  static_assert(!requires { s | split; });
> +
> +  static_assert(!requires { s | split(p); });
> +  static_assert(!requires { split(p)(s); });
> +  static_assert(!requires { s | (split(p) | views::all); });
> +  static_assert(!requires { (split(p) | views::all)(s); });
> +
> +  static_assert(requires { s | split(views::all(p)); });
> +  static_assert(requires { split(views::all(p))(s); });
> +  static_assert(requires { s | (split(views::all(p)) | views::all); });
> +  static_assert(requires { (split(views::all(p)) | views::all)(s); });
> +
> +  auto adapt = split(p);
> +  static_assert(requires { s | adapt; });
> +  static_assert(requires { adapt(s); });
> +
> +  auto adapt2 = split(p) | views::all;
> +  static_assert(requires { s | adapt2; });
> +  static_assert(requires { adapt2(s); });
> +}
> +
> +void
> +test10()
> +{
> +  // LWG 3505
> +  auto to_string = [] (auto r) {
> +    return std::string(r.begin(), ranges::next(r.begin(), r.end()));
> +  };
> +  auto v = "xxyx"sv | views::split("xy"sv) | views::transform(to_string);
> +  VERIFY( ranges::equal(v, (std::string_view[]){"x", "x"}) );
> +}
> +
> +void
> +test11()
> +{
> +  // LWG 3478
> +  static_assert(ranges::distance(views::split("text"sv, "text"sv)) == 2);
> +  static_assert(ranges::distance(views::split(" text "sv, ' ')) == 3);
> +  static_assert(ranges::distance(views::split(" t e x t "sv, ' ')) == 6);
> +  static_assert(ranges::distance(views::split("  text  "sv, "  "sv)) == 3);
> +  static_assert(ranges::distance(views::split("  text    "sv, "  "sv)) == 4);
> +  static_assert(ranges::distance(views::split("  text     "sv, "  "sv)) == 4);
> +  static_assert(ranges::distance(views::split("t"sv, 't')) == 2);
> +  static_assert(ranges::distance(views::split("text"sv, ""sv)) == 4);
> +}
> +} // namespace from_lazy_split_cc
> +
> +int
> +main()
> +{
> +  test01();
> +
> +  from_lazy_split_cc::test01();
> +  from_lazy_split_cc::test02();
> +  from_lazy_split_cc::test03();
> +  from_lazy_split_cc::test04();
> +  from_lazy_split_cc::test05();
> +  from_lazy_split_cc::test06();
> +  from_lazy_split_cc::test10();
> +  from_lazy_split_cc::test11();
> +}
> diff --git a/libstdc++-v3/testsuite/std/ranges/p2325.cc b/libstdc++-v3/testsuite/std/ranges/p2325.cc
> index 4d075409026..d2ebe9af863 100644
> --- a/libstdc++-v3/testsuite/std/ranges/p2325.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/p2325.cc
> @@ -124,6 +124,20 @@ test08()
>    static_assert(default_initializable<type4>);
>  }
>
> +void
> +test08a()
> +{
> +  // Verify split_view is conditionally default constructible.
> +  using type1 = ranges::split_view<ranges::ref_view<int[2]>, ranges::single_view<int>>;
> +  static_assert(!default_initializable<type1>);
> +  using type2 = ranges::split_view<ranges::single_view<int>, ranges::ref_view<int[2]>>;
> +  static_assert(!default_initializable<type2>);
> +  using type3 = ranges::split_view<ranges::ref_view<int[2]>, ranges::ref_view<int[2]>>;
> +  static_assert(!default_initializable<type3>);
> +  using type4 = ranges::split_view<ranges::single_view<int>, ranges::single_view<int>>;
> +  static_assert(default_initializable<type4>);
> +}
> +
>  void
>  test09()
>  {
> diff --git a/libstdc++-v3/testsuite/std/ranges/p2367.cc b/libstdc++-v3/testsuite/std/ranges/p2367.cc
> index 5228b021602..70a0304593f 100644
> --- a/libstdc++-v3/testsuite/std/ranges/p2367.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/p2367.cc
> @@ -45,4 +45,5 @@ test01()
>
>    // Verify changes to views::lazy_split.
>    auto v6 = views::lazy_split(x, 5u);
> +  auto v7 = views::split(x, 5u);
>  }
> --
> 2.32.0.93.g670b81a890
>


^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2021-06-18 21:58 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-06-17 15:22 [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Patrick Palka
2021-06-17 15:22 ` [PATCH 2/5] libstdc++: Move ranges algos used by <ranges> into ranges_util.h Patrick Palka
2021-06-17 18:14   ` Jonathan Wakely
2021-06-17 15:22 ` [PATCH 3/5] libstdc++: Rename views::split to views::lazy_split as per P2210 Patrick Palka
2021-06-18 21:56   ` Jonathan Wakely
2021-06-17 15:22 ` [PATCH 4/5] libstdc++: Implement resolution of LWG 3478 " Patrick Palka
2021-06-18 21:57   ` Jonathan Wakely
2021-06-17 15:22 ` [PATCH 5/5] libstdc++: Implement new views::split " Patrick Palka
2021-06-18 21:58   ` Jonathan Wakely
2021-06-17 18:19 ` [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Jonathan Wakely
2021-06-17 18:28   ` 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).