[libcxx-commits] [libcxx] [libc++] Backport segmented iterator optimization for std::for_each to C++03 (PR #134960)

Peng Liu via libcxx-commits libcxx-commits at lists.llvm.org
Thu Apr 10 12:53:28 PDT 2025


https://github.com/winner245 updated https://github.com/llvm/llvm-project/pull/134960

>From 0193ae137c03494f8ebaa16a9e6fe974fccb8cb2 Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Tue, 8 Apr 2025 18:47:48 -0400
Subject: [PATCH 1/2] Backport for_each segmented iterator optimization to
 C++03

---
 libcxx/include/__algorithm/for_each.h | 51 +++++++++++++++++----------
 1 file changed, 33 insertions(+), 18 deletions(-)

diff --git a/libcxx/include/__algorithm/for_each.h b/libcxx/include/__algorithm/for_each.h
index e08f583504c01..c5d1576c09d7d 100644
--- a/libcxx/include/__algorithm/for_each.h
+++ b/libcxx/include/__algorithm/for_each.h
@@ -13,7 +13,7 @@
 #include <__algorithm/for_each_segment.h>
 #include <__config>
 #include <__iterator/segmented_iterator.h>
-#include <__ranges/movable_box.h>
+#include <__type_traits/enable_if.h>
 #include <__utility/in_place.h>
 #include <__utility/move.h>
 
@@ -26,28 +26,43 @@ _LIBCPP_PUSH_MACROS
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-template <class _InputIterator, class _Function>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Function
-for_each(_InputIterator __first, _InputIterator __last, _Function __f) {
+template <class _InputIterator, class _Sent, class _Func>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __for_each(_InputIterator __first, _Sent __last, _Func& __f) {
   for (; __first != __last; ++__first)
     __f(*__first);
-  return __f;
 }
 
-// __movable_box is available in C++20, but is actually a copyable-box, so optimization is only correct in C++23
-#if _LIBCPP_STD_VER >= 23
-template <class _SegmentedIterator, class _Function>
-  requires __is_segmented_iterator<_SegmentedIterator>::value
-_LIBCPP_HIDE_FROM_ABI constexpr _Function
-for_each(_SegmentedIterator __first, _SegmentedIterator __last, _Function __func) {
-  ranges::__movable_box<_Function> __wrapped_func(in_place, std::move(__func));
-  std::__for_each_segment(__first, __last, [&](auto __lfirst, auto __llast) {
-    __wrapped_func =
-        ranges::__movable_box<_Function>(in_place, std::for_each(__lfirst, __llast, std::move(*__wrapped_func)));
-  });
-  return std::move(*__wrapped_func);
+// __segment_processor handles the per-segment processing by applying the function object __func_ to each
+// element within the segment.
+template <class _SegmentedIterator, class _Func>
+struct __segment_processor {
+  using _Traits _LIBCPP_NODEBUG = __segmented_iterator_traits<_SegmentedIterator>;
+
+  _Func& __func_;
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __segment_processor(_Func& __f) : __func_(__f) {}
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
+  operator()(typename _Traits::__local_iterator __lfirst, typename _Traits::__local_iterator __llast) {
+    std::__for_each(__lfirst, __llast, __func_);
+  }
+};
+
+template <class _SegmentedIterator,
+          class _Function,
+          __enable_if_t<__is_segmented_iterator<_SegmentedIterator>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Function
+__for_each(_SegmentedIterator __first, _SegmentedIterator __last, _Function __func) {
+  std::__for_each_segment(__first, __last, std::__segment_processor<_SegmentedIterator, _Function>(__func));
+  return __func;
+}
+
+template <class _InputIterator, class _Function>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Function
+for_each(_InputIterator __first, _InputIterator __last, _Function __f) {
+  std::__for_each(__first, __last, __f);
+  return __f;
 }
-#endif // _LIBCPP_STD_VER >= 23
 
 _LIBCPP_END_NAMESPACE_STD
 

>From 5aa59ab306c25122e2c8180c61d327929372c3ac Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Thu, 10 Apr 2025 15:52:33 -0400
Subject: [PATCH 2/2] Fix reviewer comments

---
 libcxx/include/__algorithm/for_each.h          | 15 +++++++--------
 libcxx/include/__algorithm/for_each_segment.h  |  8 ++++----
 libcxx/include/algorithm                       |  1 +
 libcxx/include/array                           |  1 +
 libcxx/include/bitset                          |  1 +
 libcxx/include/codecvt                         |  1 +
 libcxx/include/condition_variable              |  1 +
 libcxx/include/ios                             |  1 +
 libcxx/include/locale                          |  1 +
 libcxx/include/streambuf                       |  1 +
 libcxx/include/string                          |  1 +
 libcxx/include/string_view                     |  1 +
 libcxx/include/system_error                    |  1 +
 libcxx/include/vector                          |  1 +
 ...robust_against_copying_comparators.pass.cpp | 18 ++++++++++++++++++
 15 files changed, 41 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__algorithm/for_each.h b/libcxx/include/__algorithm/for_each.h
index c5d1576c09d7d..a52bdaed4ce49 100644
--- a/libcxx/include/__algorithm/for_each.h
+++ b/libcxx/include/__algorithm/for_each.h
@@ -34,16 +34,16 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __for_each(_InputIterat
 
 // __segment_processor handles the per-segment processing by applying the function object __func_ to each
 // element within the segment.
-template <class _SegmentedIterator, class _Func>
+template <class _Func>
 struct __segment_processor {
-  using _Traits _LIBCPP_NODEBUG = __segmented_iterator_traits<_SegmentedIterator>;
-
   _Func& __func_;
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __segment_processor(_Func& __f) : __func_(__f) {}
 
+  template <class _SegmentedIterator>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
-  operator()(typename _Traits::__local_iterator __lfirst, typename _Traits::__local_iterator __llast) {
+  operator()(typename __segmented_iterator_traits<_SegmentedIterator>::__local_iterator __lfirst,
+             typename __segmented_iterator_traits<_SegmentedIterator>::__local_iterator __llast) {
     std::__for_each(__lfirst, __llast, __func_);
   }
 };
@@ -51,10 +51,9 @@ struct __segment_processor {
 template <class _SegmentedIterator,
           class _Function,
           __enable_if_t<__is_segmented_iterator<_SegmentedIterator>::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Function
-__for_each(_SegmentedIterator __first, _SegmentedIterator __last, _Function __func) {
-  std::__for_each_segment(__first, __last, std::__segment_processor<_SegmentedIterator, _Function>(__func));
-  return __func;
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
+__for_each(_SegmentedIterator __first, _SegmentedIterator __last, _Function& __func) {
+  std::__for_each_segment(__first, __last, std::__segment_processor<_Function>(__func));
 }
 
 template <class _InputIterator, class _Function>
diff --git a/libcxx/include/__algorithm/for_each_segment.h b/libcxx/include/__algorithm/for_each_segment.h
index 93aa8259b2f7f..a96174c70a6bc 100644
--- a/libcxx/include/__algorithm/for_each_segment.h
+++ b/libcxx/include/__algorithm/for_each_segment.h
@@ -32,20 +32,20 @@ __for_each_segment(_SegmentedIterator __first, _SegmentedIterator __last, _Funct
 
   // We are in a single segment, so we might not be at the beginning or end
   if (__sfirst == __slast) {
-    __func(_Traits::__local(__first), _Traits::__local(__last));
+    __func.template operator()<_SegmentedIterator>(_Traits::__local(__first), _Traits::__local(__last));
     return;
   }
 
   // We have more than one segment. Iterate over the first segment, since we might not start at the beginning
-  __func(_Traits::__local(__first), _Traits::__end(__sfirst));
+  __func.template operator()<_SegmentedIterator>(_Traits::__local(__first), _Traits::__end(__sfirst));
   ++__sfirst;
   // iterate over the segments which are guaranteed to be completely in the range
   while (__sfirst != __slast) {
-    __func(_Traits::__begin(__sfirst), _Traits::__end(__sfirst));
+    __func.template operator()<_SegmentedIterator>(_Traits::__begin(__sfirst), _Traits::__end(__sfirst));
     ++__sfirst;
   }
   // iterate over the last segment
-  __func(_Traits::__begin(__sfirst), _Traits::__local(__last));
+  __func.template operator()<_SegmentedIterator>(_Traits::__begin(__sfirst), _Traits::__local(__last));
 }
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm
index bf67d3363a595..64e9fa6306145 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -2061,6 +2061,7 @@ template <class BidirectionalIterator, class Compare>
 #    include <cstring>
 #    include <iterator>
 #    include <memory>
+#    include <optional>
 #    include <stdexcept>
 #    include <type_traits>
 #    include <utility>
diff --git a/libcxx/include/array b/libcxx/include/array
index 6e3a1d82abb1b..ff46838e2e8e2 100644
--- a/libcxx/include/array
+++ b/libcxx/include/array
@@ -566,6 +566,7 @@ _LIBCPP_POP_MACROS
 #    include <cstdlib>
 #    include <iterator>
 #    include <new>
+#    include <optional>
 #    include <type_traits>
 #    include <utility>
 #  endif
diff --git a/libcxx/include/bitset b/libcxx/include/bitset
index c403c533db7e9..9273ccabbb4e3 100644
--- a/libcxx/include/bitset
+++ b/libcxx/include/bitset
@@ -973,6 +973,7 @@ _LIBCPP_POP_MACROS
 #  if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20
 #    include <concepts>
 #    include <cstdlib>
+#    include <optional>
 #    include <type_traits>
 #  endif
 #endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
diff --git a/libcxx/include/codecvt b/libcxx/include/codecvt
index 9f241b734d694..33ade1d298a7e 100644
--- a/libcxx/include/codecvt
+++ b/libcxx/include/codecvt
@@ -596,6 +596,7 @@ _LIBCPP_END_NAMESPACE_STD
 #    include <limits>
 #    include <mutex>
 #    include <new>
+#    include <optional>
 #    include <stdexcept>
 #    include <type_traits>
 #    include <typeinfo>
diff --git a/libcxx/include/condition_variable b/libcxx/include/condition_variable
index 7f44990547f55..99c74b02807ae 100644
--- a/libcxx/include/condition_variable
+++ b/libcxx/include/condition_variable
@@ -357,6 +357,7 @@ _LIBCPP_POP_MACROS
 #    include <initializer_list>
 #    include <iosfwd>
 #    include <new>
+#    include <optional>
 #    include <stdexcept>
 #    include <system_error>
 #    include <type_traits>
diff --git a/libcxx/include/ios b/libcxx/include/ios
index 9d2968753c507..9e48ec88ce59d 100644
--- a/libcxx/include/ios
+++ b/libcxx/include/ios
@@ -887,6 +887,7 @@ _LIBCPP_POP_MACROS
 #    include <limits>
 #    include <mutex>
 #    include <new>
+#    include <optional>
 #    include <stdexcept>
 #    include <system_error>
 #    include <type_traits>
diff --git a/libcxx/include/locale b/libcxx/include/locale
index 8e07b8be9ce0d..40f9dd2608fb6 100644
--- a/libcxx/include/locale
+++ b/libcxx/include/locale
@@ -3666,6 +3666,7 @@ _LIBCPP_POP_MACROS
 #    include <cstdarg>
 #    include <iterator>
 #    include <mutex>
+#    include <optional>
 #    include <stdexcept>
 #    include <type_traits>
 #    include <typeinfo>
diff --git a/libcxx/include/streambuf b/libcxx/include/streambuf
index eed7eb4d75ecb..85f3af1b88ae7 100644
--- a/libcxx/include/streambuf
+++ b/libcxx/include/streambuf
@@ -386,6 +386,7 @@ _LIBCPP_POP_MACROS
 
 #  if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20
 #    include <cstdint>
+#    include <optional>
 #  endif
 #endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
 
diff --git a/libcxx/include/string b/libcxx/include/string
index e7e541e31432d..f3f97655d79ea 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -4027,6 +4027,7 @@ _LIBCPP_POP_MACROS
 #    include <cstdlib>
 #    include <iterator>
 #    include <new>
+#    include <optional>
 #    include <type_traits>
 #    include <typeinfo>
 #    include <utility>
diff --git a/libcxx/include/string_view b/libcxx/include/string_view
index 5054b14efd2d5..861187c0640e1 100644
--- a/libcxx/include/string_view
+++ b/libcxx/include/string_view
@@ -952,6 +952,7 @@ _LIBCPP_POP_MACROS
 #    include <concepts>
 #    include <cstdlib>
 #    include <iterator>
+#    include <optional>
 #    include <type_traits>
 #  endif
 #endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
diff --git a/libcxx/include/system_error b/libcxx/include/system_error
index 4dadc0a6ab483..2b668e5f8f1bc 100644
--- a/libcxx/include/system_error
+++ b/libcxx/include/system_error
@@ -168,6 +168,7 @@ template <> struct hash<std::error_condition>;
 #    include <cstdint>
 #    include <cstring>
 #    include <limits>
+#    include <optional>
 #    include <type_traits>
 #  endif
 #endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
diff --git a/libcxx/include/vector b/libcxx/include/vector
index 9fa81dcb7e76e..d2d5fcf4a3199 100644
--- a/libcxx/include/vector
+++ b/libcxx/include/vector
@@ -362,6 +362,7 @@ template<class T, class charT> requires is-vector-bool-reference<T> // Since C++
 #    if _LIBCPP_HAS_LOCALIZATION
 #      include <locale>
 #    endif
+#    include <optional>
 #    include <string>
 #    include <string_view>
 #    include <tuple>
diff --git a/libcxx/test/libcxx/algorithms/robust_against_copying_comparators.pass.cpp b/libcxx/test/libcxx/algorithms/robust_against_copying_comparators.pass.cpp
index df656f296bd05..87bbfae949b62 100644
--- a/libcxx/test/libcxx/algorithms/robust_against_copying_comparators.pass.cpp
+++ b/libcxx/test/libcxx/algorithms/robust_against_copying_comparators.pass.cpp
@@ -12,7 +12,10 @@
 #include <cassert>
 #include <compare>
 #include <cstddef>
+#include <deque>
+#include <ranges>
 #include <type_traits>
+#include <vector>
 
 #include "test_macros.h"
 
@@ -207,10 +210,25 @@ TEST_CONSTEXPR_CXX20 bool all_the_algorithms()
     return true;
 }
 
+bool test_segmented_iterator() {
+    int copies = 0;
+    std::deque<int> dq(10);
+    (void)std::for_each(dq.begin(), dq.end(), UnaryVoid<int>(&copies)); assert(copies == 1); copies = 0;
+
+#if TEST_STD_VER >= 20
+    std::vector<std::vector<int>> vecs(3, std::vector<int>(10));
+    auto v = std::views::join(vecs);
+    std::for_each(v.begin(), v.end(), UnaryVoid<int>(&copies)); assert(copies == 1); copies = 0;
+#endif
+
+    return true;
+}
+
 int main(int, char**)
 {
     all_the_algorithms<void*>();
     all_the_algorithms<int>();
+    test_segmented_iterator();
 #if TEST_STD_VER > 17
     static_assert(all_the_algorithms<void*>());
     static_assert(all_the_algorithms<int>());



More information about the libcxx-commits mailing list