[libcxx-commits] [libcxx] [libc++] Refactor for_each_n (PR #150635)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Jul 25 08:42:09 PDT 2025
https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/150635
>From ac6b3b01f6a9f7456f0b0ecca7a893b185524884 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 25 Jul 2025 17:37:55 +0200
Subject: [PATCH] [libc++] Refactor for_each_n
---
libcxx/include/__algorithm/for_each_n.h | 91 +++++++------------
.../include/__algorithm/for_each_n_segment.h | 24 ++---
.../include/__algorithm/ranges_for_each_n.h | 5 +-
.../include/__iterator/segmented_iterator.h | 5 -
.../include/__iterator/unreachable_sentinel.h | 33 +++++++
5 files changed, 76 insertions(+), 82 deletions(-)
diff --git a/libcxx/include/__algorithm/for_each_n.h b/libcxx/include/__algorithm/for_each_n.h
index 04650e15b6362..24664c4517300 100644
--- a/libcxx/include/__algorithm/for_each_n.h
+++ b/libcxx/include/__algorithm/for_each_n.h
@@ -12,15 +12,16 @@
#include <__algorithm/for_each.h>
#include <__algorithm/for_each_n_segment.h>
+#include <__algorithm/min.h>
#include <__config>
+#include <__cstddef/size_t.h>
#include <__functional/identity.h>
#include <__iterator/iterator_traits.h>
#include <__iterator/segmented_iterator.h>
-#include <__type_traits/disjunction.h>
-#include <__type_traits/enable_if.h>
+#include <__iterator/unreachable_sentinel.h>
#include <__type_traits/invoke.h>
-#include <__type_traits/negation.h>
-#include <__utility/convert_to_integral.h>
+#include <__type_traits/is_same.h>
+#include <__utility/exchange.h>
#include <__utility/move.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -30,73 +31,47 @@
_LIBCPP_PUSH_MACROS
#include <__undef_macros>
+#if _LIBCPP_STD_VER >= 17
+
_LIBCPP_BEGIN_NAMESPACE_STD
-template <class _InputIterator,
- class _Size,
- class _Func,
- class _Proj,
- __enable_if_t<!__has_random_access_iterator_category<_InputIterator>::value &&
- _Or<integral_constant<bool, !__is_segmented_iterator_v<_InputIterator> >,
- _Not<__has_random_access_local_iterator<_InputIterator> > >::value,
- int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator
-__for_each_n(_InputIterator __first, _Size __orig_n, _Func& __f, _Proj& __proj) {
- typedef decltype(std::__convert_to_integral(__orig_n)) _IntegralSize;
- _IntegralSize __n = __orig_n;
- while (__n > 0) {
- std::__invoke(__f, std::__invoke(__proj, *__first));
- ++__first;
- --__n;
+template <class _Iter, class _Sent, class _Size, class _Func, class _Proj>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Iter
+__for_each_n(_Iter __first, _Sent __last, _Size& __n, _Func& __func, _Proj& __proj) {
+ if constexpr (__has_random_access_iterator_category<_Iter>::value) {
+ if constexpr (is_same_v<_Sent, __unreachable_sentinel_t>) {
+ return std::__for_each(__first, __first + std::exchange(__n, 0), __func, __proj);
+ } else {
+ auto __count = std::min<size_t>(__n, __last - __first);
+ __n -= __count;
+ return std::__for_each(__first, __first + __count, __func, __proj);
+ }
+ } else if constexpr (__is_segmented_iterator_v<_Iter>) {
+ return std::__for_each_n_segment(__first, __n, [&](auto __lfirst, auto __llast, _Size __max) {
+ std::__for_each_n(__lfirst, __llast, __max, __func, __proj);
+ return __max;
+ });
+ } else {
+ while (__n > 0 && __first != __last) {
+ std::__invoke(__func, std::__invoke(__proj, *__first));
+ ++__first;
+ --__n;
+ }
+ return std::move(__first);
}
- return std::move(__first);
-}
-
-template <class _RandIter,
- class _Size,
- class _Func,
- class _Proj,
- __enable_if_t<__has_random_access_iterator_category<_RandIter>::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _RandIter
-__for_each_n(_RandIter __first, _Size __orig_n, _Func& __f, _Proj& __proj) {
- typename std::iterator_traits<_RandIter>::difference_type __n = __orig_n;
- auto __last = __first + __n;
- std::__for_each(__first, __last, __f, __proj);
- return __last;
-}
-
-#ifndef _LIBCPP_CXX03_LANG
-template <class _SegmentedIterator,
- class _Size,
- class _Func,
- class _Proj,
- __enable_if_t<!__has_random_access_iterator_category<_SegmentedIterator>::value &&
- __is_segmented_iterator_v<_SegmentedIterator> &&
- __has_random_access_iterator_category<
- typename __segmented_iterator_traits<_SegmentedIterator>::__local_iterator>::value,
- int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _SegmentedIterator
-__for_each_n(_SegmentedIterator __first, _Size __orig_n, _Func& __f, _Proj& __proj) {
- using __local_iterator_t = typename __segmented_iterator_traits<_SegmentedIterator>::__local_iterator;
- return std::__for_each_n_segment(__first, __orig_n, [&](__local_iterator_t __lfirst, __local_iterator_t __llast) {
- std::__for_each(__lfirst, __llast, __f, __proj);
- });
}
-#endif // !_LIBCPP_CXX03_LANG
-
-#if _LIBCPP_STD_VER >= 17
template <class _InputIterator, class _Size, class _Func>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator
for_each_n(_InputIterator __first, _Size __orig_n, _Func __f) {
__identity __proj;
- return std::__for_each_n(__first, __orig_n, __f, __proj);
+ return std::__for_each_n(__first, __unreachable_sentinel, __orig_n, __f, __proj);
}
-#endif // _LIBCPP_STD_VER >= 17
-
_LIBCPP_END_NAMESPACE_STD
+#endif // _LIBCPP_STD_VER >= 17
+
_LIBCPP_POP_MACROS
#endif // _LIBCPP___ALGORITHM_FOR_EACH_N_H
diff --git a/libcxx/include/__algorithm/for_each_n_segment.h b/libcxx/include/__algorithm/for_each_n_segment.h
index a433df5d098ae..2e5c8ba2d336a 100644
--- a/libcxx/include/__algorithm/for_each_n_segment.h
+++ b/libcxx/include/__algorithm/for_each_n_segment.h
@@ -26,36 +26,28 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template <class _SegmentedIterator, class _Size, class _Functor>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _SegmentedIterator
-__for_each_n_segment(_SegmentedIterator __first, _Size __orig_n, _Functor __func) {
+__for_each_n_segment(_SegmentedIterator __first, _Size __n, _Functor __func) {
static_assert(__is_segmented_iterator_v<_SegmentedIterator> &&
__has_random_access_iterator_category<
typename __segmented_iterator_traits<_SegmentedIterator>::__local_iterator>::value,
"__for_each_n_segment only works with segmented iterators with random-access local iterators");
- if (__orig_n <= 0)
+ if (__n <= 0)
return __first;
using _Traits = __segmented_iterator_traits<_SegmentedIterator>;
using __local_iter_t = typename _Traits::__local_iterator;
- using __difference_t = typename std::iterator_traits<__local_iter_t>::difference_type;
- __difference_t __n = __orig_n;
- auto __seg = _Traits::__segment(__first);
+ auto __segment_iter = _Traits::__segment(__first);
auto __local_first = _Traits::__local(__first);
__local_iter_t __local_last;
+ __n = __func(_Traits::__local(__first), _Traits::__end(__segment_iter), __n);
+
while (__n > 0) {
- __local_last = _Traits::__end(__seg);
- auto __seg_size = __local_last - __local_first;
- if (__n <= __seg_size) {
- __local_last = __local_first + __n;
- __func(__local_first, __local_last);
- break;
- }
- __func(__local_first, __local_last);
- __n -= __seg_size;
- __local_first = _Traits::__begin(++__seg);
+ ++__segment_iter;
+ __n = __func(_Traits::__begin(__segment_iter), _Traits::__end(__segment_iter), __n);
}
- return _Traits::__compose(__seg, __local_last);
+ return _Traits::__compose(__segment_iter, __local_last);
}
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__algorithm/ranges_for_each_n.h b/libcxx/include/__algorithm/ranges_for_each_n.h
index 3aab1b79c10a1..7beba8f2d3693 100644
--- a/libcxx/include/__algorithm/ranges_for_each_n.h
+++ b/libcxx/include/__algorithm/ranges_for_each_n.h
@@ -15,9 +15,8 @@
#include <__functional/identity.h>
#include <__iterator/concepts.h>
#include <__iterator/incrementable_traits.h>
-#include <__iterator/iterator_traits.h>
#include <__iterator/projected.h>
-#include <__ranges/concepts.h>
+#include <__iterator/unreachable_sentinel.h>
#include <__utility/move.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -40,7 +39,7 @@ struct __for_each_n {
template <input_iterator _Iter, class _Proj = identity, indirectly_unary_invocable<projected<_Iter, _Proj>> _Func>
_LIBCPP_HIDE_FROM_ABI constexpr for_each_n_result<_Iter, _Func>
operator()(_Iter __first, iter_difference_t<_Iter> __count, _Func __func, _Proj __proj = {}) const {
- auto __last = std::__for_each_n(std::move(__first), __count, __func, __proj);
+ auto __last = std::__for_each_n(std::move(__first), __unreachable_sentinel, __count, __func, __proj);
return {std::move(__last), std::move(__func)};
}
};
diff --git a/libcxx/include/__iterator/segmented_iterator.h b/libcxx/include/__iterator/segmented_iterator.h
index 5df9737137101..dc56a740130b5 100644
--- a/libcxx/include/__iterator/segmented_iterator.h
+++ b/libcxx/include/__iterator/segmented_iterator.h
@@ -75,11 +75,6 @@ inline const bool __has_specialization_v<_Tp, sizeof(_Tp) * 0> = true;
template <class _Iterator>
inline const bool __is_segmented_iterator_v = __has_specialization_v<__segmented_iterator_traits<_Iterator> >;
-template <class _SegmentedIterator>
-struct __has_random_access_local_iterator
- : __has_random_access_iterator_category<
- typename __segmented_iterator_traits< _SegmentedIterator >::__local_iterator > {};
-
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___SEGMENTED_ITERATOR_H
diff --git a/libcxx/include/__iterator/unreachable_sentinel.h b/libcxx/include/__iterator/unreachable_sentinel.h
index 77e663da4b3a6..0351b88d4b234 100644
--- a/libcxx/include/__iterator/unreachable_sentinel.h
+++ b/libcxx/include/__iterator/unreachable_sentinel.h
@@ -12,6 +12,9 @@
#include <__config>
#include <__iterator/concepts.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/remove_cvref.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -19,6 +22,36 @@
_LIBCPP_BEGIN_NAMESPACE_STD
+inline constexpr struct __unreachable_sentinel_t {} __unreachable_sentinel;
+
+template <class _UnreachableSentinel,
+ class _Iter,
+ __enable_if_t<is_same<__remove_cvref_t<_UnreachableSentinel>, __unreachable_sentinel_t>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(_UnreachableSentinel&&, _Iter&&) {
+ return false;
+}
+
+template <class _UnreachableSentinel,
+ class _Iter,
+ __enable_if_t<is_same<__remove_cvref_t<_UnreachableSentinel>, __unreachable_sentinel_t>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(_Iter&&, _UnreachableSentinel&&) {
+ return false;
+}
+
+template <class _UnreachableSentinel,
+ class _Iter,
+ __enable_if_t<is_same<__remove_cvref_t<_UnreachableSentinel>, __unreachable_sentinel_t>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(_UnreachableSentinel&&, _Iter&&) {
+ return true;
+}
+
+template <class _UnreachableSentinel,
+ class _Iter,
+ __enable_if_t<is_same<__remove_cvref_t<_UnreachableSentinel>, __unreachable_sentinel_t>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(_Iter&&, _UnreachableSentinel&&) {
+ return true;
+}
+
#if _LIBCPP_STD_VER >= 20
struct unreachable_sentinel_t {
More information about the libcxx-commits
mailing list