From 04f26fac15c29d0cf3c988767d9e005f3e3a662e Mon Sep 17 00:00:00 2001 From: Peng Liu Date: Tue, 8 Apr 2025 18:47:48 -0400 Subject: [PATCH 1/5] 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 -_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Function -for_each(_InputIterator __first, _InputIterator __last, _Function __f) { +template +_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 - 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 +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 ::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 +_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 a9a4b914f5a5058294ef19142388a2d0af55cf59 Mon Sep 17 00:00:00 2001 From: Peng Liu Date: Thu, 10 Apr 2025 15:52:33 -0400 Subject: [PATCH 2/5] 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 +template 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 _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 ::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 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 # include # include # include +# include # include # include # include 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 # include # include +# include # include # include # 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 # include +# include # include # 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 # include # include +# include # include # include # include 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 # include # include +# include # include # include # include 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 # include # include +# include # include # include # include diff --git a/libcxx/include/locale b/libcxx/include/locale index 26495497d660f..fa2620d883598 100644 --- a/libcxx/include/locale +++ b/libcxx/include/locale @@ -3692,6 +3692,7 @@ _LIBCPP_POP_MACROS # include # include # include +# include # include # include # include 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 +# include # 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 # include # include +# include # include # include # include 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 # include # include +# include # include # 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; # include # include # include +# include # include # 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 requires is-vector-bool-reference // Since C++ # if _LIBCPP_HAS_LOCALIZATION # include # endif +# include # include # include # include 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 #include #include +#include +#include #include +#include #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 dq(10); + (void)std::for_each(dq.begin(), dq.end(), UnaryVoid(&copies)); assert(copies == 1); copies = 0; + +#if TEST_STD_VER >= 20 + std::vector> vecs(3, std::vector(10)); + auto v = std::views::join(vecs); + std::for_each(v.begin(), v.end(), UnaryVoid(&copies)); assert(copies == 1); copies = 0; +#endif + + return true; +} + int main(int, char**) { all_the_algorithms(); all_the_algorithms(); + test_segmented_iterator(); #if TEST_STD_VER > 17 static_assert(all_the_algorithms()); static_assert(all_the_algorithms()); From 3109062499f40a5f1f49687bf3ff4a67ca554e12 Mon Sep 17 00:00:00 2001 From: Peng Liu Date: Thu, 10 Apr 2025 17:02:50 -0400 Subject: [PATCH 3/5] Use lambda instead of function object class template --- libcxx/include/__algorithm/for_each.h | 29 +- libcxx/include/__algorithm/for_each_segment.h | 8 +- ...obust_against_copying_comparators.pass.cpp | 379 +++++++++++------- 3 files changed, 243 insertions(+), 173 deletions(-) diff --git a/libcxx/include/__algorithm/for_each.h b/libcxx/include/__algorithm/for_each.h index a52bdaed4ce49..74352797de301 100644 --- a/libcxx/include/__algorithm/for_each.h +++ b/libcxx/include/__algorithm/for_each.h @@ -21,9 +21,6 @@ # pragma GCC system_header #endif -_LIBCPP_PUSH_MACROS -#include <__undef_macros> - _LIBCPP_BEGIN_NAMESPACE_STD template @@ -32,29 +29,19 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __for_each(_InputIterat __f(*__first); } -// __segment_processor handles the per-segment processing by applying the function object __func_ to each -// element within the segment. -template -struct __segment_processor { - _Func& __func_; - - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __segment_processor(_Func& __f) : __func_(__f) {} - - template - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void - operator()(typename __segmented_iterator_traits<_SegmentedIterator>::__local_iterator __lfirst, - typename __segmented_iterator_traits<_SegmentedIterator>::__local_iterator __llast) { - std::__for_each(__lfirst, __llast, __func_); - } -}; - +#ifndef _LIBCPP_CXX03_LANG template ::value, int> = 0> _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)); + using _Traits = __segmented_iterator_traits<_SegmentedIterator>; + std::__for_each_segment( + __first, __last, [&](typename _Traits::__local_iterator __lfirst, typename _Traits::__local_iterator __llast) { + std::__for_each(__lfirst, __llast, __func); + }); } +#endif template _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Function @@ -65,6 +52,4 @@ for_each(_InputIterator __first, _InputIterator __last, _Function __f) { _LIBCPP_END_NAMESPACE_STD -_LIBCPP_POP_MACROS - #endif // _LIBCPP___ALGORITHM_FOR_EACH_H diff --git a/libcxx/include/__algorithm/for_each_segment.h b/libcxx/include/__algorithm/for_each_segment.h index a96174c70a6bc..93aa8259b2f7f 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.template operator()<_SegmentedIterator>(_Traits::__local(__first), _Traits::__local(__last)); + __func(_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.template operator()<_SegmentedIterator>(_Traits::__local(__first), _Traits::__end(__sfirst)); + __func(_Traits::__local(__first), _Traits::__end(__sfirst)); ++__sfirst; // iterate over the segments which are guaranteed to be completely in the range while (__sfirst != __slast) { - __func.template operator()<_SegmentedIterator>(_Traits::__begin(__sfirst), _Traits::__end(__sfirst)); + __func(_Traits::__begin(__sfirst), _Traits::__end(__sfirst)); ++__sfirst; } // iterate over the last segment - __func.template operator()<_SegmentedIterator>(_Traits::__begin(__sfirst), _Traits::__local(__last)); + __func(_Traits::__begin(__sfirst), _Traits::__local(__last)); } _LIBCPP_END_NAMESPACE_STD 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 87bbfae949b62..256251686bb3e 100644 --- a/libcxx/test/libcxx/algorithms/robust_against_copying_comparators.pass.cpp +++ b/libcxx/test/libcxx/algorithms/robust_against_copying_comparators.pass.cpp @@ -21,218 +21,303 @@ template struct Less { - int *copies_; - TEST_CONSTEXPR explicit Less(int *copies) : copies_(copies) {} - TEST_CONSTEXPR_CXX14 Less(const Less& rhs) : copies_(rhs.copies_) { *copies_ += 1; } - TEST_CONSTEXPR_CXX14 Less& operator=(const Less&) = default; - TEST_CONSTEXPR bool operator()(T, T) const { return false; } + int* copies_; + TEST_CONSTEXPR explicit Less(int* copies) : copies_(copies) {} + TEST_CONSTEXPR_CXX14 Less(const Less& rhs) : copies_(rhs.copies_) { *copies_ += 1; } + TEST_CONSTEXPR_CXX14 Less& operator=(const Less&) = default; + TEST_CONSTEXPR bool operator()(T, T) const { return false; } }; template struct Equal { - int *copies_; - TEST_CONSTEXPR explicit Equal(int *copies) : copies_(copies) {} - TEST_CONSTEXPR_CXX14 Equal(const Equal& rhs) : copies_(rhs.copies_) { *copies_ += 1; } - TEST_CONSTEXPR_CXX14 Equal& operator=(const Equal&) = default; - TEST_CONSTEXPR bool operator()(T, T) const { return true; } + int* copies_; + TEST_CONSTEXPR explicit Equal(int* copies) : copies_(copies) {} + TEST_CONSTEXPR_CXX14 Equal(const Equal& rhs) : copies_(rhs.copies_) { *copies_ += 1; } + TEST_CONSTEXPR_CXX14 Equal& operator=(const Equal&) = default; + TEST_CONSTEXPR bool operator()(T, T) const { return true; } }; template struct UnaryVoid { - int *copies_; - TEST_CONSTEXPR explicit UnaryVoid(int *copies) : copies_(copies) {} - TEST_CONSTEXPR_CXX14 UnaryVoid(const UnaryVoid& rhs) : copies_(rhs.copies_) { *copies_ += 1; } - TEST_CONSTEXPR_CXX14 UnaryVoid& operator=(const UnaryVoid&) = default; - TEST_CONSTEXPR_CXX14 void operator()(T) const {} + int* copies_; + TEST_CONSTEXPR explicit UnaryVoid(int* copies) : copies_(copies) {} + TEST_CONSTEXPR_CXX14 UnaryVoid(const UnaryVoid& rhs) : copies_(rhs.copies_) { *copies_ += 1; } + TEST_CONSTEXPR_CXX14 UnaryVoid& operator=(const UnaryVoid&) = default; + TEST_CONSTEXPR_CXX14 void operator()(T) const {} }; template struct UnaryTrue { - int *copies_; - TEST_CONSTEXPR explicit UnaryTrue(int *copies) : copies_(copies) {} - TEST_CONSTEXPR_CXX14 UnaryTrue(const UnaryTrue& rhs) : copies_(rhs.copies_) { *copies_ += 1; } - TEST_CONSTEXPR_CXX14 UnaryTrue& operator=(const UnaryTrue&) = default; - TEST_CONSTEXPR bool operator()(T) const { return true; } + int* copies_; + TEST_CONSTEXPR explicit UnaryTrue(int* copies) : copies_(copies) {} + TEST_CONSTEXPR_CXX14 UnaryTrue(const UnaryTrue& rhs) : copies_(rhs.copies_) { *copies_ += 1; } + TEST_CONSTEXPR_CXX14 UnaryTrue& operator=(const UnaryTrue&) = default; + TEST_CONSTEXPR bool operator()(T) const { return true; } }; template struct NullaryValue { - int *copies_; - TEST_CONSTEXPR explicit NullaryValue(int *copies) : copies_(copies) {} - TEST_CONSTEXPR_CXX14 NullaryValue(const NullaryValue& rhs) : copies_(rhs.copies_) { *copies_ += 1; } - TEST_CONSTEXPR_CXX14 NullaryValue& operator=(const NullaryValue&) = default; - TEST_CONSTEXPR T operator()() const { return 0; } + int* copies_; + TEST_CONSTEXPR explicit NullaryValue(int* copies) : copies_(copies) {} + TEST_CONSTEXPR_CXX14 NullaryValue(const NullaryValue& rhs) : copies_(rhs.copies_) { *copies_ += 1; } + TEST_CONSTEXPR_CXX14 NullaryValue& operator=(const NullaryValue&) = default; + TEST_CONSTEXPR T operator()() const { return 0; } }; template struct UnaryTransform { - int *copies_; - TEST_CONSTEXPR explicit UnaryTransform(int *copies) : copies_(copies) {} - TEST_CONSTEXPR_CXX14 UnaryTransform(const UnaryTransform& rhs) : copies_(rhs.copies_) { *copies_ += 1; } - TEST_CONSTEXPR_CXX14 UnaryTransform& operator=(const UnaryTransform&) = default; - TEST_CONSTEXPR T operator()(T) const { return 0; } + int* copies_; + TEST_CONSTEXPR explicit UnaryTransform(int* copies) : copies_(copies) {} + TEST_CONSTEXPR_CXX14 UnaryTransform(const UnaryTransform& rhs) : copies_(rhs.copies_) { *copies_ += 1; } + TEST_CONSTEXPR_CXX14 UnaryTransform& operator=(const UnaryTransform&) = default; + TEST_CONSTEXPR T operator()(T) const { return 0; } }; template struct BinaryTransform { - int *copies_; - TEST_CONSTEXPR explicit BinaryTransform(int *copies) : copies_(copies) {} - TEST_CONSTEXPR_CXX14 BinaryTransform(const BinaryTransform& rhs) : copies_(rhs.copies_) { *copies_ += 1; } - TEST_CONSTEXPR_CXX14 BinaryTransform& operator=(const BinaryTransform&) = default; - TEST_CONSTEXPR T operator()(T, T) const { return 0; } + int* copies_; + TEST_CONSTEXPR explicit BinaryTransform(int* copies) : copies_(copies) {} + TEST_CONSTEXPR_CXX14 BinaryTransform(const BinaryTransform& rhs) : copies_(rhs.copies_) { *copies_ += 1; } + TEST_CONSTEXPR_CXX14 BinaryTransform& operator=(const BinaryTransform&) = default; + TEST_CONSTEXPR T operator()(T, T) const { return 0; } }; #if TEST_STD_VER > 17 template struct ThreeWay { - int *copies_; - constexpr explicit ThreeWay(int *copies) : copies_(copies) {} - constexpr ThreeWay(const ThreeWay& rhs) : copies_(rhs.copies_) { *copies_ += 1; } - constexpr ThreeWay& operator=(const ThreeWay&) = default; - constexpr std::strong_ordering operator()(T, T) const { return std::strong_ordering::equal; } + int* copies_; + constexpr explicit ThreeWay(int* copies) : copies_(copies) {} + constexpr ThreeWay(const ThreeWay& rhs) : copies_(rhs.copies_) { *copies_ += 1; } + constexpr ThreeWay& operator=(const ThreeWay&) = default; + constexpr std::strong_ordering operator()(T, T) const { return std::strong_ordering::equal; } }; #endif template -TEST_CONSTEXPR_CXX20 bool all_the_algorithms() -{ - T a[10] = {}; - T b[10] = {}; - T *first = a; - T *mid = a+5; - T *last = a+10; - T *first2 = b; - T *mid2 = b+5; - T *last2 = b+10; - T value = 0; - int count = 1; - - int copies = 0; - (void)std::adjacent_find(first, last, Equal(&copies)); assert(copies == 0); +TEST_CONSTEXPR_CXX20 bool all_the_algorithms() { + T a[10] = {}; + T b[10] = {}; + T* first = a; + T* mid = a + 5; + T* last = a + 10; + T* first2 = b; + T* mid2 = b + 5; + T* last2 = b + 10; + T value = 0; + int count = 1; + + int copies = 0; + (void)std::adjacent_find(first, last, Equal(&copies)); + assert(copies == 0); #if TEST_STD_VER >= 11 - (void)std::all_of(first, last, UnaryTrue(&copies)); assert(copies == 0); - (void)std::any_of(first, last, UnaryTrue(&copies)); assert(copies == 0); + (void)std::all_of(first, last, UnaryTrue(&copies)); + assert(copies == 0); + (void)std::any_of(first, last, UnaryTrue(&copies)); + assert(copies == 0); #endif - (void)std::binary_search(first, last, value, Less(&copies)); assert(copies == 0); + (void)std::binary_search(first, last, value, Less(&copies)); + assert(copies == 0); #if TEST_STD_VER > 17 - (void)std::clamp(value, value, value, Less(&copies)); assert(copies == 0); + (void)std::clamp(value, value, value, Less(&copies)); + assert(copies == 0); #endif - (void)std::count_if(first, last, UnaryTrue(&copies)); assert(copies == 0); - (void)std::copy_if(first, last, first2, UnaryTrue(&copies)); assert(copies == 0); - (void)std::equal(first, last, first2, Equal(&copies)); assert(copies == 0); + (void)std::count_if(first, last, UnaryTrue(&copies)); + assert(copies == 0); + (void)std::copy_if(first, last, first2, UnaryTrue(&copies)); + assert(copies == 0); + (void)std::equal(first, last, first2, Equal(&copies)); + assert(copies == 0); #if TEST_STD_VER > 11 - (void)std::equal(first, last, first2, last2, Equal(&copies)); assert(copies == 0); + (void)std::equal(first, last, first2, last2, Equal(&copies)); + assert(copies == 0); #endif - (void)std::equal_range(first, last, value, Less(&copies)); assert(copies == 0); - (void)std::find_end(first, last, first2, mid2, Equal(&copies)); assert(copies == 0); - (void)std::find_first_of(first, last, first2, last2, Equal(&copies)); assert(copies == 0); - (void)std::find_if(first, last, UnaryTrue(&copies)); assert(copies == 0); - (void)std::find_if_not(first, last, UnaryTrue(&copies)); assert(copies == 0); - (void)std::for_each(first, last, UnaryVoid(&copies)); assert(copies == 1); copies = 0; + (void)std::equal_range(first, last, value, Less(&copies)); + assert(copies == 0); + (void)std::find_end(first, last, first2, mid2, Equal(&copies)); + assert(copies == 0); + (void)std::find_first_of(first, last, first2, last2, Equal(&copies)); + assert(copies == 0); + (void)std::find_if(first, last, UnaryTrue(&copies)); + assert(copies == 0); + (void)std::find_if_not(first, last, UnaryTrue(&copies)); + assert(copies == 0); + (void)std::for_each(first, last, UnaryVoid(&copies)); + assert(copies == 1); + copies = 0; #if TEST_STD_VER > 14 - (void)std::for_each_n(first, count, UnaryVoid(&copies)); assert(copies == 0); + (void)std::for_each_n(first, count, UnaryVoid(&copies)); + assert(copies == 0); #endif - (void)std::generate(first, last, NullaryValue(&copies)); assert(copies == 0); - (void)std::generate_n(first, count, NullaryValue(&copies)); assert(copies == 0); - (void)std::includes(first, last, first2, last2, Less(&copies)); assert(copies == 0); - (void)std::is_heap(first, last, Less(&copies)); assert(copies == 0); - (void)std::is_heap_until(first, last, Less(&copies)); assert(copies == 0); - (void)std::is_partitioned(first, last, UnaryTrue(&copies)); assert(copies == 0); - (void)std::is_permutation(first, last, first2, Equal(&copies)); assert(copies == 0); + (void)std::generate(first, last, NullaryValue(&copies)); + assert(copies == 0); + (void)std::generate_n(first, count, NullaryValue(&copies)); + assert(copies == 0); + (void)std::includes(first, last, first2, last2, Less(&copies)); + assert(copies == 0); + (void)std::is_heap(first, last, Less(&copies)); + assert(copies == 0); + (void)std::is_heap_until(first, last, Less(&copies)); + assert(copies == 0); + (void)std::is_partitioned(first, last, UnaryTrue(&copies)); + assert(copies == 0); + (void)std::is_permutation(first, last, first2, Equal(&copies)); + assert(copies == 0); #if TEST_STD_VER > 11 - (void)std::is_permutation(first, last, first2, last2, Equal(&copies)); assert(copies == 0); + (void)std::is_permutation(first, last, first2, last2, Equal(&copies)); + assert(copies == 0); #endif - (void)std::is_sorted(first, last, Less(&copies)); assert(copies == 0); - (void)std::is_sorted_until(first, last, Less(&copies)); assert(copies == 0); - if (!TEST_IS_CONSTANT_EVALUATED) { (void)std::inplace_merge(first, mid, last, Less(&copies)); assert(copies == 0); } - (void)std::lexicographical_compare(first, last, first2, last2, Less(&copies)); assert(copies == 0); + (void)std::is_sorted(first, last, Less(&copies)); + assert(copies == 0); + (void)std::is_sorted_until(first, last, Less(&copies)); + assert(copies == 0); + if (!TEST_IS_CONSTANT_EVALUATED) { + (void)std::inplace_merge(first, mid, last, Less(&copies)); + assert(copies == 0); + } + (void)std::lexicographical_compare(first, last, first2, last2, Less(&copies)); + assert(copies == 0); #if TEST_STD_VER > 17 - (void)std::lexicographical_compare_three_way(first, last, first2, last2, ThreeWay(&copies)); assert(copies == 0); + (void)std::lexicographical_compare_three_way(first, last, first2, last2, ThreeWay(&copies)); + assert(copies == 0); #endif - (void)std::lower_bound(first, last, value, Less(&copies)); assert(copies == 0); - (void)std::make_heap(first, last, Less(&copies)); assert(copies == 0); - (void)std::max(value, value, Less(&copies)); assert(copies == 0); + (void)std::lower_bound(first, last, value, Less(&copies)); + assert(copies == 0); + (void)std::make_heap(first, last, Less(&copies)); + assert(copies == 0); + (void)std::max(value, value, Less(&copies)); + assert(copies == 0); #if TEST_STD_VER >= 11 - (void)std::max({ value, value }, Less(&copies)); assert(copies == 0); + (void)std::max({value, value}, Less(&copies)); + assert(copies == 0); #endif - (void)std::max_element(first, last, Less(&copies)); assert(copies == 0); - (void)std::merge(first, mid, mid, last, first2, Less(&copies)); assert(copies == 0); - (void)std::min(value, value, Less(&copies)); assert(copies == 0); + (void)std::max_element(first, last, Less(&copies)); + assert(copies == 0); + (void)std::merge(first, mid, mid, last, first2, Less(&copies)); + assert(copies == 0); + (void)std::min(value, value, Less(&copies)); + assert(copies == 0); #if TEST_STD_VER >= 11 - (void)std::min({ value, value }, Less(&copies)); assert(copies == 0); + (void)std::min({value, value}, Less(&copies)); + assert(copies == 0); #endif - (void)std::min_element(first, last, Less(&copies)); assert(copies == 0); - (void)std::minmax(value, value, Less(&copies)); assert(copies == 0); + (void)std::min_element(first, last, Less(&copies)); + assert(copies == 0); + (void)std::minmax(value, value, Less(&copies)); + assert(copies == 0); #if TEST_STD_VER >= 11 - (void)std::minmax({ value, value }, Less(&copies)); assert(copies == 0); + (void)std::minmax({value, value}, Less(&copies)); + assert(copies == 0); #endif - (void)std::minmax_element(first, last, Less(&copies)); assert(copies == 0); - (void)std::mismatch(first, last, first2, Equal(&copies)); assert(copies == 0); + (void)std::minmax_element(first, last, Less(&copies)); + assert(copies == 0); + (void)std::mismatch(first, last, first2, Equal(&copies)); + assert(copies == 0); #if TEST_STD_VER > 11 - (void)std::mismatch(first, last, first2, last2, Equal(&copies)); assert(copies == 0); + (void)std::mismatch(first, last, first2, last2, Equal(&copies)); + assert(copies == 0); #endif - (void)std::next_permutation(first, last, Less(&copies)); assert(copies == 0); + (void)std::next_permutation(first, last, Less(&copies)); + assert(copies == 0); #if TEST_STD_VER >= 11 - (void)std::none_of(first, last, UnaryTrue(&copies)); assert(copies == 0); + (void)std::none_of(first, last, UnaryTrue(&copies)); + assert(copies == 0); #endif - (void)std::nth_element(first, mid, last, Less(&copies)); assert(copies == 0); - (void)std::partial_sort(first, mid, last, Less(&copies)); assert(copies == 0); - (void)std::partial_sort_copy(first, last, first2, mid2, Less(&copies)); assert(copies == 0); - (void)std::partition(first, last, UnaryTrue(&copies)); assert(copies == 0); - (void)std::partition_copy(first, last, first2, last2, UnaryTrue(&copies)); assert(copies == 0); - (void)std::partition_point(first, last, UnaryTrue(&copies)); assert(copies == 0); - (void)std::pop_heap(first, last, Less(&copies)); assert(copies == 0); - (void)std::prev_permutation(first, last, Less(&copies)); assert(copies == 0); - (void)std::push_heap(first, last, Less(&copies)); assert(copies == 0); - (void)std::remove_copy_if(first, last, first2, UnaryTrue(&copies)); assert(copies == 0); - (void)std::remove_if(first, last, UnaryTrue(&copies)); assert(copies == 0); - (void)std::replace_copy_if(first, last, first2, UnaryTrue(&copies), value); assert(copies == 0); - (void)std::replace_if(first, last, UnaryTrue(&copies), value); assert(copies == 0); - (void)std::search(first, last, first2, mid2, Equal(&copies)); assert(copies == 0); - (void)std::search_n(first, last, count, value, Equal(&copies)); assert(copies == 0); - (void)std::set_difference(first, mid, mid, last, first2, Less(&copies)); assert(copies == 0); - (void)std::set_intersection(first, mid, mid, last, first2, Less(&copies)); assert(copies == 0); - (void)std::set_symmetric_difference(first, mid, mid, last, first2, Less(&copies)); assert(copies == 0); - (void)std::set_union(first, mid, mid, last, first2, Less(&copies)); assert(copies == 0); - (void)std::sort(first, first+3, Less(&copies)); assert(copies == 0); - (void)std::sort(first, first+4, Less(&copies)); assert(copies == 0); - (void)std::sort(first, first+5, Less(&copies)); assert(copies == 0); - (void)std::sort(first, last, Less(&copies)); assert(copies == 0); - (void)std::sort_heap(first, last, Less(&copies)); assert(copies == 0); - if (!TEST_IS_CONSTANT_EVALUATED) { (void)std::stable_partition(first, last, UnaryTrue(&copies)); assert(copies == 0); } - if (!TEST_IS_CONSTANT_EVALUATED) { (void)std::stable_sort(first, last, Less(&copies)); assert(copies == 0); } - (void)std::transform(first, last, first2, UnaryTransform(&copies)); assert(copies == 0); - (void)std::transform(first, mid, mid, first2, BinaryTransform(&copies)); assert(copies == 0); - (void)std::unique(first, last, Equal(&copies)); assert(copies == 0); - (void)std::unique_copy(first, last, first2, Equal(&copies)); assert(copies == 0); - (void)std::upper_bound(first, last, value, Less(&copies)); assert(copies == 0); - - return true; + (void)std::nth_element(first, mid, last, Less(&copies)); + assert(copies == 0); + (void)std::partial_sort(first, mid, last, Less(&copies)); + assert(copies == 0); + (void)std::partial_sort_copy(first, last, first2, mid2, Less(&copies)); + assert(copies == 0); + (void)std::partition(first, last, UnaryTrue(&copies)); + assert(copies == 0); + (void)std::partition_copy(first, last, first2, last2, UnaryTrue(&copies)); + assert(copies == 0); + (void)std::partition_point(first, last, UnaryTrue(&copies)); + assert(copies == 0); + (void)std::pop_heap(first, last, Less(&copies)); + assert(copies == 0); + (void)std::prev_permutation(first, last, Less(&copies)); + assert(copies == 0); + (void)std::push_heap(first, last, Less(&copies)); + assert(copies == 0); + (void)std::remove_copy_if(first, last, first2, UnaryTrue(&copies)); + assert(copies == 0); + (void)std::remove_if(first, last, UnaryTrue(&copies)); + assert(copies == 0); + (void)std::replace_copy_if(first, last, first2, UnaryTrue(&copies), value); + assert(copies == 0); + (void)std::replace_if(first, last, UnaryTrue(&copies), value); + assert(copies == 0); + (void)std::search(first, last, first2, mid2, Equal(&copies)); + assert(copies == 0); + (void)std::search_n(first, last, count, value, Equal(&copies)); + assert(copies == 0); + (void)std::set_difference(first, mid, mid, last, first2, Less(&copies)); + assert(copies == 0); + (void)std::set_intersection(first, mid, mid, last, first2, Less(&copies)); + assert(copies == 0); + (void)std::set_symmetric_difference(first, mid, mid, last, first2, Less(&copies)); + assert(copies == 0); + (void)std::set_union(first, mid, mid, last, first2, Less(&copies)); + assert(copies == 0); + (void)std::sort(first, first + 3, Less(&copies)); + assert(copies == 0); + (void)std::sort(first, first + 4, Less(&copies)); + assert(copies == 0); + (void)std::sort(first, first + 5, Less(&copies)); + assert(copies == 0); + (void)std::sort(first, last, Less(&copies)); + assert(copies == 0); + (void)std::sort_heap(first, last, Less(&copies)); + assert(copies == 0); + if (!TEST_IS_CONSTANT_EVALUATED) { + (void)std::stable_partition(first, last, UnaryTrue(&copies)); + assert(copies == 0); + } + if (!TEST_IS_CONSTANT_EVALUATED) { + (void)std::stable_sort(first, last, Less(&copies)); + assert(copies == 0); + } + (void)std::transform(first, last, first2, UnaryTransform(&copies)); + assert(copies == 0); + (void)std::transform(first, mid, mid, first2, BinaryTransform(&copies)); + assert(copies == 0); + (void)std::unique(first, last, Equal(&copies)); + assert(copies == 0); + (void)std::unique_copy(first, last, first2, Equal(&copies)); + assert(copies == 0); + (void)std::upper_bound(first, last, value, Less(&copies)); + assert(copies == 0); + + return true; } bool test_segmented_iterator() { - int copies = 0; - std::deque dq(10); - (void)std::for_each(dq.begin(), dq.end(), UnaryVoid(&copies)); assert(copies == 1); copies = 0; + int copies = 0; + std::deque dq(10); + (void)std::for_each(dq.begin(), dq.end(), UnaryVoid(&copies)); + assert(copies == 1); + copies = 0; #if TEST_STD_VER >= 20 - std::vector> vecs(3, std::vector(10)); - auto v = std::views::join(vecs); - std::for_each(v.begin(), v.end(), UnaryVoid(&copies)); assert(copies == 1); copies = 0; + std::vector> vecs(3, std::vector(10)); + auto v = std::views::join(vecs); + (void)std::for_each(v.begin(), v.end(), UnaryVoid(&copies)); + assert(copies == 1); + copies = 0; #endif - return true; + return true; } -int main(int, char**) -{ - all_the_algorithms(); - all_the_algorithms(); - test_segmented_iterator(); +int main(int, char**) { + all_the_algorithms(); + all_the_algorithms(); + assert(test_segmented_iterator()); #if TEST_STD_VER > 17 - static_assert(all_the_algorithms()); - static_assert(all_the_algorithms()); + static_assert(all_the_algorithms()); + static_assert(all_the_algorithms()); #endif - return 0; + return 0; } From f7f45c8f62a58c006ec442533b601ef6d47aee8e Mon Sep 17 00:00:00 2001 From: Peng Liu Date: Fri, 11 Apr 2025 07:57:59 -0400 Subject: [PATCH 4/5] Remove unused headers --- libcxx/include/__algorithm/for_each.h | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/libcxx/include/__algorithm/for_each.h b/libcxx/include/__algorithm/for_each.h index 74352797de301..b6c2c7c056edd 100644 --- a/libcxx/include/__algorithm/for_each.h +++ b/libcxx/include/__algorithm/for_each.h @@ -14,8 +14,6 @@ #include <__config> #include <__iterator/segmented_iterator.h> #include <__type_traits/enable_if.h> -#include <__utility/in_place.h> -#include <__utility/move.h> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -35,13 +33,12 @@ template ::value, int> = 0> _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __for_each(_SegmentedIterator __first, _SegmentedIterator __last, _Function& __func) { - using _Traits = __segmented_iterator_traits<_SegmentedIterator>; - std::__for_each_segment( - __first, __last, [&](typename _Traits::__local_iterator __lfirst, typename _Traits::__local_iterator __llast) { - std::__for_each(__lfirst, __llast, __func); - }); + using __local_iterator_t = typename __segmented_iterator_traits<_SegmentedIterator>::__local_iterator; + std::__for_each_segment(__first, __last, [&](__local_iterator_t __lfirst, __local_iterator_t __llast) { + std::__for_each(__lfirst, __llast, __func); + }); } -#endif +#endif // !_LIBCPP_CXX03_LANG template _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Function From 32d8bf47a4c70577c80f6e2eff197c07e5b7c5c4 Mon Sep 17 00:00:00 2001 From: Peng Liu Date: Fri, 11 Apr 2025 08:07:03 -0400 Subject: [PATCH 5/5] Add a release note entry --- libcxx/docs/ReleaseNotes/21.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libcxx/docs/ReleaseNotes/21.rst b/libcxx/docs/ReleaseNotes/21.rst index 2091a713ea200..a7382c5222d08 100644 --- a/libcxx/docs/ReleaseNotes/21.rst +++ b/libcxx/docs/ReleaseNotes/21.rst @@ -67,6 +67,9 @@ Improvements and New Features - The ``std::stable_sort`` algorithm uses radix sort for floating-point types now, which can improve the performance up to 10x, depending on type of sorted elements and the initial state of the sorted array. +- The segmented iterator optimization for ``std::for_each`` has been backported to C++11. Previously it was only available + in C++23 and later. + Deprecations and Removals -------------------------