public inbox for libstdc++@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH 1/3] libstdc++: Implement ranges::contains/contains_subrange from P2302R4
@ 2022-11-14  4:50 Patrick Palka
  2022-11-14  4:50 ` [PATCH 2/3] libstdc++: Implement ranges::iota from P2440R1 Patrick Palka
                   ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Patrick Palka @ 2022-11-14  4:50 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Patrick Palka

Tested on x86_64-pc-linux-gnu, does this look OK for trunk?

libstdc++-v3/ChangeLog:

	* include/bits/ranges_algo.h (__contains_fn, contains): Define.
	(__contains_subrange_fn, contains_subrange): Define.
	* testsuite/25_algorithms/contains/1.cc: New test.
	* testsuite/25_algorithms/contains_subrange/1.cc: New test.
---
 libstdc++-v3/include/bits/ranges_algo.h       | 54 +++++++++++++++++++
 .../testsuite/25_algorithms/contains/1.cc     | 33 ++++++++++++
 .../25_algorithms/contains_subrange/1.cc      | 35 ++++++++++++
 3 files changed, 122 insertions(+)
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/contains/1.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/contains_subrange/1.cc

diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h
index de71bd07a2f..da0ca981dc3 100644
--- a/libstdc++-v3/include/bits/ranges_algo.h
+++ b/libstdc++-v3/include/bits/ranges_algo.h
@@ -3464,6 +3464,60 @@ namespace ranges
 
   inline constexpr __prev_permutation_fn prev_permutation{};
 
+#if __cplusplus > 202002L
+  struct __contains_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 bool
+      operator()(_Iter __first, _Sent __last, const _Tp& __value, _Proj __proj = {}) const
+      { return ranges::find(std::move(__first), __last, __value, __proj) != __last; }
+
+    template<input_range _Range, typename _Tp, typename _Proj = identity>
+      requires indirect_binary_predicate<ranges::equal_to,
+					 projected<iterator_t<_Range>, _Proj>, const _Tp*>
+      constexpr bool
+      operator()(_Range&& __r, const _Tp& __value, _Proj __proj = {}) const
+      { return (*this)(ranges::begin(__r), ranges::end(__r), __value, std::move(__proj)); }
+  };
+
+  inline constexpr __contains_fn contains{};
+
+  struct __contains_subrange_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 bool
+      operator()(_Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Sent2 __last2,
+		 _Pred __pred = {}, Proj1 __proj1 = {}, Proj2 __proj2 = {}) const
+      {
+	return __first2 == __last2
+	  || !ranges::search(__first1, __last1, __first2, __last2,
+			     std::move(__pred), std::move(__proj1), std::move(__proj2)).empty();
+      }
+
+    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 bool
+      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 __contains_subrange_fn contains_subrange{};
+#endif // C++23
 } // namespace ranges
 
 #define __cpp_lib_shift 201806L
diff --git a/libstdc++-v3/testsuite/25_algorithms/contains/1.cc b/libstdc++-v3/testsuite/25_algorithms/contains/1.cc
new file mode 100644
index 00000000000..146ab593b70
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/contains/1.cc
@@ -0,0 +1,33 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  int x[] = {1,2,3};
+  using to_input = __gnu_test::test_input_range<int>;
+  VERIFY( ranges::contains(to_input(x), 1) );
+  VERIFY( ranges::contains(to_input(x), 2) );
+  VERIFY( ranges::contains(to_input(x), 3) );
+  VERIFY( !ranges::contains(to_input(x), 4) );
+  VERIFY( !ranges::contains(x, x+2, 3) );
+  auto neg = [](int n) { return -n; };
+  VERIFY( ranges::contains(to_input(x), -1, neg) );
+  VERIFY( ranges::contains(to_input(x), -2, neg) );
+  VERIFY( ranges::contains(to_input(x), -3, neg) );
+  VERIFY( !ranges::contains(to_input(x), -4, neg) );
+
+  VERIFY( !ranges::contains(x, x+2, -3, neg) );
+}
+
+int
+main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/contains_subrange/1.cc b/libstdc++-v3/testsuite/25_algorithms/contains_subrange/1.cc
new file mode 100644
index 00000000000..62b92795f94
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/contains_subrange/1.cc
@@ -0,0 +1,35 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  int x[] = {1,2,3,4,5};
+  int y[] = {2,3,4};
+  int z[] = {4,5,6};
+  __gnu_test::test_forward_range<int> rx(x);
+  __gnu_test::test_forward_range<int> ry(y);
+  __gnu_test::test_forward_range<int> rz(z);
+  VERIFY( ranges::contains_subrange(rx, ry) );
+  VERIFY( !ranges::contains_subrange(rx, rz) );
+  VERIFY( ranges::contains_subrange(rx, ry, ranges::less{}) );
+  VERIFY( ranges::contains_subrange(rx, rz, ranges::less{}) );
+  auto plus3 = [](int n) { return n+3; };
+  VERIFY( !ranges::contains_subrange(rx, ry, ranges::equal_to{}, plus3) );
+  VERIFY( ranges::contains_subrange(rx, rz, ranges::equal_to{}, plus3) );
+
+  VERIFY( ranges::contains_subrange(x, x+2, y, y+1) );
+  VERIFY( !ranges::contains_subrange(x, x+2, y, y+2) );
+}
+
+int
+main()
+{
+  test01();
+}
-- 
2.38.1.420.g319605f8f0


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

* [PATCH 2/3] libstdc++: Implement ranges::iota from P2440R1
  2022-11-14  4:50 [PATCH 1/3] libstdc++: Implement ranges::contains/contains_subrange from P2302R4 Patrick Palka
@ 2022-11-14  4:50 ` Patrick Palka
  2022-11-14 10:08   ` Jonathan Wakely
  2022-11-14  4:50 ` [PATCH 3/3] libstdc++: Implement ranges::find_last{,_if,_if_not} from P1223R5 Patrick Palka
  2022-11-14  9:04 ` [PATCH 1/3] libstdc++: Implement ranges::contains/contains_subrange from P2302R4 Jonathan Wakely
  2 siblings, 1 reply; 12+ messages in thread
From: Patrick Palka @ 2022-11-14  4:50 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Patrick Palka

Tested on x86_64-pc-linux-gnu, does this look OK for trunk?

libstdc++-v3/ChangeLog:

	* include/bits/ranges_algo.h (out_value_result): Define.
	(iota_result): Define.
	(__iota_fn, iota): Define.
	* testsuite/25_algorithms/iota/1.cc: New test.
---
 libstdc++-v3/include/bits/ranges_algo.h       | 48 +++++++++++++++++++
 .../testsuite/25_algorithms/iota/1.cc         | 29 +++++++++++
 2 files changed, 77 insertions(+)
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/iota/1.cc

diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h
index da0ca981dc3..f003117c569 100644
--- a/libstdc++-v3/include/bits/ranges_algo.h
+++ b/libstdc++-v3/include/bits/ranges_algo.h
@@ -3517,6 +3517,54 @@ namespace ranges
   };
 
   inline constexpr __contains_subrange_fn contains_subrange{};
+
+  template<typename _Out, typename _Tp>
+    struct out_value_result
+    {
+      [[no_unique_address]] _Out out;
+      [[no_unique_address]] _Tp value;
+
+      template<typename _Out2, typename _Tp2>
+	requires convertible_to<const _Out&, _Out2>
+	  && convertible_to<const _Tp&, _Tp2>
+	constexpr
+	operator out_value_result<_Out2, _Tp2>() const &
+	{ return {out, value}; }
+
+      template<typename _Out2, typename _Tp2>
+	requires convertible_to<_Out, _Out2>
+	  && convertible_to<_Tp, _Tp2>
+	constexpr
+	operator out_value_result<_Out2, _Tp2>() &&
+	{ return {std::move(out), std::move(value)}; }
+    };
+
+  template<typename _Out, typename _Tp>
+    using iota_result = out_value_result<_Out, _Tp>;
+
+  struct __iota_fn
+  {
+    template<input_or_output_iterator _Out, sentinel_for<_Out> _Sent, weakly_incrementable _Tp>
+      requires indirectly_writable<_Out, const _Tp&>
+      constexpr iota_result<_Out, _Tp>
+      operator()(_Out __first, _Sent __last, _Tp __value) const
+      {
+	while (__first != __last)
+	  {
+	    *__first = static_cast<add_const_t<_Tp>&>(__value);
+	    ++__first;
+	    ++__value;
+	  }
+	return {std::move(__first), std::move(__value)};
+      }
+
+    template<weakly_incrementable _Tp, output_range<const _Tp&> _Range>
+      constexpr iota_result<borrowed_iterator_t<_Range>, _Tp>
+      operator()(_Range&& __r, _Tp __value) const
+      { return (*this)(ranges::begin(__r), ranges::end(__r), std::move(__value)); }
+  };
+
+  inline constexpr __iota_fn iota{};
 #endif // C++23
 } // namespace ranges
 
diff --git a/libstdc++-v3/testsuite/25_algorithms/iota/1.cc b/libstdc++-v3/testsuite/25_algorithms/iota/1.cc
new file mode 100644
index 00000000000..ad2bf08adf5
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/iota/1.cc
@@ -0,0 +1,29 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  int x[3] = {};
+  __gnu_test::test_output_range<int> rx(x);
+  auto r0 = ranges::iota(rx, 0);
+  VERIFY( r0.out.ptr == x+3 );
+  VERIFY( r0.value == 3 );
+  VERIFY( ranges::equal(x, (int[]){0,1,2}) );
+  auto r1 = ranges::iota(x, x+2, 5);
+  VERIFY( r1.out == x+2 );
+  VERIFY( r1.value == 7 );
+  VERIFY( ranges::equal(x, (int[]){5,6,2}) );
+}
+
+int
+main()
+{
+  test01();
+}
-- 
2.38.1.420.g319605f8f0


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

* [PATCH 3/3] libstdc++: Implement ranges::find_last{,_if,_if_not} from P1223R5
  2022-11-14  4:50 [PATCH 1/3] libstdc++: Implement ranges::contains/contains_subrange from P2302R4 Patrick Palka
  2022-11-14  4:50 ` [PATCH 2/3] libstdc++: Implement ranges::iota from P2440R1 Patrick Palka
@ 2022-11-14  4:50 ` Patrick Palka
  2022-11-14 15:12   ` [PATCH 3/3] libstdc++: Implement ranges::find_last{, _if, _if_not} " Jonathan Wakely
  2022-11-14  9:04 ` [PATCH 1/3] libstdc++: Implement ranges::contains/contains_subrange from P2302R4 Jonathan Wakely
  2 siblings, 1 reply; 12+ messages in thread
From: Patrick Palka @ 2022-11-14  4:50 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Patrick Palka

Tested on x86_64-pc-linux-gnu, does this look OK for trunk?

libstdc++-v3/ChangeLog:

	* include/bits/ranges_algo.h (__find_last_fn, find_last):
	Define.
	(__find_last_if_fn, find_last_if): Define.
	(__find_last_if_not_fn, find_last_if_not): Define.
	* testsuite/25_algorithms/find_last/1.cc: New test.
	* testsuite/25_algorithms/find_last_if/1.cc: New test.
	* testsuite/25_algorithms/find_last_if_not/1.cc: New test.
---
 libstdc++-v3/include/bits/ranges_algo.h       | 123 ++++++++++++++++++
 .../testsuite/25_algorithms/find_last/1.cc    |  90 +++++++++++++
 .../testsuite/25_algorithms/find_last_if/1.cc |  92 +++++++++++++
 .../25_algorithms/find_last_if_not/1.cc       |  92 +++++++++++++
 4 files changed, 397 insertions(+)
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/find_last/1.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/find_last_if/1.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/find_last_if_not/1.cc

diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h
index f003117c569..0e4329382eb 100644
--- a/libstdc++-v3/include/bits/ranges_algo.h
+++ b/libstdc++-v3/include/bits/ranges_algo.h
@@ -3565,6 +3565,129 @@ namespace ranges
   };
 
   inline constexpr __iota_fn iota{};
+
+  struct __find_last_fn
+  {
+    template<forward_iterator _Iter, sentinel_for<_Iter> _Sent, typename T, typename _Proj = identity>
+      requires indirect_binary_predicate<ranges::equal_to, projected<_Iter, _Proj>, const T*>
+      constexpr subrange<_Iter>
+      operator()(_Iter __first, _Sent __last, const T& __value, _Proj __proj = {}) const
+      {
+	if constexpr (same_as<_Iter, _Sent> && bidirectional_iterator<_Iter>)
+	  {
+	    _Iter __found = ranges::find(reverse_iterator<_Iter>{__last},
+					 reverse_iterator<_Iter>{__first},
+					 __value, __proj).base();
+	    if (__found == __first)
+	      return {__last, __last};
+	    else
+	      return {ranges::prev(__found), __last};
+	  }
+	else
+	  {
+	    _Iter __found = ranges::find(__first, __last, __value, __proj);
+	    if (__found == __last)
+	      return {__found, __found};
+	    for (;;)
+	      {
+		__first = ranges::find(ranges::next(__first), __last, __value, __proj);
+		if (__first == __last)
+		  return {__found, __first};
+		__found = __first;
+	      }
+	  }
+      }
+
+    template<forward_range _Range, typename T, typename _Proj = identity>
+      requires indirect_binary_predicate<ranges::equal_to, projected<iterator_t<_Range>, _Proj>, const T*>
+      constexpr borrowed_subrange_t<_Range>
+      operator()(_Range&& __r, const T& __value, _Proj __proj = {}) const
+      { return (*this)(ranges::begin(__r), ranges::end(__r), __value, std::move(__proj)); }
+  };
+
+  inline constexpr __find_last_fn find_last{};
+
+  struct __find_last_if_fn
+  {
+    template<forward_iterator _Iter, sentinel_for<_Iter> _Sent, typename _Proj = identity,
+	     indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+      constexpr subrange<_Iter>
+      operator()(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {}) const
+      {
+	if constexpr (same_as<_Iter, _Sent> && bidirectional_iterator<_Iter>)
+	  {
+	    _Iter __found = ranges::find_if(reverse_iterator<_Iter>{__last},
+					    reverse_iterator<_Iter>{__first},
+					    __pred, __proj).base();
+	    if (__found == __first)
+	      return {__last, __last};
+	    else
+	      return {ranges::prev(__found), __last};
+	  }
+	else
+	  {
+	    _Iter __found = ranges::find_if(__first, __last, __pred, __proj);
+	    if (__found == __last)
+	      return {__found, __found};
+	    for (;;)
+	      {
+		__first = ranges::find_if(ranges::next(__first), __last, __pred, __proj);
+		if (__first == __last)
+		  return {__found, __first};
+		__found = __first;
+	      }
+	  }
+      }
+
+    template<forward_range _Range, typename _Proj = identity,
+	     indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Pred>
+      constexpr borrowed_subrange_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_last_if_fn find_last_if{};
+
+  struct __find_last_if_not_fn
+  {
+    template<forward_iterator _Iter, sentinel_for<_Iter> _Sent, typename _Proj = identity,
+	     indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+      constexpr subrange<_Iter>
+      operator()(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {}) const
+      {
+	if constexpr (same_as<_Iter, _Sent> && bidirectional_iterator<_Iter>)
+	  {
+	    _Iter __found = ranges::find_if_not(reverse_iterator<_Iter>{__last},
+						reverse_iterator<_Iter>{__first},
+						__pred, __proj).base();
+	    if (__found == __first)
+	      return {__last, __last};
+	    else
+	      return {ranges::prev(__found), __last};
+	  }
+	else
+	  {
+	    _Iter __found = ranges::find_if_not(__first, __last, __pred, __proj);
+	    if (__found == __last)
+	      return {__found, __found};
+	    for (;;)
+	      {
+		__first = ranges::find_if_not(ranges::next(__first), __last, __pred, __proj);
+		if (__first == __last)
+		  return {__found, __first};
+		__found = __first;
+	      }
+	  }
+      }
+
+    template<forward_range _Range, typename _Proj = identity,
+	     indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Pred>
+      constexpr borrowed_subrange_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_last_if_not_fn find_last_if_not{};
 #endif // C++23
 } // namespace ranges
 
diff --git a/libstdc++-v3/testsuite/25_algorithms/find_last/1.cc b/libstdc++-v3/testsuite/25_algorithms/find_last/1.cc
new file mode 100644
index 00000000000..ef5844c8afd
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/find_last/1.cc
@@ -0,0 +1,90 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+namespace ranges = std::ranges;
+
+constexpr bool
+test01()
+{
+  int x[] = {1, 2, 1, 2, 1, 2, 1, 2};
+
+  auto sr0 = ranges::find_last(x, 0);
+  VERIFY( ranges::empty(sr0) );
+  VERIFY( sr0.begin() == ranges::end(x) );
+
+  auto sr1 = ranges::find_last(x, 1);
+  VERIFY( ranges::equal(sr1, (int[]){1, 2}) );
+  VERIFY( sr1.begin() == &x[6] );
+
+  auto sr2 = ranges::find_last(x, 2);
+  VERIFY( ranges::equal(sr2, (int[]){2}) );
+  VERIFY( sr2.begin() == &x[7] );
+
+  auto plus3 = [](int n) { return n+3; };
+
+  auto sr3 = ranges::find_last(x, 3, plus3);
+  VERIFY( ranges::empty(sr3) );
+  VERIFY( sr3.begin() == ranges::end(x) );
+
+  auto sr4 = ranges::find_last(x, 4, plus3);
+  VERIFY( ranges::equal(sr4, (int[]){1, 2}) );
+  VERIFY( sr4.begin() == &x[6] );
+
+  auto sr5 = ranges::find_last(x, 5, plus3);
+  VERIFY( ranges::equal(sr5, (int[]){2}) );
+  VERIFY( sr5.begin() == &x[7] );
+
+  return true;
+}
+
+void
+test02()
+{
+  int x[] = {1, 2, 3, 1, 2, 3, 1, 2, 3};
+  __gnu_test::test_forward_range<int> rx(x);
+
+  auto sr0 = ranges::find_last(rx, 0);
+  VERIFY( ranges::empty(sr0) );
+  VERIFY( sr0.begin() == ranges::end(rx) );
+
+  auto sr1 = ranges::find_last(rx, 1);
+  VERIFY( ranges::equal(sr1, (int[]){1, 2, 3}) );
+  VERIFY( sr1.begin().ptr == &x[6] );
+
+  auto sr2 = ranges::find_last(rx, 2);
+  VERIFY( ranges::equal(sr2, (int[]){2, 3}) );
+  VERIFY( sr2.begin().ptr == &x[7] );
+
+  auto sr3 = ranges::find_last(rx, 3);
+  VERIFY( ranges::equal(sr3, (int[]){3}) );
+  VERIFY( sr3.begin().ptr == &x[8] );
+
+  auto plus4 = [](int n) { return n+4; };
+
+  auto sr4 = ranges::find_last(rx, 4, plus4);
+  VERIFY( ranges::empty(sr4) );
+  VERIFY( sr4.begin() == ranges::end(rx) );
+
+  auto sr5 = ranges::find_last(rx, 5, plus4);
+  VERIFY( ranges::equal(sr5, (int[]){1, 2, 3}) );
+  VERIFY( sr5.begin().ptr == &x[6] );
+
+  auto sr6 = ranges::find_last(rx, 6, plus4);
+  VERIFY( ranges::equal(sr6, (int[]){2, 3}) );
+  VERIFY( sr6.begin().ptr == &x[7] );
+
+  auto sr7 = ranges::find_last(rx, 7, plus4);
+  VERIFY( ranges::equal(sr7, (int[]){3}) );
+  VERIFY( sr7.begin().ptr == &x[8] );
+}
+
+int
+main()
+{
+  static_assert(test01());
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/find_last_if/1.cc b/libstdc++-v3/testsuite/25_algorithms/find_last_if/1.cc
new file mode 100644
index 00000000000..0a723475dec
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/find_last_if/1.cc
@@ -0,0 +1,92 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+namespace ranges = std::ranges;
+
+template<int N> constexpr auto eq = [](int m) { return m == N; };
+
+constexpr bool
+test01()
+{
+  int x[] = {1, 2, 1, 2, 1, 2, 1, 2};
+
+  auto sr0 = ranges::find_last_if(x, eq<0>);
+  VERIFY( ranges::empty(sr0) );
+  VERIFY( sr0.begin() == ranges::end(x) );
+
+  auto sr1 = ranges::find_last_if(x, eq<1>);
+  VERIFY( ranges::equal(sr1, (int[]){1, 2}) );
+  VERIFY( sr1.begin() == &x[6] );
+
+  auto sr2 = ranges::find_last_if(x, eq<2>);
+  VERIFY( ranges::equal(sr2, (int[]){2}) );
+  VERIFY( sr2.begin() == &x[7] );
+
+  auto plus3 = [](int n) { return n+3; };
+
+  auto sr3 = ranges::find_last_if(x, eq<3>, plus3);
+  VERIFY( ranges::empty(sr3) );
+  VERIFY( sr3.begin() == ranges::end(x) );
+
+  auto sr4 = ranges::find_last_if(x, eq<4>, plus3);
+  VERIFY( ranges::equal(sr4, (int[]){1, 2}) );
+  VERIFY( sr4.begin() == &x[6] );
+
+  auto sr5 = ranges::find_last_if(x, eq<5>, plus3);
+  VERIFY( ranges::equal(sr5, (int[]){2}) );
+  VERIFY( sr5.begin() == &x[7] );
+
+  return true;
+}
+
+void
+test02()
+{
+  int x[] = {1, 2, 3, 1, 2, 3, 1, 2, 3};
+  __gnu_test::test_forward_range<int> rx(x);
+
+  auto sr0 = ranges::find_last_if(rx, eq<0>);
+  VERIFY( ranges::empty(sr0) );
+  VERIFY( sr0.begin() == ranges::end(rx) );
+
+  auto sr1 = ranges::find_last_if(rx, eq<1>);
+  VERIFY( ranges::equal(sr1, (int[]){1, 2, 3}) );
+  VERIFY( sr1.begin().ptr == &x[6] );
+
+  auto sr2 = ranges::find_last_if(rx, eq<2>);
+  VERIFY( ranges::equal(sr2, (int[]){2, 3}) );
+  VERIFY( sr2.begin().ptr == &x[7] );
+
+  auto sr3 = ranges::find_last_if(rx, eq<3>);
+  VERIFY( ranges::equal(sr3, (int[]){3}) );
+  VERIFY( sr3.begin().ptr == &x[8] );
+
+  auto plus4 = [](int n) { return n+4; };
+
+  auto sr4 = ranges::find_last_if(rx, eq<4>, plus4);
+  VERIFY( ranges::empty(sr4) );
+  VERIFY( sr4.begin() == ranges::end(rx) );
+
+  auto sr5 = ranges::find_last_if(rx, eq<5>, plus4);
+  VERIFY( ranges::equal(sr5, (int[]){1, 2, 3}) );
+  VERIFY( sr5.begin().ptr == &x[6] );
+
+  auto sr6 = ranges::find_last_if(rx, eq<6>, plus4);
+  VERIFY( ranges::equal(sr6, (int[]){2, 3}) );
+  VERIFY( sr6.begin().ptr == &x[7] );
+
+  auto sr7 = ranges::find_last_if(rx, eq<7>, plus4);
+  VERIFY( ranges::equal(sr7, (int[]){3}) );
+  VERIFY( sr7.begin().ptr == &x[8] );
+}
+
+int
+main()
+{
+  static_assert(test01());
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/find_last_if_not/1.cc b/libstdc++-v3/testsuite/25_algorithms/find_last_if_not/1.cc
new file mode 100644
index 00000000000..98aa94b7f2c
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/find_last_if_not/1.cc
@@ -0,0 +1,92 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+namespace ranges = std::ranges;
+
+template<int N> constexpr auto ne = [](int m) { return m != N; };
+
+constexpr bool
+test01()
+{
+  int x[] = {1, 2, 1, 2, 1, 2, 1, 2};
+
+  auto sr0 = ranges::find_last_if_not(x, ne<0>);
+  VERIFY( ranges::empty(sr0) );
+  VERIFY( sr0.begin() == ranges::end(x) );
+
+  auto sr1 = ranges::find_last_if_not(x, ne<1>);
+  VERIFY( ranges::equal(sr1, (int[]){1, 2}) );
+  VERIFY( sr1.begin() == &x[6] );
+
+  auto sr2 = ranges::find_last_if_not(x, ne<2>);
+  VERIFY( ranges::equal(sr2, (int[]){2}) );
+  VERIFY( sr2.begin() == &x[7] );
+
+  auto plus3 = [](int n) { return n+3; };
+
+  auto sr3 = ranges::find_last_if_not(x, ne<3>, plus3);
+  VERIFY( ranges::empty(sr3) );
+  VERIFY( sr3.begin() == ranges::end(x) );
+
+  auto sr4 = ranges::find_last_if_not(x, ne<4>, plus3);
+  VERIFY( ranges::equal(sr4, (int[]){1, 2}) );
+  VERIFY( sr4.begin() == &x[6] );
+
+  auto sr5 = ranges::find_last_if_not(x, ne<5>, plus3);
+  VERIFY( ranges::equal(sr5, (int[]){2}) );
+  VERIFY( sr5.begin() == &x[7] );
+
+  return true;
+}
+
+void
+test02()
+{
+  int x[] = {1, 2, 3, 1, 2, 3, 1, 2, 3};
+  __gnu_test::test_forward_range<int> rx(x);
+
+  auto sr0 = ranges::find_last_if_not(rx, ne<0>);
+  VERIFY( ranges::empty(sr0) );
+  VERIFY( sr0.begin() == ranges::end(rx) );
+
+  auto sr1 = ranges::find_last_if_not(rx, ne<1>);
+  VERIFY( ranges::equal(sr1, (int[]){1, 2, 3}) );
+  VERIFY( sr1.begin().ptr == &x[6] );
+
+  auto sr2 = ranges::find_last_if_not(rx, ne<2>);
+  VERIFY( ranges::equal(sr2, (int[]){2, 3}) );
+  VERIFY( sr2.begin().ptr == &x[7] );
+
+  auto sr3 = ranges::find_last_if_not(rx, ne<3>);
+  VERIFY( ranges::equal(sr3, (int[]){3}) );
+  VERIFY( sr3.begin().ptr == &x[8] );
+
+  auto plus4 = [](int n) { return n+4; };
+
+  auto sr4 = ranges::find_last_if_not(rx, ne<4>, plus4);
+  VERIFY( ranges::empty(sr4) );
+  VERIFY( sr4.begin() == ranges::end(rx) );
+
+  auto sr5 = ranges::find_last_if_not(rx, ne<5>, plus4);
+  VERIFY( ranges::equal(sr5, (int[]){1, 2, 3}) );
+  VERIFY( sr5.begin().ptr == &x[6] );
+
+  auto sr6 = ranges::find_last_if_not(rx, ne<6>, plus4);
+  VERIFY( ranges::equal(sr6, (int[]){2, 3}) );
+  VERIFY( sr6.begin().ptr == &x[7] );
+
+  auto sr7 = ranges::find_last_if_not(rx, ne<7>, plus4);
+  VERIFY( ranges::equal(sr7, (int[]){3}) );
+  VERIFY( sr7.begin().ptr == &x[8] );
+}
+
+int
+main()
+{
+  static_assert(test01());
+  test02();
+}
-- 
2.38.1.420.g319605f8f0


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

* Re: [PATCH 1/3] libstdc++: Implement ranges::contains/contains_subrange from P2302R4
  2022-11-14  4:50 [PATCH 1/3] libstdc++: Implement ranges::contains/contains_subrange from P2302R4 Patrick Palka
  2022-11-14  4:50 ` [PATCH 2/3] libstdc++: Implement ranges::iota from P2440R1 Patrick Palka
  2022-11-14  4:50 ` [PATCH 3/3] libstdc++: Implement ranges::find_last{,_if,_if_not} from P1223R5 Patrick Palka
@ 2022-11-14  9:04 ` Jonathan Wakely
  2022-11-14 15:07   ` Patrick Palka
  2 siblings, 1 reply; 12+ messages in thread
From: Jonathan Wakely @ 2022-11-14  9:04 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches, libstdc++

On Mon, 14 Nov 2022 at 04:51, Patrick Palka via Libstdc++
<libstdc++@gcc.gnu.org> wrote:
>
> Tested on x86_64-pc-linux-gnu, does this look OK for trunk?
>
> libstdc++-v3/ChangeLog:
>
>         * include/bits/ranges_algo.h (__contains_fn, contains): Define.
>         (__contains_subrange_fn, contains_subrange): Define.
>         * testsuite/25_algorithms/contains/1.cc: New test.
>         * testsuite/25_algorithms/contains_subrange/1.cc: New test.
> ---
>  libstdc++-v3/include/bits/ranges_algo.h       | 54 +++++++++++++++++++
>  .../testsuite/25_algorithms/contains/1.cc     | 33 ++++++++++++
>  .../25_algorithms/contains_subrange/1.cc      | 35 ++++++++++++
>  3 files changed, 122 insertions(+)
>  create mode 100644 libstdc++-v3/testsuite/25_algorithms/contains/1.cc
>  create mode 100644 libstdc++-v3/testsuite/25_algorithms/contains_subrange/1.cc
>
> diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h
> index de71bd07a2f..da0ca981dc3 100644
> --- a/libstdc++-v3/include/bits/ranges_algo.h
> +++ b/libstdc++-v3/include/bits/ranges_algo.h
> @@ -3464,6 +3464,60 @@ namespace ranges
>
>    inline constexpr __prev_permutation_fn prev_permutation{};
>
> +#if __cplusplus > 202002L
> +  struct __contains_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 bool
> +      operator()(_Iter __first, _Sent __last, const _Tp& __value, _Proj __proj = {}) const
> +      { return ranges::find(std::move(__first), __last, __value, __proj) != __last; }

Should this use std::move(__proj)?



> +
> +    template<input_range _Range, typename _Tp, typename _Proj = identity>
> +      requires indirect_binary_predicate<ranges::equal_to,
> +                                        projected<iterator_t<_Range>, _Proj>, const _Tp*>
> +      constexpr bool
> +      operator()(_Range&& __r, const _Tp& __value, _Proj __proj = {}) const
> +      { return (*this)(ranges::begin(__r), ranges::end(__r), __value, std::move(__proj)); }
> +  };
> +
> +  inline constexpr __contains_fn contains{};
> +
> +  struct __contains_subrange_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 bool
> +      operator()(_Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Sent2 __last2,
> +                _Pred __pred = {}, Proj1 __proj1 = {}, Proj2 __proj2 = {}) const
> +      {
> +       return __first2 == __last2
> +         || !ranges::search(__first1, __last1, __first2, __last2,
> +                            std::move(__pred), std::move(__proj1), std::move(__proj2)).empty();
> +      }
> +
> +    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 bool
> +      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 __contains_subrange_fn contains_subrange{};
> +#endif // C++23
>  } // namespace ranges
>
>  #define __cpp_lib_shift 201806L
> diff --git a/libstdc++-v3/testsuite/25_algorithms/contains/1.cc b/libstdc++-v3/testsuite/25_algorithms/contains/1.cc
> new file mode 100644
> index 00000000000..146ab593b70
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/25_algorithms/contains/1.cc
> @@ -0,0 +1,33 @@
> +// { dg-options "-std=gnu++23" }
> +// { dg-do run { target c++23 } }
> +
> +#include <algorithm>
> +#include <testsuite_hooks.h>
> +#include <testsuite_iterators.h>
> +
> +namespace ranges = std::ranges;
> +
> +void
> +test01()
> +{
> +  int x[] = {1,2,3};
> +  using to_input = __gnu_test::test_input_range<int>;
> +  VERIFY( ranges::contains(to_input(x), 1) );
> +  VERIFY( ranges::contains(to_input(x), 2) );
> +  VERIFY( ranges::contains(to_input(x), 3) );
> +  VERIFY( !ranges::contains(to_input(x), 4) );
> +  VERIFY( !ranges::contains(x, x+2, 3) );
> +  auto neg = [](int n) { return -n; };
> +  VERIFY( ranges::contains(to_input(x), -1, neg) );
> +  VERIFY( ranges::contains(to_input(x), -2, neg) );
> +  VERIFY( ranges::contains(to_input(x), -3, neg) );
> +  VERIFY( !ranges::contains(to_input(x), -4, neg) );
> +
> +  VERIFY( !ranges::contains(x, x+2, -3, neg) );
> +}
> +
> +int
> +main()
> +{
> +  test01();
> +}
> diff --git a/libstdc++-v3/testsuite/25_algorithms/contains_subrange/1.cc b/libstdc++-v3/testsuite/25_algorithms/contains_subrange/1.cc
> new file mode 100644
> index 00000000000..62b92795f94
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/25_algorithms/contains_subrange/1.cc
> @@ -0,0 +1,35 @@
> +// { dg-options "-std=gnu++23" }
> +// { dg-do run { target c++23 } }
> +
> +#include <algorithm>
> +#include <testsuite_hooks.h>
> +#include <testsuite_iterators.h>
> +
> +namespace ranges = std::ranges;
> +
> +void
> +test01()
> +{
> +  int x[] = {1,2,3,4,5};
> +  int y[] = {2,3,4};
> +  int z[] = {4,5,6};
> +  __gnu_test::test_forward_range<int> rx(x);
> +  __gnu_test::test_forward_range<int> ry(y);
> +  __gnu_test::test_forward_range<int> rz(z);
> +  VERIFY( ranges::contains_subrange(rx, ry) );
> +  VERIFY( !ranges::contains_subrange(rx, rz) );
> +  VERIFY( ranges::contains_subrange(rx, ry, ranges::less{}) );
> +  VERIFY( ranges::contains_subrange(rx, rz, ranges::less{}) );
> +  auto plus3 = [](int n) { return n+3; };
> +  VERIFY( !ranges::contains_subrange(rx, ry, ranges::equal_to{}, plus3) );
> +  VERIFY( ranges::contains_subrange(rx, rz, ranges::equal_to{}, plus3) );
> +
> +  VERIFY( ranges::contains_subrange(x, x+2, y, y+1) );
> +  VERIFY( !ranges::contains_subrange(x, x+2, y, y+2) );
> +}
> +
> +int
> +main()
> +{
> +  test01();
> +}
> --
> 2.38.1.420.g319605f8f0
>


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

* Re: [PATCH 2/3] libstdc++: Implement ranges::iota from P2440R1
  2022-11-14  4:50 ` [PATCH 2/3] libstdc++: Implement ranges::iota from P2440R1 Patrick Palka
@ 2022-11-14 10:08   ` Jonathan Wakely
  2022-11-14 10:17     ` Daniel Krügler
  0 siblings, 1 reply; 12+ messages in thread
From: Jonathan Wakely @ 2022-11-14 10:08 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches, libstdc++

On Mon, 14 Nov 2022 at 04:52, Patrick Palka via Libstdc++
<libstdc++@gcc.gnu.org> wrote:
>
> Tested on x86_64-pc-linux-gnu, does this look OK for trunk?
>
> libstdc++-v3/ChangeLog:
>
>         * include/bits/ranges_algo.h (out_value_result): Define.
>         (iota_result): Define.
>         (__iota_fn, iota): Define.
>         * testsuite/25_algorithms/iota/1.cc: New test.
> ---
>  libstdc++-v3/include/bits/ranges_algo.h       | 48 +++++++++++++++++++
>  .../testsuite/25_algorithms/iota/1.cc         | 29 +++++++++++
>  2 files changed, 77 insertions(+)
>  create mode 100644 libstdc++-v3/testsuite/25_algorithms/iota/1.cc
>
> diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h
> index da0ca981dc3..f003117c569 100644
> --- a/libstdc++-v3/include/bits/ranges_algo.h
> +++ b/libstdc++-v3/include/bits/ranges_algo.h
> @@ -3517,6 +3517,54 @@ namespace ranges
>    };
>
>    inline constexpr __contains_subrange_fn contains_subrange{};
> +
> +  template<typename _Out, typename _Tp>
> +    struct out_value_result
> +    {
> +      [[no_unique_address]] _Out out;
> +      [[no_unique_address]] _Tp value;
> +
> +      template<typename _Out2, typename _Tp2>
> +       requires convertible_to<const _Out&, _Out2>
> +         && convertible_to<const _Tp&, _Tp2>
> +       constexpr
> +       operator out_value_result<_Out2, _Tp2>() const &
> +       { return {out, value}; }
> +
> +      template<typename _Out2, typename _Tp2>
> +       requires convertible_to<_Out, _Out2>
> +         && convertible_to<_Tp, _Tp2>
> +       constexpr
> +       operator out_value_result<_Out2, _Tp2>() &&
> +       { return {std::move(out), std::move(value)}; }
> +    };
> +
> +  template<typename _Out, typename _Tp>
> +    using iota_result = out_value_result<_Out, _Tp>;
> +
> +  struct __iota_fn
> +  {
> +    template<input_or_output_iterator _Out, sentinel_for<_Out> _Sent, weakly_incrementable _Tp>
> +      requires indirectly_writable<_Out, const _Tp&>
> +      constexpr iota_result<_Out, _Tp>
> +      operator()(_Out __first, _Sent __last, _Tp __value) const
> +      {
> +       while (__first != __last)
> +         {
> +           *__first = static_cast<add_const_t<_Tp>&>(__value);

Is this any different to const_cast<const _Tp&>(__value) ?

We know _Tp must be the same as decay_t<_Tp> and non-void, because
it's passed by value, and therefore I think is_same_v<const _Tp,
add_const_t<_Tp>> is always true, isn't it? We don't need to care
about people saying ranges::iota.operator()<O,S,T&>(o,s,t), those
people are animals.

We would just change the function parameter to const _Tp which would
mean that *_first = __value; always uses a const lvalue, but maybe
that's a bit too subtle. The cast makes it more explicit what's
happening, especially the const_cast version.


> +           ++__first;
> +           ++__value;
> +         }
> +       return {std::move(__first), std::move(__value)};
> +      }
> +
> +    template<weakly_incrementable _Tp, output_range<const _Tp&> _Range>
> +      constexpr iota_result<borrowed_iterator_t<_Range>, _Tp>
> +      operator()(_Range&& __r, _Tp __value) const
> +      { return (*this)(ranges::begin(__r), ranges::end(__r), std::move(__value)); }
> +  };
> +
> +  inline constexpr __iota_fn iota{};
>  #endif // C++23
>  } // namespace ranges
>
> diff --git a/libstdc++-v3/testsuite/25_algorithms/iota/1.cc b/libstdc++-v3/testsuite/25_algorithms/iota/1.cc
> new file mode 100644
> index 00000000000..ad2bf08adf5
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/25_algorithms/iota/1.cc
> @@ -0,0 +1,29 @@
> +// { dg-options "-std=gnu++23" }
> +// { dg-do run { target c++23 } }
> +
> +#include <algorithm>
> +#include <testsuite_hooks.h>
> +#include <testsuite_iterators.h>
> +
> +namespace ranges = std::ranges;
> +
> +void
> +test01()
> +{
> +  int x[3] = {};
> +  __gnu_test::test_output_range<int> rx(x);
> +  auto r0 = ranges::iota(rx, 0);
> +  VERIFY( r0.out.ptr == x+3 );
> +  VERIFY( r0.value == 3 );
> +  VERIFY( ranges::equal(x, (int[]){0,1,2}) );
> +  auto r1 = ranges::iota(x, x+2, 5);
> +  VERIFY( r1.out == x+2 );
> +  VERIFY( r1.value == 7 );
> +  VERIFY( ranges::equal(x, (int[]){5,6,2}) );
> +}
> +
> +int
> +main()
> +{
> +  test01();
> +}
> --
> 2.38.1.420.g319605f8f0
>


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

* Re: [PATCH 2/3] libstdc++: Implement ranges::iota from P2440R1
  2022-11-14 10:08   ` Jonathan Wakely
@ 2022-11-14 10:17     ` Daniel Krügler
  2022-11-14 10:20       ` Jonathan Wakely
  0 siblings, 1 reply; 12+ messages in thread
From: Daniel Krügler @ 2022-11-14 10:17 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: Patrick Palka, gcc-patches, libstdc++

Am Mo., 14. Nov. 2022 um 11:09 Uhr schrieb Jonathan Wakely via
Libstdc++ <libstdc++@gcc.gnu.org>:
>
> On Mon, 14 Nov 2022 at 04:52, Patrick Palka via Libstdc++
> <libstdc++@gcc.gnu.org> wrote:
> >
> > Tested on x86_64-pc-linux-gnu, does this look OK for trunk?
> >
> > libstdc++-v3/ChangeLog:
> >
> >         * include/bits/ranges_algo.h (out_value_result): Define.
> >         (iota_result): Define.
> >         (__iota_fn, iota): Define.
> >         * testsuite/25_algorithms/iota/1.cc: New test.
> > ---
> >  libstdc++-v3/include/bits/ranges_algo.h       | 48 +++++++++++++++++++
> >  .../testsuite/25_algorithms/iota/1.cc         | 29 +++++++++++
> >  2 files changed, 77 insertions(+)
> >  create mode 100644 libstdc++-v3/testsuite/25_algorithms/iota/1.cc
> >
> > diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h
> > index da0ca981dc3..f003117c569 100644
> > --- a/libstdc++-v3/include/bits/ranges_algo.h
> > +++ b/libstdc++-v3/include/bits/ranges_algo.h
> > @@ -3517,6 +3517,54 @@ namespace ranges
> >    };
> >
> >    inline constexpr __contains_subrange_fn contains_subrange{};
> > +
> > +  template<typename _Out, typename _Tp>
> > +    struct out_value_result
> > +    {
> > +      [[no_unique_address]] _Out out;
> > +      [[no_unique_address]] _Tp value;
> > +
> > +      template<typename _Out2, typename _Tp2>
> > +       requires convertible_to<const _Out&, _Out2>
> > +         && convertible_to<const _Tp&, _Tp2>
> > +       constexpr
> > +       operator out_value_result<_Out2, _Tp2>() const &
> > +       { return {out, value}; }
> > +
> > +      template<typename _Out2, typename _Tp2>
> > +       requires convertible_to<_Out, _Out2>
> > +         && convertible_to<_Tp, _Tp2>
> > +       constexpr
> > +       operator out_value_result<_Out2, _Tp2>() &&
> > +       { return {std::move(out), std::move(value)}; }
> > +    };
> > +
> > +  template<typename _Out, typename _Tp>
> > +    using iota_result = out_value_result<_Out, _Tp>;
> > +
> > +  struct __iota_fn
> > +  {
> > +    template<input_or_output_iterator _Out, sentinel_for<_Out> _Sent, weakly_incrementable _Tp>
> > +      requires indirectly_writable<_Out, const _Tp&>
> > +      constexpr iota_result<_Out, _Tp>
> > +      operator()(_Out __first, _Sent __last, _Tp __value) const
> > +      {
> > +       while (__first != __last)
> > +         {
> > +           *__first = static_cast<add_const_t<_Tp>&>(__value);
>
> Is this any different to const_cast<const _Tp&>(__value) ?

I think it is. const_cast<const _Tp&> can potentially mean the removal
of volatile, so I would always look with suspicion on const_cast<const
_Tp&>, while static_cast is clearer. Alternatively, as_const could be
used, which does add_const_t.

- Daniel

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

* Re: [PATCH 2/3] libstdc++: Implement ranges::iota from P2440R1
  2022-11-14 10:17     ` Daniel Krügler
@ 2022-11-14 10:20       ` Jonathan Wakely
  2022-11-14 15:11         ` Patrick Palka
  0 siblings, 1 reply; 12+ messages in thread
From: Jonathan Wakely @ 2022-11-14 10:20 UTC (permalink / raw)
  To: Daniel Krügler; +Cc: Patrick Palka, gcc-patches, libstdc++

On Mon, 14 Nov 2022 at 10:17, Daniel Krügler <daniel.kruegler@gmail.com> wrote:
>
> Am Mo., 14. Nov. 2022 um 11:09 Uhr schrieb Jonathan Wakely via
> Libstdc++ <libstdc++@gcc.gnu.org>:
> >
> > On Mon, 14 Nov 2022 at 04:52, Patrick Palka via Libstdc++
> > <libstdc++@gcc.gnu.org> wrote:
> > >
> > > Tested on x86_64-pc-linux-gnu, does this look OK for trunk?
> > >
> > > libstdc++-v3/ChangeLog:
> > >
> > >         * include/bits/ranges_algo.h (out_value_result): Define.
> > >         (iota_result): Define.
> > >         (__iota_fn, iota): Define.
> > >         * testsuite/25_algorithms/iota/1.cc: New test.
> > > ---
> > >  libstdc++-v3/include/bits/ranges_algo.h       | 48 +++++++++++++++++++
> > >  .../testsuite/25_algorithms/iota/1.cc         | 29 +++++++++++
> > >  2 files changed, 77 insertions(+)
> > >  create mode 100644 libstdc++-v3/testsuite/25_algorithms/iota/1.cc
> > >
> > > diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h
> > > index da0ca981dc3..f003117c569 100644
> > > --- a/libstdc++-v3/include/bits/ranges_algo.h
> > > +++ b/libstdc++-v3/include/bits/ranges_algo.h
> > > @@ -3517,6 +3517,54 @@ namespace ranges
> > >    };
> > >
> > >    inline constexpr __contains_subrange_fn contains_subrange{};
> > > +
> > > +  template<typename _Out, typename _Tp>
> > > +    struct out_value_result
> > > +    {
> > > +      [[no_unique_address]] _Out out;
> > > +      [[no_unique_address]] _Tp value;
> > > +
> > > +      template<typename _Out2, typename _Tp2>
> > > +       requires convertible_to<const _Out&, _Out2>
> > > +         && convertible_to<const _Tp&, _Tp2>
> > > +       constexpr
> > > +       operator out_value_result<_Out2, _Tp2>() const &
> > > +       { return {out, value}; }
> > > +
> > > +      template<typename _Out2, typename _Tp2>
> > > +       requires convertible_to<_Out, _Out2>
> > > +         && convertible_to<_Tp, _Tp2>
> > > +       constexpr
> > > +       operator out_value_result<_Out2, _Tp2>() &&
> > > +       { return {std::move(out), std::move(value)}; }
> > > +    };
> > > +
> > > +  template<typename _Out, typename _Tp>
> > > +    using iota_result = out_value_result<_Out, _Tp>;
> > > +
> > > +  struct __iota_fn
> > > +  {
> > > +    template<input_or_output_iterator _Out, sentinel_for<_Out> _Sent, weakly_incrementable _Tp>
> > > +      requires indirectly_writable<_Out, const _Tp&>
> > > +      constexpr iota_result<_Out, _Tp>
> > > +      operator()(_Out __first, _Sent __last, _Tp __value) const
> > > +      {
> > > +       while (__first != __last)
> > > +         {
> > > +           *__first = static_cast<add_const_t<_Tp>&>(__value);
> >
> > Is this any different to const_cast<const _Tp&>(__value) ?
>
> I think it is. const_cast<const _Tp&> can potentially mean the removal
> of volatile,

True.

> so I would always look with suspicion on const_cast<const
> _Tp&>, while static_cast is clearer. Alternatively, as_const could be
> used, which does add_const_t.

Which means evaluating the add_const trait *and* overload resolution
for as_const* *and* a runtime function call.

Let's go with static_cast<const _Tp&>.


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

* Re: [PATCH 1/3] libstdc++: Implement ranges::contains/contains_subrange from P2302R4
  2022-11-14  9:04 ` [PATCH 1/3] libstdc++: Implement ranges::contains/contains_subrange from P2302R4 Jonathan Wakely
@ 2022-11-14 15:07   ` Patrick Palka
  2022-11-14 15:09     ` Jonathan Wakely
  0 siblings, 1 reply; 12+ messages in thread
From: Patrick Palka @ 2022-11-14 15:07 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: Patrick Palka, gcc-patches, libstdc++

On Mon, 14 Nov 2022, Jonathan Wakely wrote:

> On Mon, 14 Nov 2022 at 04:51, Patrick Palka via Libstdc++
> <libstdc++@gcc.gnu.org> wrote:
> >
> > Tested on x86_64-pc-linux-gnu, does this look OK for trunk?
> >
> > libstdc++-v3/ChangeLog:
> >
> >         * include/bits/ranges_algo.h (__contains_fn, contains): Define.
> >         (__contains_subrange_fn, contains_subrange): Define.
> >         * testsuite/25_algorithms/contains/1.cc: New test.
> >         * testsuite/25_algorithms/contains_subrange/1.cc: New test.
> > ---
> >  libstdc++-v3/include/bits/ranges_algo.h       | 54 +++++++++++++++++++
> >  .../testsuite/25_algorithms/contains/1.cc     | 33 ++++++++++++
> >  .../25_algorithms/contains_subrange/1.cc      | 35 ++++++++++++
> >  3 files changed, 122 insertions(+)
> >  create mode 100644 libstdc++-v3/testsuite/25_algorithms/contains/1.cc
> >  create mode 100644 libstdc++-v3/testsuite/25_algorithms/contains_subrange/1.cc
> >
> > diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h
> > index de71bd07a2f..da0ca981dc3 100644
> > --- a/libstdc++-v3/include/bits/ranges_algo.h
> > +++ b/libstdc++-v3/include/bits/ranges_algo.h
> > @@ -3464,6 +3464,60 @@ namespace ranges
> >
> >    inline constexpr __prev_permutation_fn prev_permutation{};
> >
> > +#if __cplusplus > 202002L
> > +  struct __contains_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 bool
> > +      operator()(_Iter __first, _Sent __last, const _Tp& __value, _Proj __proj = {}) const
> > +      { return ranges::find(std::move(__first), __last, __value, __proj) != __last; }
> 
> Should this use std::move(__proj)?

Oops yes, IIUC std::move'ing projections isn't necessary since they're
copyable and equality preserving, but doing so is consistent with the
rest of the ranges algos which tend to std::move function objects.

-- >8 --

Subject: [PATCH 1/3] libstdc++: Implement ranges::contains/contains_subrange
 from P2302R4

libstdc++-v3/ChangeLog:

	* include/bits/ranges_algo.h (__contains_fn, contains): Define.
	(__contains_subrange_fn, contains_subrange): Define.
	* testsuite/25_algorithms/contains/1.cc: New test.
	* testsuite/25_algorithms/contains_subrange/1.cc: New test.
---
 libstdc++-v3/include/bits/ranges_algo.h       | 54 +++++++++++++++++++
 .../testsuite/25_algorithms/contains/1.cc     | 33 ++++++++++++
 .../25_algorithms/contains_subrange/1.cc      | 37 +++++++++++++
 3 files changed, 124 insertions(+)
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/contains/1.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/contains_subrange/1.cc

diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h
index de71bd07a2f..11206bdbcaa 100644
--- a/libstdc++-v3/include/bits/ranges_algo.h
+++ b/libstdc++-v3/include/bits/ranges_algo.h
@@ -3464,6 +3464,60 @@ namespace ranges
 
   inline constexpr __prev_permutation_fn prev_permutation{};
 
+#if __cplusplus > 202002L
+  struct __contains_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 bool
+      operator()(_Iter __first, _Sent __last, const _Tp& __value, _Proj __proj = {}) const
+      { return ranges::find(std::move(__first), __last, __value, std::move(__proj)) != __last; }
+
+    template<input_range _Range, typename _Tp, typename _Proj = identity>
+      requires indirect_binary_predicate<ranges::equal_to,
+					 projected<iterator_t<_Range>, _Proj>, const _Tp*>
+      constexpr bool
+      operator()(_Range&& __r, const _Tp& __value, _Proj __proj = {}) const
+      { return (*this)(ranges::begin(__r), ranges::end(__r), __value, std::move(__proj)); }
+  };
+
+  inline constexpr __contains_fn contains{};
+
+  struct __contains_subrange_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 bool
+      operator()(_Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Sent2 __last2,
+		 _Pred __pred = {}, Proj1 __proj1 = {}, Proj2 __proj2 = {}) const
+      {
+	return __first2 == __last2
+	  || !ranges::search(__first1, __last1, __first2, __last2,
+			     std::move(__pred), std::move(__proj1), std::move(__proj2)).empty();
+      }
+
+    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 bool
+      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 __contains_subrange_fn contains_subrange{};
+#endif // C++23
 } // namespace ranges
 
 #define __cpp_lib_shift 201806L
diff --git a/libstdc++-v3/testsuite/25_algorithms/contains/1.cc b/libstdc++-v3/testsuite/25_algorithms/contains/1.cc
new file mode 100644
index 00000000000..146ab593b70
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/contains/1.cc
@@ -0,0 +1,33 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  int x[] = {1,2,3};
+  using to_input = __gnu_test::test_input_range<int>;
+  VERIFY( ranges::contains(to_input(x), 1) );
+  VERIFY( ranges::contains(to_input(x), 2) );
+  VERIFY( ranges::contains(to_input(x), 3) );
+  VERIFY( !ranges::contains(to_input(x), 4) );
+  VERIFY( !ranges::contains(x, x+2, 3) );
+  auto neg = [](int n) { return -n; };
+  VERIFY( ranges::contains(to_input(x), -1, neg) );
+  VERIFY( ranges::contains(to_input(x), -2, neg) );
+  VERIFY( ranges::contains(to_input(x), -3, neg) );
+  VERIFY( !ranges::contains(to_input(x), -4, neg) );
+
+  VERIFY( !ranges::contains(x, x+2, -3, neg) );
+}
+
+int
+main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/contains_subrange/1.cc b/libstdc++-v3/testsuite/25_algorithms/contains_subrange/1.cc
new file mode 100644
index 00000000000..6c3c99c0fd6
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/contains_subrange/1.cc
@@ -0,0 +1,37 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  int x[] = {1,2,3,4,5};
+  int y[] = {2,3,4};
+  int z[] = {4,5,6};
+  __gnu_test::test_forward_range<int> rx(x);
+  __gnu_test::test_forward_range<int> ry(y);
+  __gnu_test::test_forward_range<int> rz(z);
+  VERIFY( ranges::contains_subrange(rx, ry) );
+  VERIFY( !ranges::contains_subrange(rx, rz) );
+  VERIFY( ranges::contains_subrange(rx, ry, ranges::less{}) );
+  VERIFY( ranges::contains_subrange(rx, rz, ranges::less{}) );
+  auto plus3 = [](int n) { return n+3; };
+  VERIFY( !ranges::contains_subrange(rx, ry, {}, plus3) );
+  VERIFY( ranges::contains_subrange(rx, rz, {}, plus3) );
+  VERIFY( ranges::contains_subrange(rx, ry, {}, plus3, plus3) );
+  VERIFY( !ranges::contains_subrange(rx, rz, {}, plus3, plus3) );
+
+  VERIFY( ranges::contains_subrange(x, x+2, y, y+1) );
+  VERIFY( !ranges::contains_subrange(x, x+2, y, y+2) );
+}
+
+int
+main()
+{
+  test01();
+}
-- 
2.38.1.420.g319605f8f0


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

* Re: [PATCH 1/3] libstdc++: Implement ranges::contains/contains_subrange from P2302R4
  2022-11-14 15:07   ` Patrick Palka
@ 2022-11-14 15:09     ` Jonathan Wakely
  0 siblings, 0 replies; 12+ messages in thread
From: Jonathan Wakely @ 2022-11-14 15:09 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches, libstdc++

On Mon, 14 Nov 2022 at 15:07, Patrick Palka <ppalka@redhat.com> wrote:
>
> On Mon, 14 Nov 2022, Jonathan Wakely wrote:
>
> > On Mon, 14 Nov 2022 at 04:51, Patrick Palka via Libstdc++
> > <libstdc++@gcc.gnu.org> wrote:
> > >
> > > Tested on x86_64-pc-linux-gnu, does this look OK for trunk?
> > >
> > > libstdc++-v3/ChangeLog:
> > >
> > >         * include/bits/ranges_algo.h (__contains_fn, contains): Define.
> > >         (__contains_subrange_fn, contains_subrange): Define.
> > >         * testsuite/25_algorithms/contains/1.cc: New test.
> > >         * testsuite/25_algorithms/contains_subrange/1.cc: New test.
> > > ---
> > >  libstdc++-v3/include/bits/ranges_algo.h       | 54 +++++++++++++++++++
> > >  .../testsuite/25_algorithms/contains/1.cc     | 33 ++++++++++++
> > >  .../25_algorithms/contains_subrange/1.cc      | 35 ++++++++++++
> > >  3 files changed, 122 insertions(+)
> > >  create mode 100644 libstdc++-v3/testsuite/25_algorithms/contains/1.cc
> > >  create mode 100644 libstdc++-v3/testsuite/25_algorithms/contains_subrange/1.cc
> > >
> > > diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h
> > > index de71bd07a2f..da0ca981dc3 100644
> > > --- a/libstdc++-v3/include/bits/ranges_algo.h
> > > +++ b/libstdc++-v3/include/bits/ranges_algo.h
> > > @@ -3464,6 +3464,60 @@ namespace ranges
> > >
> > >    inline constexpr __prev_permutation_fn prev_permutation{};
> > >
> > > +#if __cplusplus > 202002L
> > > +  struct __contains_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 bool
> > > +      operator()(_Iter __first, _Sent __last, const _Tp& __value, _Proj __proj = {}) const
> > > +      { return ranges::find(std::move(__first), __last, __value, __proj) != __last; }
> >
> > Should this use std::move(__proj)?
>
> Oops yes, IIUC std::move'ing projections isn't necessary since they're
> copyable and equality preserving, but doing so is consistent with the
> rest of the ranges algos which tend to std::move function objects.

Revised patch is OK for trunk, thanks.

>
> -- >8 --
>
> Subject: [PATCH 1/3] libstdc++: Implement ranges::contains/contains_subrange
>  from P2302R4
>
> libstdc++-v3/ChangeLog:
>
>         * include/bits/ranges_algo.h (__contains_fn, contains): Define.
>         (__contains_subrange_fn, contains_subrange): Define.
>         * testsuite/25_algorithms/contains/1.cc: New test.
>         * testsuite/25_algorithms/contains_subrange/1.cc: New test.
> ---
>  libstdc++-v3/include/bits/ranges_algo.h       | 54 +++++++++++++++++++
>  .../testsuite/25_algorithms/contains/1.cc     | 33 ++++++++++++
>  .../25_algorithms/contains_subrange/1.cc      | 37 +++++++++++++
>  3 files changed, 124 insertions(+)
>  create mode 100644 libstdc++-v3/testsuite/25_algorithms/contains/1.cc
>  create mode 100644 libstdc++-v3/testsuite/25_algorithms/contains_subrange/1.cc
>
> diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h
> index de71bd07a2f..11206bdbcaa 100644
> --- a/libstdc++-v3/include/bits/ranges_algo.h
> +++ b/libstdc++-v3/include/bits/ranges_algo.h
> @@ -3464,6 +3464,60 @@ namespace ranges
>
>    inline constexpr __prev_permutation_fn prev_permutation{};
>
> +#if __cplusplus > 202002L
> +  struct __contains_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 bool
> +      operator()(_Iter __first, _Sent __last, const _Tp& __value, _Proj __proj = {}) const
> +      { return ranges::find(std::move(__first), __last, __value, std::move(__proj)) != __last; }
> +
> +    template<input_range _Range, typename _Tp, typename _Proj = identity>
> +      requires indirect_binary_predicate<ranges::equal_to,
> +                                        projected<iterator_t<_Range>, _Proj>, const _Tp*>
> +      constexpr bool
> +      operator()(_Range&& __r, const _Tp& __value, _Proj __proj = {}) const
> +      { return (*this)(ranges::begin(__r), ranges::end(__r), __value, std::move(__proj)); }
> +  };
> +
> +  inline constexpr __contains_fn contains{};
> +
> +  struct __contains_subrange_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 bool
> +      operator()(_Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Sent2 __last2,
> +                _Pred __pred = {}, Proj1 __proj1 = {}, Proj2 __proj2 = {}) const
> +      {
> +       return __first2 == __last2
> +         || !ranges::search(__first1, __last1, __first2, __last2,
> +                            std::move(__pred), std::move(__proj1), std::move(__proj2)).empty();
> +      }
> +
> +    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 bool
> +      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 __contains_subrange_fn contains_subrange{};
> +#endif // C++23
>  } // namespace ranges
>
>  #define __cpp_lib_shift 201806L
> diff --git a/libstdc++-v3/testsuite/25_algorithms/contains/1.cc b/libstdc++-v3/testsuite/25_algorithms/contains/1.cc
> new file mode 100644
> index 00000000000..146ab593b70
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/25_algorithms/contains/1.cc
> @@ -0,0 +1,33 @@
> +// { dg-options "-std=gnu++23" }
> +// { dg-do run { target c++23 } }
> +
> +#include <algorithm>
> +#include <testsuite_hooks.h>
> +#include <testsuite_iterators.h>
> +
> +namespace ranges = std::ranges;
> +
> +void
> +test01()
> +{
> +  int x[] = {1,2,3};
> +  using to_input = __gnu_test::test_input_range<int>;
> +  VERIFY( ranges::contains(to_input(x), 1) );
> +  VERIFY( ranges::contains(to_input(x), 2) );
> +  VERIFY( ranges::contains(to_input(x), 3) );
> +  VERIFY( !ranges::contains(to_input(x), 4) );
> +  VERIFY( !ranges::contains(x, x+2, 3) );
> +  auto neg = [](int n) { return -n; };
> +  VERIFY( ranges::contains(to_input(x), -1, neg) );
> +  VERIFY( ranges::contains(to_input(x), -2, neg) );
> +  VERIFY( ranges::contains(to_input(x), -3, neg) );
> +  VERIFY( !ranges::contains(to_input(x), -4, neg) );
> +
> +  VERIFY( !ranges::contains(x, x+2, -3, neg) );
> +}
> +
> +int
> +main()
> +{
> +  test01();
> +}
> diff --git a/libstdc++-v3/testsuite/25_algorithms/contains_subrange/1.cc b/libstdc++-v3/testsuite/25_algorithms/contains_subrange/1.cc
> new file mode 100644
> index 00000000000..6c3c99c0fd6
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/25_algorithms/contains_subrange/1.cc
> @@ -0,0 +1,37 @@
> +// { dg-options "-std=gnu++23" }
> +// { dg-do run { target c++23 } }
> +
> +#include <algorithm>
> +#include <testsuite_hooks.h>
> +#include <testsuite_iterators.h>
> +
> +namespace ranges = std::ranges;
> +
> +void
> +test01()
> +{
> +  int x[] = {1,2,3,4,5};
> +  int y[] = {2,3,4};
> +  int z[] = {4,5,6};
> +  __gnu_test::test_forward_range<int> rx(x);
> +  __gnu_test::test_forward_range<int> ry(y);
> +  __gnu_test::test_forward_range<int> rz(z);
> +  VERIFY( ranges::contains_subrange(rx, ry) );
> +  VERIFY( !ranges::contains_subrange(rx, rz) );
> +  VERIFY( ranges::contains_subrange(rx, ry, ranges::less{}) );
> +  VERIFY( ranges::contains_subrange(rx, rz, ranges::less{}) );
> +  auto plus3 = [](int n) { return n+3; };
> +  VERIFY( !ranges::contains_subrange(rx, ry, {}, plus3) );
> +  VERIFY( ranges::contains_subrange(rx, rz, {}, plus3) );
> +  VERIFY( ranges::contains_subrange(rx, ry, {}, plus3, plus3) );
> +  VERIFY( !ranges::contains_subrange(rx, rz, {}, plus3, plus3) );
> +
> +  VERIFY( ranges::contains_subrange(x, x+2, y, y+1) );
> +  VERIFY( !ranges::contains_subrange(x, x+2, y, y+2) );
> +}
> +
> +int
> +main()
> +{
> +  test01();
> +}
> --
> 2.38.1.420.g319605f8f0
>


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

* Re: [PATCH 2/3] libstdc++: Implement ranges::iota from P2440R1
  2022-11-14 10:20       ` Jonathan Wakely
@ 2022-11-14 15:11         ` Patrick Palka
  2022-11-14 15:12           ` Jonathan Wakely
  0 siblings, 1 reply; 12+ messages in thread
From: Patrick Palka @ 2022-11-14 15:11 UTC (permalink / raw)
  To: Jonathan Wakely
  Cc: Daniel Krügler, Patrick Palka, gcc-patches, libstdc++

[-- Attachment #1: Type: text/plain, Size: 6639 bytes --]

On Mon, 14 Nov 2022, Jonathan Wakely wrote:

> On Mon, 14 Nov 2022 at 10:17, Daniel Krügler <daniel.kruegler@gmail.com> wrote:
> >
> > Am Mo., 14. Nov. 2022 um 11:09 Uhr schrieb Jonathan Wakely via
> > Libstdc++ <libstdc++@gcc.gnu.org>:
> > >
> > > On Mon, 14 Nov 2022 at 04:52, Patrick Palka via Libstdc++
> > > <libstdc++@gcc.gnu.org> wrote:
> > > >
> > > > Tested on x86_64-pc-linux-gnu, does this look OK for trunk?
> > > >
> > > > libstdc++-v3/ChangeLog:
> > > >
> > > >         * include/bits/ranges_algo.h (out_value_result): Define.
> > > >         (iota_result): Define.
> > > >         (__iota_fn, iota): Define.
> > > >         * testsuite/25_algorithms/iota/1.cc: New test.
> > > > ---
> > > >  libstdc++-v3/include/bits/ranges_algo.h       | 48 +++++++++++++++++++
> > > >  .../testsuite/25_algorithms/iota/1.cc         | 29 +++++++++++
> > > >  2 files changed, 77 insertions(+)
> > > >  create mode 100644 libstdc++-v3/testsuite/25_algorithms/iota/1.cc
> > > >
> > > > diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h
> > > > index da0ca981dc3..f003117c569 100644
> > > > --- a/libstdc++-v3/include/bits/ranges_algo.h
> > > > +++ b/libstdc++-v3/include/bits/ranges_algo.h
> > > > @@ -3517,6 +3517,54 @@ namespace ranges
> > > >    };
> > > >
> > > >    inline constexpr __contains_subrange_fn contains_subrange{};
> > > > +
> > > > +  template<typename _Out, typename _Tp>
> > > > +    struct out_value_result
> > > > +    {
> > > > +      [[no_unique_address]] _Out out;
> > > > +      [[no_unique_address]] _Tp value;
> > > > +
> > > > +      template<typename _Out2, typename _Tp2>
> > > > +       requires convertible_to<const _Out&, _Out2>
> > > > +         && convertible_to<const _Tp&, _Tp2>
> > > > +       constexpr
> > > > +       operator out_value_result<_Out2, _Tp2>() const &
> > > > +       { return {out, value}; }
> > > > +
> > > > +      template<typename _Out2, typename _Tp2>
> > > > +       requires convertible_to<_Out, _Out2>
> > > > +         && convertible_to<_Tp, _Tp2>
> > > > +       constexpr
> > > > +       operator out_value_result<_Out2, _Tp2>() &&
> > > > +       { return {std::move(out), std::move(value)}; }
> > > > +    };
> > > > +
> > > > +  template<typename _Out, typename _Tp>
> > > > +    using iota_result = out_value_result<_Out, _Tp>;
> > > > +
> > > > +  struct __iota_fn
> > > > +  {
> > > > +    template<input_or_output_iterator _Out, sentinel_for<_Out> _Sent, weakly_incrementable _Tp>
> > > > +      requires indirectly_writable<_Out, const _Tp&>
> > > > +      constexpr iota_result<_Out, _Tp>
> > > > +      operator()(_Out __first, _Sent __last, _Tp __value) const
> > > > +      {
> > > > +       while (__first != __last)
> > > > +         {
> > > > +           *__first = static_cast<add_const_t<_Tp>&>(__value);
> > >
> > > Is this any different to const_cast<const _Tp&>(__value) ?
> >
> > I think it is. const_cast<const _Tp&> can potentially mean the removal
> > of volatile,
> 
> True.
> 
> > so I would always look with suspicion on const_cast<const
> > _Tp&>, while static_cast is clearer. Alternatively, as_const could be
> > used, which does add_const_t.
> 
> Which means evaluating the add_const trait *and* overload resolution
> for as_const* *and* a runtime function call.
> 
> Let's go with static_cast<const _Tp&>.

Sounds good, like so?

-- >8 --


Subject: [PATCH 2/3] libstdc++: Implement ranges::iota from P2440R1

libstdc++-v3/ChangeLog:

	* include/bits/ranges_algo.h (out_value_result): Define.
	(iota_result): Define.
	(__iota_fn, iota): Define.
	* testsuite/25_algorithms/iota/1.cc: New test.
---
 libstdc++-v3/include/bits/ranges_algo.h       | 48 +++++++++++++++++++
 .../testsuite/25_algorithms/iota/1.cc         | 29 +++++++++++
 2 files changed, 77 insertions(+)
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/iota/1.cc

diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h
index 11206bdbcaa..f75735f02cb 100644
--- a/libstdc++-v3/include/bits/ranges_algo.h
+++ b/libstdc++-v3/include/bits/ranges_algo.h
@@ -3517,6 +3517,54 @@ namespace ranges
   };
 
   inline constexpr __contains_subrange_fn contains_subrange{};
+
+  template<typename _Out, typename _Tp>
+    struct out_value_result
+    {
+      [[no_unique_address]] _Out out;
+      [[no_unique_address]] _Tp value;
+
+      template<typename _Out2, typename _Tp2>
+	requires convertible_to<const _Out&, _Out2>
+	  && convertible_to<const _Tp&, _Tp2>
+	constexpr
+	operator out_value_result<_Out2, _Tp2>() const &
+	{ return {out, value}; }
+
+      template<typename _Out2, typename _Tp2>
+	requires convertible_to<_Out, _Out2>
+	  && convertible_to<_Tp, _Tp2>
+	constexpr
+	operator out_value_result<_Out2, _Tp2>() &&
+	{ return {std::move(out), std::move(value)}; }
+    };
+
+  template<typename _Out, typename _Tp>
+    using iota_result = out_value_result<_Out, _Tp>;
+
+  struct __iota_fn
+  {
+    template<input_or_output_iterator _Out, sentinel_for<_Out> _Sent, weakly_incrementable _Tp>
+      requires indirectly_writable<_Out, const _Tp&>
+      constexpr iota_result<_Out, _Tp>
+      operator()(_Out __first, _Sent __last, _Tp __value) const
+      {
+	while (__first != __last)
+	  {
+	    *__first = static_cast<const _Tp&>(__value);
+	    ++__first;
+	    ++__value;
+	  }
+	return {std::move(__first), std::move(__value)};
+      }
+
+    template<weakly_incrementable _Tp, output_range<const _Tp&> _Range>
+      constexpr iota_result<borrowed_iterator_t<_Range>, _Tp>
+      operator()(_Range&& __r, _Tp __value) const
+      { return (*this)(ranges::begin(__r), ranges::end(__r), std::move(__value)); }
+  };
+
+  inline constexpr __iota_fn iota{};
 #endif // C++23
 } // namespace ranges
 
diff --git a/libstdc++-v3/testsuite/25_algorithms/iota/1.cc b/libstdc++-v3/testsuite/25_algorithms/iota/1.cc
new file mode 100644
index 00000000000..ad2bf08adf5
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/iota/1.cc
@@ -0,0 +1,29 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <algorithm>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+namespace ranges = std::ranges;
+
+void
+test01()
+{
+  int x[3] = {};
+  __gnu_test::test_output_range<int> rx(x);
+  auto r0 = ranges::iota(rx, 0);
+  VERIFY( r0.out.ptr == x+3 );
+  VERIFY( r0.value == 3 );
+  VERIFY( ranges::equal(x, (int[]){0,1,2}) );
+  auto r1 = ranges::iota(x, x+2, 5);
+  VERIFY( r1.out == x+2 );
+  VERIFY( r1.value == 7 );
+  VERIFY( ranges::equal(x, (int[]){5,6,2}) );
+}
+
+int
+main()
+{
+  test01();
+}
-- 
2.38.1.420.g319605f8f0

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

* Re: [PATCH 3/3] libstdc++: Implement ranges::find_last{, _if, _if_not} from P1223R5
  2022-11-14  4:50 ` [PATCH 3/3] libstdc++: Implement ranges::find_last{,_if,_if_not} from P1223R5 Patrick Palka
@ 2022-11-14 15:12   ` Jonathan Wakely
  0 siblings, 0 replies; 12+ messages in thread
From: Jonathan Wakely @ 2022-11-14 15:12 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches, libstdc++

On Mon, 14 Nov 2022 at 04:51, Patrick Palka via Libstdc++
<libstdc++@gcc.gnu.org> wrote:
>
> Tested on x86_64-pc-linux-gnu, does this look OK for trunk?
>
> libstdc++-v3/ChangeLog:
>
>         * include/bits/ranges_algo.h (__find_last_fn, find_last):
>         Define.
>         (__find_last_if_fn, find_last_if): Define.
>         (__find_last_if_not_fn, find_last_if_not): Define.
>         * testsuite/25_algorithms/find_last/1.cc: New test.
>         * testsuite/25_algorithms/find_last_if/1.cc: New test.
>         * testsuite/25_algorithms/find_last_if_not/1.cc: New test.
> ---
>  libstdc++-v3/include/bits/ranges_algo.h       | 123 ++++++++++++++++++
>  .../testsuite/25_algorithms/find_last/1.cc    |  90 +++++++++++++
>  .../testsuite/25_algorithms/find_last_if/1.cc |  92 +++++++++++++
>  .../25_algorithms/find_last_if_not/1.cc       |  92 +++++++++++++
>  4 files changed, 397 insertions(+)
>  create mode 100644 libstdc++-v3/testsuite/25_algorithms/find_last/1.cc
>  create mode 100644 libstdc++-v3/testsuite/25_algorithms/find_last_if/1.cc
>  create mode 100644 libstdc++-v3/testsuite/25_algorithms/find_last_if_not/1.cc
>
> diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h
> index f003117c569..0e4329382eb 100644
> --- a/libstdc++-v3/include/bits/ranges_algo.h
> +++ b/libstdc++-v3/include/bits/ranges_algo.h
> @@ -3565,6 +3565,129 @@ namespace ranges
>    };
>
>    inline constexpr __iota_fn iota{};
> +
> +  struct __find_last_fn
> +  {
> +    template<forward_iterator _Iter, sentinel_for<_Iter> _Sent, typename T, typename _Proj = identity>
> +      requires indirect_binary_predicate<ranges::equal_to, projected<_Iter, _Proj>, const T*>
> +      constexpr subrange<_Iter>
> +      operator()(_Iter __first, _Sent __last, const T& __value, _Proj __proj = {}) const
> +      {
> +       if constexpr (same_as<_Iter, _Sent> && bidirectional_iterator<_Iter>)
> +         {
> +           _Iter __found = ranges::find(reverse_iterator<_Iter>{__last},
> +                                        reverse_iterator<_Iter>{__first},
> +                                        __value, __proj).base();
> +           if (__found == __first)
> +             return {__last, __last};
> +           else
> +             return {ranges::prev(__found), __last};
> +         }
> +       else
> +         {
> +           _Iter __found = ranges::find(__first, __last, __value, __proj);

std::move(__proj) here too, for consistency.

> +           if (__found == __last)
> +             return {__found, __found};
> +           for (;;)
> +             {
> +               __first = ranges::find(ranges::next(__first), __last, __value, __proj);

And here.

> +               if (__first == __last)
> +                 return {__found, __first};
> +               __found = __first;
> +             }
> +         }
> +      }
> +
> +    template<forward_range _Range, typename T, typename _Proj = identity>
> +      requires indirect_binary_predicate<ranges::equal_to, projected<iterator_t<_Range>, _Proj>, const T*>
> +      constexpr borrowed_subrange_t<_Range>
> +      operator()(_Range&& __r, const T& __value, _Proj __proj = {}) const
> +      { return (*this)(ranges::begin(__r), ranges::end(__r), __value, std::move(__proj)); }
> +  };
> +
> +  inline constexpr __find_last_fn find_last{};
> +
> +  struct __find_last_if_fn
> +  {
> +    template<forward_iterator _Iter, sentinel_for<_Iter> _Sent, typename _Proj = identity,
> +            indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
> +      constexpr subrange<_Iter>
> +      operator()(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {}) const
> +      {
> +       if constexpr (same_as<_Iter, _Sent> && bidirectional_iterator<_Iter>)
> +         {
> +           _Iter __found = ranges::find_if(reverse_iterator<_Iter>{__last},
> +                                           reverse_iterator<_Iter>{__first},
> +                                           __pred, __proj).base();

And here, and std::move(__pred) too, I think.

OK for trunk with those changes here (and the later cases).


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

* Re: [PATCH 2/3] libstdc++: Implement ranges::iota from P2440R1
  2022-11-14 15:11         ` Patrick Palka
@ 2022-11-14 15:12           ` Jonathan Wakely
  0 siblings, 0 replies; 12+ messages in thread
From: Jonathan Wakely @ 2022-11-14 15:12 UTC (permalink / raw)
  To: Patrick Palka; +Cc: Daniel Krügler, gcc-patches, libstdc++

On Mon, 14 Nov 2022 at 15:11, Patrick Palka <ppalka@redhat.com> wrote:
>
> On Mon, 14 Nov 2022, Jonathan Wakely wrote:
>
> > On Mon, 14 Nov 2022 at 10:17, Daniel Krügler <daniel.kruegler@gmail.com> wrote:
> > >
> > > Am Mo., 14. Nov. 2022 um 11:09 Uhr schrieb Jonathan Wakely via
> > > Libstdc++ <libstdc++@gcc.gnu.org>:
> > > >
> > > > On Mon, 14 Nov 2022 at 04:52, Patrick Palka via Libstdc++
> > > > <libstdc++@gcc.gnu.org> wrote:
> > > > >
> > > > > Tested on x86_64-pc-linux-gnu, does this look OK for trunk?
> > > > >
> > > > > libstdc++-v3/ChangeLog:
> > > > >
> > > > >         * include/bits/ranges_algo.h (out_value_result): Define.
> > > > >         (iota_result): Define.
> > > > >         (__iota_fn, iota): Define.
> > > > >         * testsuite/25_algorithms/iota/1.cc: New test.
> > > > > ---
> > > > >  libstdc++-v3/include/bits/ranges_algo.h       | 48 +++++++++++++++++++
> > > > >  .../testsuite/25_algorithms/iota/1.cc         | 29 +++++++++++
> > > > >  2 files changed, 77 insertions(+)
> > > > >  create mode 100644 libstdc++-v3/testsuite/25_algorithms/iota/1.cc
> > > > >
> > > > > diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h
> > > > > index da0ca981dc3..f003117c569 100644
> > > > > --- a/libstdc++-v3/include/bits/ranges_algo.h
> > > > > +++ b/libstdc++-v3/include/bits/ranges_algo.h
> > > > > @@ -3517,6 +3517,54 @@ namespace ranges
> > > > >    };
> > > > >
> > > > >    inline constexpr __contains_subrange_fn contains_subrange{};
> > > > > +
> > > > > +  template<typename _Out, typename _Tp>
> > > > > +    struct out_value_result
> > > > > +    {
> > > > > +      [[no_unique_address]] _Out out;
> > > > > +      [[no_unique_address]] _Tp value;
> > > > > +
> > > > > +      template<typename _Out2, typename _Tp2>
> > > > > +       requires convertible_to<const _Out&, _Out2>
> > > > > +         && convertible_to<const _Tp&, _Tp2>
> > > > > +       constexpr
> > > > > +       operator out_value_result<_Out2, _Tp2>() const &
> > > > > +       { return {out, value}; }
> > > > > +
> > > > > +      template<typename _Out2, typename _Tp2>
> > > > > +       requires convertible_to<_Out, _Out2>
> > > > > +         && convertible_to<_Tp, _Tp2>
> > > > > +       constexpr
> > > > > +       operator out_value_result<_Out2, _Tp2>() &&
> > > > > +       { return {std::move(out), std::move(value)}; }
> > > > > +    };
> > > > > +
> > > > > +  template<typename _Out, typename _Tp>
> > > > > +    using iota_result = out_value_result<_Out, _Tp>;
> > > > > +
> > > > > +  struct __iota_fn
> > > > > +  {
> > > > > +    template<input_or_output_iterator _Out, sentinel_for<_Out> _Sent, weakly_incrementable _Tp>
> > > > > +      requires indirectly_writable<_Out, const _Tp&>
> > > > > +      constexpr iota_result<_Out, _Tp>
> > > > > +      operator()(_Out __first, _Sent __last, _Tp __value) const
> > > > > +      {
> > > > > +       while (__first != __last)
> > > > > +         {
> > > > > +           *__first = static_cast<add_const_t<_Tp>&>(__value);
> > > >
> > > > Is this any different to const_cast<const _Tp&>(__value) ?
> > >
> > > I think it is. const_cast<const _Tp&> can potentially mean the removal
> > > of volatile,
> >
> > True.
> >
> > > so I would always look with suspicion on const_cast<const
> > > _Tp&>, while static_cast is clearer. Alternatively, as_const could be
> > > used, which does add_const_t.
> >
> > Which means evaluating the add_const trait *and* overload resolution
> > for as_const* *and* a runtime function call.
> >
> > Let's go with static_cast<const _Tp&>.
>
> Sounds good, like so?

OK for trunk, thanks.


>
> -- >8 --
>
>
> Subject: [PATCH 2/3] libstdc++: Implement ranges::iota from P2440R1
>
> libstdc++-v3/ChangeLog:
>
>         * include/bits/ranges_algo.h (out_value_result): Define.
>         (iota_result): Define.
>         (__iota_fn, iota): Define.
>         * testsuite/25_algorithms/iota/1.cc: New test.
> ---
>  libstdc++-v3/include/bits/ranges_algo.h       | 48 +++++++++++++++++++
>  .../testsuite/25_algorithms/iota/1.cc         | 29 +++++++++++
>  2 files changed, 77 insertions(+)
>  create mode 100644 libstdc++-v3/testsuite/25_algorithms/iota/1.cc
>
> diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h
> index 11206bdbcaa..f75735f02cb 100644
> --- a/libstdc++-v3/include/bits/ranges_algo.h
> +++ b/libstdc++-v3/include/bits/ranges_algo.h
> @@ -3517,6 +3517,54 @@ namespace ranges
>    };
>
>    inline constexpr __contains_subrange_fn contains_subrange{};
> +
> +  template<typename _Out, typename _Tp>
> +    struct out_value_result
> +    {
> +      [[no_unique_address]] _Out out;
> +      [[no_unique_address]] _Tp value;
> +
> +      template<typename _Out2, typename _Tp2>
> +       requires convertible_to<const _Out&, _Out2>
> +         && convertible_to<const _Tp&, _Tp2>
> +       constexpr
> +       operator out_value_result<_Out2, _Tp2>() const &
> +       { return {out, value}; }
> +
> +      template<typename _Out2, typename _Tp2>
> +       requires convertible_to<_Out, _Out2>
> +         && convertible_to<_Tp, _Tp2>
> +       constexpr
> +       operator out_value_result<_Out2, _Tp2>() &&
> +       { return {std::move(out), std::move(value)}; }
> +    };
> +
> +  template<typename _Out, typename _Tp>
> +    using iota_result = out_value_result<_Out, _Tp>;
> +
> +  struct __iota_fn
> +  {
> +    template<input_or_output_iterator _Out, sentinel_for<_Out> _Sent, weakly_incrementable _Tp>
> +      requires indirectly_writable<_Out, const _Tp&>
> +      constexpr iota_result<_Out, _Tp>
> +      operator()(_Out __first, _Sent __last, _Tp __value) const
> +      {
> +       while (__first != __last)
> +         {
> +           *__first = static_cast<const _Tp&>(__value);
> +           ++__first;
> +           ++__value;
> +         }
> +       return {std::move(__first), std::move(__value)};
> +      }
> +
> +    template<weakly_incrementable _Tp, output_range<const _Tp&> _Range>
> +      constexpr iota_result<borrowed_iterator_t<_Range>, _Tp>
> +      operator()(_Range&& __r, _Tp __value) const
> +      { return (*this)(ranges::begin(__r), ranges::end(__r), std::move(__value)); }
> +  };
> +
> +  inline constexpr __iota_fn iota{};
>  #endif // C++23
>  } // namespace ranges
>
> diff --git a/libstdc++-v3/testsuite/25_algorithms/iota/1.cc b/libstdc++-v3/testsuite/25_algorithms/iota/1.cc
> new file mode 100644
> index 00000000000..ad2bf08adf5
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/25_algorithms/iota/1.cc
> @@ -0,0 +1,29 @@
> +// { dg-options "-std=gnu++23" }
> +// { dg-do run { target c++23 } }
> +
> +#include <algorithm>
> +#include <testsuite_hooks.h>
> +#include <testsuite_iterators.h>
> +
> +namespace ranges = std::ranges;
> +
> +void
> +test01()
> +{
> +  int x[3] = {};
> +  __gnu_test::test_output_range<int> rx(x);
> +  auto r0 = ranges::iota(rx, 0);
> +  VERIFY( r0.out.ptr == x+3 );
> +  VERIFY( r0.value == 3 );
> +  VERIFY( ranges::equal(x, (int[]){0,1,2}) );
> +  auto r1 = ranges::iota(x, x+2, 5);
> +  VERIFY( r1.out == x+2 );
> +  VERIFY( r1.value == 7 );
> +  VERIFY( ranges::equal(x, (int[]){5,6,2}) );
> +}
> +
> +int
> +main()
> +{
> +  test01();
> +}
> --
> 2.38.1.420.g319605f8f0


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

end of thread, other threads:[~2022-11-14 15:13 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-14  4:50 [PATCH 1/3] libstdc++: Implement ranges::contains/contains_subrange from P2302R4 Patrick Palka
2022-11-14  4:50 ` [PATCH 2/3] libstdc++: Implement ranges::iota from P2440R1 Patrick Palka
2022-11-14 10:08   ` Jonathan Wakely
2022-11-14 10:17     ` Daniel Krügler
2022-11-14 10:20       ` Jonathan Wakely
2022-11-14 15:11         ` Patrick Palka
2022-11-14 15:12           ` Jonathan Wakely
2022-11-14  4:50 ` [PATCH 3/3] libstdc++: Implement ranges::find_last{,_if,_if_not} from P1223R5 Patrick Palka
2022-11-14 15:12   ` [PATCH 3/3] libstdc++: Implement ranges::find_last{, _if, _if_not} " Jonathan Wakely
2022-11-14  9:04 ` [PATCH 1/3] libstdc++: Implement ranges::contains/contains_subrange from P2302R4 Jonathan Wakely
2022-11-14 15:07   ` Patrick Palka
2022-11-14 15:09     ` 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).