I started to work on ranges::equal to find out if what I am trying to do is totally silly.

With this patch ranges::equal is in pare with std::equal specializations that is to say that it correctly deals with Debug mode or std::deque iterators.

Once below patch is in:

https://gcc.gnu.org/ml/libstdc++/2019-12/msg00032.html

We will even be able to call std::__equal_aux1 directly using __niter_base to get rid of the Debug safe iterator layer. And even in this case get rid of the branch __use_memcmp and leave it to __equal_aux1.

In addition to testsuite I checked running gdb that it does the right thing.

Ok to commit ?

    libstdc++ Leverage on std::equal plumbing in ranges::equal.

    Benefit from the std::equal plumbing to correctly deal with
    _GLIBCXX_DEBUG mode and std::deque iterators.

            * include/bits/ranges_algobase.h (__equal_fn::operator()):
            Review conditions to call std::__equal_aux.
            * testsuite/25_algorithms/equal/constrained.cc (test04): New.

François


diff --git a/libstdc++-v3/include/bits/ranges_algobase.h b/libstdc++-v3/include/bits/ranges_algobase.h
index 80c9a774301..d4f89cb9fb2 100644
--- a/libstdc++-v3/include/bits/ranges_algobase.h
+++ b/libstdc++-v3/include/bits/ranges_algobase.h
@@ -82,8 +82,6 @@ namespace ranges
 		 _Iter2 __first2, _Sent2 __last2, _Pred __pred = {},
 		 _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const
       {
-	// TODO: implement more specializations to at least have parity with
-	// std::equal.
 	if constexpr (__detail::__is_normal_iterator<_Iter1>
 		      || __detail::__is_normal_iterator<_Iter2>)
 	  return (*this)(std::__niter_base(std::move(__first1)),
@@ -100,19 +98,24 @@ namespace ranges
 	    if (__d1 != __d2)
 	      return false;
 
+	    if (!__d1)
+	      return true;
+
+	    constexpr bool __is_simple_equal
+	      = (is_same_v<_Pred, ranges::equal_to>
+		 && is_same_v<_Proj1, identity>
+		 && is_same_v<_Proj2, identity>);
+	    if constexpr (__is_simple_equal)
+	      {
 		using _ValueType1 = iter_value_t<_Iter1>;
 		using _ValueType2 = iter_value_t<_Iter2>;
 		constexpr bool __use_memcmp
 		  = ((is_integral_v<_ValueType1> || is_pointer_v<_ValueType1>)
-		 && __memcmpable<_Iter1, _Iter2>::__value
-		 && is_same_v<_Pred, ranges::equal_to>
-		 && is_same_v<_Proj1, identity>
-		 && is_same_v<_Proj2, identity>);
+		     && __memcmpable<_Iter1, _Iter2>::__value);
 		if constexpr (__use_memcmp)
-	      {
-		if (const size_t __len = (__last1 - __first1))
-		  return !std::__memcmp(__first1, __first2, __len);
-		return true;
+		  return !std::__memcmp(__first1, __first2, __d1);
+		else
+		  return std::__equal_aux(__first1, __first1 + __d1, __first2);
 	      }
 	    else
 	      {
diff --git a/libstdc++-v3/testsuite/25_algorithms/equal/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/equal/constrained.cc
index 231bd8cfeaa..aa27fd195a2 100644
--- a/libstdc++-v3/testsuite/25_algorithms/equal/constrained.cc
+++ b/libstdc++-v3/testsuite/25_algorithms/equal/constrained.cc
@@ -19,6 +19,9 @@
 // { dg-do run { target c++2a } }
 
 #include <algorithm>
+#include <vector>
+#include <deque>
+
 #include <testsuite_hooks.h>
 #include <testsuite_iterators.h>
 
@@ -87,10 +90,21 @@ test03()
   VERIFY( !ranges::equal(x, z) );
 }
 
+void
+test04()
+{
+  std::deque<int> x = { {2}, {2}, {6}, {8}, {10}, {11} };
+  std::deque<int> y = { {2}, {2}, {6}, {8}, {10}, {11} };
+  std::deque<int> z = { {2}, {2}, {6}, {8}, {10}, {12} };
+  VERIFY( ranges::equal(x, y) );
+  VERIFY( !ranges::equal(x, z) );
+}
+
 int
 main()
 {
   test01();
   test02();
   test03();
+  test04();
 }

Reply via email to