[libcxx-commits] [libcxx] [libc++] Add `ranges::fold_left_first` and `ranges::fold_left_first_with_iter` (PR #121558)
A. Jiang via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Mar 21 02:10:48 PDT 2025
https://github.com/frederick-vs-ja updated https://github.com/llvm/llvm-project/pull/121558
>From 6359ec7edda3d565facebc13a6578d9489cac8e1 Mon Sep 17 00:00:00 2001
From: JCGoran <jcgoran at protonmail.com>
Date: Thu, 2 Jan 2025 15:00:01 +0100
Subject: [PATCH 01/13] Initial impl of `fold_left_first_with_iter`
---
libcxx/include/__algorithm/ranges_fold.h | 51 +++++++++++++++++
libcxx/include/algorithm | 12 ++++
.../algorithm.nodiscard.verify.cpp | 4 ++
.../alg.fold/left_folds.pass.cpp | 57 +++++++++++++++++++
4 files changed, 124 insertions(+)
diff --git a/libcxx/include/__algorithm/ranges_fold.h b/libcxx/include/__algorithm/ranges_fold.h
index d2c3921398504..91873e6bd340a 100644
--- a/libcxx/include/__algorithm/ranges_fold.h
+++ b/libcxx/include/__algorithm/ranges_fold.h
@@ -62,6 +62,9 @@ struct in_value_result {
template <class _Ip, class _Tp>
using fold_left_with_iter_result = in_value_result<_Ip, _Tp>;
+template <class _Ip, class _Tp>
+using fold_left_first_with_iter_result = in_value_result<_Ip, _Tp>;
+
template <class _Fp, class _Tp, class _Ip, class _Rp, class _Up = decay_t<_Rp>>
concept __indirectly_binary_left_foldable_impl =
convertible_to<_Rp, _Up> && //
@@ -118,6 +121,54 @@ struct __fold_left {
};
inline constexpr auto fold_left = __fold_left();
+
+struct __fold_left_first_with_iter {
+ template <input_iterator _Ip, sentinel_for<_Ip> _Sp, __indirectly_binary_left_foldable<iter_value_t<_Ip>, _Ip> _Fp>
+ requires constructible_from<iter_value_t<_Ip>, iter_reference_t<_Ip>>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Ip __first, _Sp __last, _Fp __f) {
+ using _Up = decltype(fold_left(std::move(__first), __last, iter_value_t<_Ip>(*__first), __f));
+
+ // case of empty range
+ if (__first == __last) {
+ return fold_left_first_with_iter_result<_Ip, optional<_Up>>{std::move(__first), optional<_Up>()};
+ }
+
+ optional<_Up> __result(std::in_place, *__first);
+ for (++__first; __first != __last; ++__first) {
+ *__result = std::invoke(__f, std::move(*__result), *__first);
+ }
+
+ return fold_left_first_with_iter_result<_Ip, optional<_Up>>{std::move(__first), optional<_Up>(std::move(__result))};
+ }
+
+ template <input_range _Rp, __indirectly_binary_left_foldable<range_value_t<_Rp>, iterator_t<_Rp>> _Fp>
+ requires constructible_from<range_value_t<_Rp>, range_reference_t<_Rp>>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Rp&& __r, _Fp __f) {
+ auto __result = operator()(ranges::begin(__r), ranges::end(__r), std::ref(__f));
+
+ using _Up = decltype(fold_left(ranges::begin(__r), ranges::end(__r), range_value_t<_Rp>(*ranges::begin(__r)), __f));
+ return fold_left_first_with_iter_result<borrowed_iterator_t<_Rp>, optional<_Up>>{std::move(__result.in), optional(std::move(__result.value))};
+ }
+};
+
+inline constexpr auto fold_left_first_with_iter = __fold_left_first_with_iter();
+
+struct __fold_left_first {
+ template <input_iterator _Ip, sentinel_for<_Ip> _Sp, __indirectly_binary_left_foldable<iter_value_t<_Ip>, _Ip> _Fp>
+ requires constructible_from<iter_value_t<_Ip>, iter_reference_t<_Ip>>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Ip __first, _Sp __last, _Fp __f) {
+ return fold_left_first_with_iter(std::move(__first), std::move(__last), std::ref(__f)).value;
+ }
+
+ template <input_range _Rp, __indirectly_binary_left_foldable<range_value_t<_Rp>, iterator_t<_Rp>> _Fp>
+ requires constructible_from<range_value_t<_Rp>, range_reference_t<_Rp>>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Rp&& __r, _Fp __f) {
+ return fold_left_first_with_iter(ranges::begin(__r), ranges::end(__r), std::ref(__f)).value;
+ }
+};
+
+inline constexpr auto fold_left_first = __fold_left_first();
+
} // namespace ranges
#endif // _LIBCPP_STD_VER >= 23
diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm
index e593ae26ed6e2..16728cbf0bcff 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -948,6 +948,18 @@ namespace ranges {
template<input_range R, class T, indirectly-binary-left-foldable<T, iterator_t<R>> F>
constexpr see below fold_left_with_iter(R&& r, T init, F f); // since C++23
+ template<class I, class T>
+ using fold_left_first_with_iter_result = in_value_result<I, optional<T>>; // since C++23
+
+ template<input_iterator I, sentinel_for<I> S,
+ indirectly-binary-left-foldable<T, I> F>
+ requires constructible_from<iter_value_t<I>, iter_reference_t<I>>
+ constexpr see below fold_left_first_with_iter(I first, S last, F f); // since C++23
+
+ template<input_range R, indirectly-binary-left-foldable<T, iterator_t<R>> F>
+ requires constructible_from<range_value_t<R>, range_reference_t<R>>
+ constexpr see below fold_left_first_with_iter(R&& r, F f); // since C++23
+
template<forward_iterator I1, sentinel_for<I1> S1, forward_iterator I2, sentinel_for<I2> S2,
class Pred = ranges::equal_to, class Proj1 = identity, class Proj2 = identity>
requires indirectly_comparable<I1, I2, Pred, Proj1, Proj2>
diff --git a/libcxx/test/libcxx/diagnostics/algorithm.nodiscard.verify.cpp b/libcxx/test/libcxx/diagnostics/algorithm.nodiscard.verify.cpp
index 14febc12a8a2d..f4f14e513308b 100644
--- a/libcxx/test/libcxx/diagnostics/algorithm.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/diagnostics/algorithm.nodiscard.verify.cpp
@@ -392,5 +392,9 @@ void test() {
// expected-warning at -1{{ignoring return value of function declared with 'nodiscard' attribute}}
std::ranges::fold_left_with_iter(iter, iter, 0, std::plus());
// expected-warning at -1{{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::fold_left_first_with_iter(range, std::plus());
+ // expected-warning at -1{{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::fold_left_first_with_iter(iter, iter, std::plus());
+ // expected-warning at -1{{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
}
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/left_folds.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/left_folds.pass.cpp
index 4987ca9cac4ae..22c5e1b8620a0 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/left_folds.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/left_folds.pass.cpp
@@ -39,6 +39,7 @@
#include <string_view>
#include <string>
#include <vector>
+#include <optional>
#include "test_macros.h"
#include "test_range.h"
@@ -50,7 +51,9 @@
#endif
using std::ranges::fold_left;
+using std::ranges::fold_left_first;
using std::ranges::fold_left_with_iter;
+using std::ranges::fold_left_first_with_iter;
template <class Result, class Range, class T>
concept is_in_value_result =
@@ -105,6 +108,47 @@ constexpr void check_iterator(R& r, T const& init, F f, Expected const& expected
}
}
+template <std::ranges::input_range R, class F, std::equality_comparable Expected>
+ requires std::copyable<R>
+constexpr void check_iterator(R& r, F f, std::optional<Expected> const& expected) {
+ {
+ is_in_value_result<R, std::optional<Expected>> decltype(auto) result = fold_left_first_with_iter(r.begin(), r.end(), f);
+ assert(result.in == r.end());
+ assert(result.value == expected);
+ }
+
+ {
+ auto telemetry = invocable_telemetry();
+ auto f2 = invocable_with_telemetry(f, telemetry);
+ is_in_value_result<R, std::optional<Expected>> decltype(auto) result = fold_left_first_with_iter(r.begin(), r.end(), f2);
+ assert(result.in == r.end());
+ assert(result.value == expected);
+ if (result.value.has_value()) {
+ assert(telemetry.invocations == std::ranges::distance(r) - 1);
+ assert(telemetry.moves == 0);
+ assert(telemetry.copies == 1);
+ }
+ }
+
+ {
+ std::same_as<std::optional<Expected>> decltype(auto) result = fold_left_first(r.begin(), r.end(), f);
+ assert(result == expected);
+ }
+
+ {
+ auto telemetry = invocable_telemetry();
+ auto f2 = invocable_with_telemetry(f, telemetry);
+ std::same_as<std::optional<Expected>> decltype(auto) result = fold_left_first(r.begin(), r.end(), f2);
+ assert(result == expected);
+ if (result.has_value()) {
+ assert(telemetry.invocations == std::ranges::distance(r) - 1);
+ assert(telemetry.moves == 0);
+ assert(telemetry.copies == 1);
+ }
+ }
+}
+
+
template <std::ranges::input_range R, class T, class F, std::equality_comparable Expected>
requires std::copyable<R>
constexpr void check_lvalue_range(R& r, T const& init, F f, Expected const& expected) {
@@ -186,19 +230,29 @@ constexpr void check(R r, T const& init, F f, Expected const& expected) {
check_rvalue_range(r, init, f, expected);
}
+template <std::ranges::input_range R, class F, std::equality_comparable Expected>
+ requires std::copyable<R>
+constexpr void check(R r, F f, std::optional<Expected> const& expected) {
+ check_iterator(r, f, expected);
+}
+
constexpr void empty_range_test_case() {
auto const data = std::vector<int>{};
check(data, 100, std::plus(), 100);
check(data, -100, std::multiplies(), -100);
+ check(data, std::plus(), std::optional<int>());
check(data | std::views::take_while([](auto) { return false; }), 1.23, std::plus(), 1.23);
check(data, Integer(52), &Integer::plus, Integer(52));
+ check(data | std::views::take_while([](auto) { return false; }), std::plus(), std::optional<int>());
}
constexpr void common_range_test_case() {
auto const data = std::vector<int>{1, 2, 3, 4};
check(data, 0, std::plus(), triangular_sum(data));
check(data, 1, std::multiplies(), factorial(data.back()));
+ check(data, std::plus(), std::optional(triangular_sum(data)));
+ check(data, std::multiplies(), std::optional(factorial(data.back())));
auto multiply_with_prev = [n = 1](auto const x, auto const y) mutable {
auto const result = x * y * n;
@@ -206,6 +260,7 @@ constexpr void common_range_test_case() {
return static_cast<std::size_t>(result);
};
check(data, 1, multiply_with_prev, factorial(data.size()) * factorial(data.size() - 1));
+ check(data, multiply_with_prev, std::optional(factorial(data.size()) * factorial(data.size() - 1)));
auto fib = [n = 1](auto x, auto) mutable {
auto old_x = x;
@@ -237,6 +292,7 @@ constexpr void non_common_range_test_case() {
auto data = std::vector<std::string>{"five", "three", "two", "six", "one", "four"};
auto range = data | std::views::transform(parse);
check(range, 0, std::plus(), triangular_sum(range));
+ check(range, std::plus(), std::optional(triangular_sum(range)));
}
{
@@ -248,6 +304,7 @@ constexpr void non_common_range_test_case() {
auto range =
std::views::lazy_split(data, ' ') | std::views::transform(to_string_view) | std::views::transform(parse);
check(range, 0, std::plus(), triangular_sum(range));
+ check(range, std::plus(), std::optional(triangular_sum(range)));
}
}
>From 8eb9f22996e5b12c8567ac7840bb0ada90b5f16a Mon Sep 17 00:00:00 2001
From: JCGoran <jcgoran at protonmail.com>
Date: Thu, 2 Jan 2025 22:29:19 +0100
Subject: [PATCH 02/13] Update
---
libcxx/modules/std/algorithm.inc | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/libcxx/modules/std/algorithm.inc b/libcxx/modules/std/algorithm.inc
index 3c2139cd64ee4..fbf260241e89a 100644
--- a/libcxx/modules/std/algorithm.inc
+++ b/libcxx/modules/std/algorithm.inc
@@ -164,13 +164,12 @@ export namespace std {
using std::ranges::fold_left;
using std::ranges::fold_left_with_iter;
using std::ranges::fold_left_with_iter_result;
-# if 0
using std::ranges::fold_left_first;
+ using std::ranges::fold_left_first_with_iter;
+ using std::ranges::fold_left_first_with_iter_result;
+# if 0
using std::ranges::fold_right;
using std::ranges::fold_right_last;
- using std::ranges::fold_left_with_iter;
- using std::ranges::fold_left_first_with_iter;
- using std::ranges::fold_left_first_with_iter;
# endif
#endif // _LIBCPP_STD_VER >= 23
} // namespace ranges
>From 8067ea1728a6861648c8a206324e7909e8695d77 Mon Sep 17 00:00:00 2001
From: JCGoran <jcgoran at protonmail.com>
Date: Thu, 2 Jan 2025 22:40:16 +0100
Subject: [PATCH 03/13] Update header
---
libcxx/include/algorithm | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm
index 16728cbf0bcff..5592387e45495 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -938,6 +938,15 @@ namespace ranges {
template<input_range R, class T, indirectly-binary-left-foldable<T, iterator_t<R>> F>
constexpr auto fold_left(R&& r, T init, F f); // since C++23
+ template<input_iterator I, sentinel_for<I> S,
+ indirectly-binary-left-foldable<T, I> F>
+ requires constructible_from<iter_value_t<I>, iter_reference_t<I>>
+ constexpr see below fold_left_first(I first, S last, F f); // since C++23
+
+ template<input_range R, indirectly-binary-left-foldable<T, iterator_t<R>> F>
+ requires constructible_from<range_value_t<R>, range_reference_t<R>>
+ constexpr see below fold_left_first(R&& r, F f); // since C++23
+
template<class I, class T>
using fold_left_with_iter_result = in_value_result<I, T>; // since C++23
@@ -949,7 +958,7 @@ namespace ranges {
constexpr see below fold_left_with_iter(R&& r, T init, F f); // since C++23
template<class I, class T>
- using fold_left_first_with_iter_result = in_value_result<I, optional<T>>; // since C++23
+ using fold_left_first_with_iter_result = in_value_result<I, T>; // since C++23
template<input_iterator I, sentinel_for<I> S,
indirectly-binary-left-foldable<T, I> F>
>From a5515dba2f6d5fef5455d136d1dbccf20c340a4b Mon Sep 17 00:00:00 2001
From: JCGoran <jcgoran at protonmail.com>
Date: Fri, 3 Jan 2025 11:38:48 +0100
Subject: [PATCH 04/13] Formatting
---
libcxx/modules/std/algorithm.inc | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libcxx/modules/std/algorithm.inc b/libcxx/modules/std/algorithm.inc
index fbf260241e89a..20a3aea4fa8da 100644
--- a/libcxx/modules/std/algorithm.inc
+++ b/libcxx/modules/std/algorithm.inc
@@ -162,11 +162,11 @@ export namespace std {
// [alg.fold], fold
using std::ranges::fold_left;
- using std::ranges::fold_left_with_iter;
- using std::ranges::fold_left_with_iter_result;
using std::ranges::fold_left_first;
using std::ranges::fold_left_first_with_iter;
using std::ranges::fold_left_first_with_iter_result;
+ using std::ranges::fold_left_with_iter;
+ using std::ranges::fold_left_with_iter_result;
# if 0
using std::ranges::fold_right;
using std::ranges::fold_right_last;
>From 1cdc9bf38c8b825328d0c588c6000933154ba063 Mon Sep 17 00:00:00 2001
From: JCGoran <jcgoran at protonmail.com>
Date: Fri, 3 Jan 2025 15:35:13 +0100
Subject: [PATCH 05/13] More formatting
---
libcxx/include/__algorithm/ranges_fold.h | 11 +++----
.../alg.fold/left_folds.pass.cpp | 29 ++++++++++---------
2 files changed, 21 insertions(+), 19 deletions(-)
diff --git a/libcxx/include/__algorithm/ranges_fold.h b/libcxx/include/__algorithm/ranges_fold.h
index 91873e6bd340a..b31ff7ca27b35 100644
--- a/libcxx/include/__algorithm/ranges_fold.h
+++ b/libcxx/include/__algorithm/ranges_fold.h
@@ -124,7 +124,7 @@ inline constexpr auto fold_left = __fold_left();
struct __fold_left_first_with_iter {
template <input_iterator _Ip, sentinel_for<_Ip> _Sp, __indirectly_binary_left_foldable<iter_value_t<_Ip>, _Ip> _Fp>
- requires constructible_from<iter_value_t<_Ip>, iter_reference_t<_Ip>>
+ requires constructible_from<iter_value_t<_Ip>, iter_reference_t<_Ip>>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Ip __first, _Sp __last, _Fp __f) {
using _Up = decltype(fold_left(std::move(__first), __last, iter_value_t<_Ip>(*__first), __f));
@@ -142,12 +142,13 @@ struct __fold_left_first_with_iter {
}
template <input_range _Rp, __indirectly_binary_left_foldable<range_value_t<_Rp>, iterator_t<_Rp>> _Fp>
- requires constructible_from<range_value_t<_Rp>, range_reference_t<_Rp>>
+ requires constructible_from<range_value_t<_Rp>, range_reference_t<_Rp>>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Rp&& __r, _Fp __f) {
auto __result = operator()(ranges::begin(__r), ranges::end(__r), std::ref(__f));
using _Up = decltype(fold_left(ranges::begin(__r), ranges::end(__r), range_value_t<_Rp>(*ranges::begin(__r)), __f));
- return fold_left_first_with_iter_result<borrowed_iterator_t<_Rp>, optional<_Up>>{std::move(__result.in), optional(std::move(__result.value))};
+ return fold_left_first_with_iter_result<borrowed_iterator_t<_Rp>, optional<_Up>>{
+ std::move(__result.in), optional(std::move(__result.value))};
}
};
@@ -155,13 +156,13 @@ inline constexpr auto fold_left_first_with_iter = __fold_left_first_with_iter();
struct __fold_left_first {
template <input_iterator _Ip, sentinel_for<_Ip> _Sp, __indirectly_binary_left_foldable<iter_value_t<_Ip>, _Ip> _Fp>
- requires constructible_from<iter_value_t<_Ip>, iter_reference_t<_Ip>>
+ requires constructible_from<iter_value_t<_Ip>, iter_reference_t<_Ip>>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Ip __first, _Sp __last, _Fp __f) {
return fold_left_first_with_iter(std::move(__first), std::move(__last), std::ref(__f)).value;
}
template <input_range _Rp, __indirectly_binary_left_foldable<range_value_t<_Rp>, iterator_t<_Rp>> _Fp>
- requires constructible_from<range_value_t<_Rp>, range_reference_t<_Rp>>
+ requires constructible_from<range_value_t<_Rp>, range_reference_t<_Rp>>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Rp&& __r, _Fp __f) {
return fold_left_first_with_iter(ranges::begin(__r), ranges::end(__r), std::ref(__f)).value;
}
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/left_folds.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/left_folds.pass.cpp
index 22c5e1b8620a0..c5b75a2e4ca3c 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/left_folds.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/left_folds.pass.cpp
@@ -52,8 +52,8 @@
using std::ranges::fold_left;
using std::ranges::fold_left_first;
-using std::ranges::fold_left_with_iter;
using std::ranges::fold_left_first_with_iter;
+using std::ranges::fold_left_with_iter;
template <class Result, class Range, class T>
concept is_in_value_result =
@@ -112,21 +112,23 @@ template <std::ranges::input_range R, class F, std::equality_comparable Expected
requires std::copyable<R>
constexpr void check_iterator(R& r, F f, std::optional<Expected> const& expected) {
{
- is_in_value_result<R, std::optional<Expected>> decltype(auto) result = fold_left_first_with_iter(r.begin(), r.end(), f);
+ is_in_value_result<R, std::optional<Expected>> decltype(auto) result =
+ fold_left_first_with_iter(r.begin(), r.end(), f);
assert(result.in == r.end());
assert(result.value == expected);
}
{
- auto telemetry = invocable_telemetry();
- auto f2 = invocable_with_telemetry(f, telemetry);
- is_in_value_result<R, std::optional<Expected>> decltype(auto) result = fold_left_first_with_iter(r.begin(), r.end(), f2);
+ auto telemetry = invocable_telemetry();
+ auto f2 = invocable_with_telemetry(f, telemetry);
+ is_in_value_result<R, std::optional<Expected>> decltype(auto) result =
+ fold_left_first_with_iter(r.begin(), r.end(), f2);
assert(result.in == r.end());
assert(result.value == expected);
if (result.value.has_value()) {
- assert(telemetry.invocations == std::ranges::distance(r) - 1);
- assert(telemetry.moves == 0);
- assert(telemetry.copies == 1);
+ assert(telemetry.invocations == std::ranges::distance(r) - 1);
+ assert(telemetry.moves == 0);
+ assert(telemetry.copies == 1);
}
}
@@ -136,19 +138,18 @@ constexpr void check_iterator(R& r, F f, std::optional<Expected> const& expected
}
{
- auto telemetry = invocable_telemetry();
- auto f2 = invocable_with_telemetry(f, telemetry);
+ auto telemetry = invocable_telemetry();
+ auto f2 = invocable_with_telemetry(f, telemetry);
std::same_as<std::optional<Expected>> decltype(auto) result = fold_left_first(r.begin(), r.end(), f2);
assert(result == expected);
if (result.has_value()) {
- assert(telemetry.invocations == std::ranges::distance(r) - 1);
- assert(telemetry.moves == 0);
- assert(telemetry.copies == 1);
+ assert(telemetry.invocations == std::ranges::distance(r) - 1);
+ assert(telemetry.moves == 0);
+ assert(telemetry.copies == 1);
}
}
}
-
template <std::ranges::input_range R, class T, class F, std::equality_comparable Expected>
requires std::copyable<R>
constexpr void check_lvalue_range(R& r, T const& init, F f, Expected const& expected) {
>From a5126a408b0dcd93810b5c94218a8c896ffff9be Mon Sep 17 00:00:00 2001
From: JCGoran <jcgoran at protonmail.com>
Date: Fri, 3 Jan 2025 16:52:50 +0100
Subject: [PATCH 06/13] Apply suggested changes
---
libcxx/include/__algorithm/ranges_fold.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/libcxx/include/__algorithm/ranges_fold.h b/libcxx/include/__algorithm/ranges_fold.h
index b31ff7ca27b35..3e9ddf13e4454 100644
--- a/libcxx/include/__algorithm/ranges_fold.h
+++ b/libcxx/include/__algorithm/ranges_fold.h
@@ -133,9 +133,9 @@ struct __fold_left_first_with_iter {
return fold_left_first_with_iter_result<_Ip, optional<_Up>>{std::move(__first), optional<_Up>()};
}
- optional<_Up> __result(std::in_place, *__first);
+ _Up __result(*__first);
for (++__first; __first != __last; ++__first) {
- *__result = std::invoke(__f, std::move(*__result), *__first);
+ __result = std::invoke(__f, std::move(__result), *__first);
}
return fold_left_first_with_iter_result<_Ip, optional<_Up>>{std::move(__first), optional<_Up>(std::move(__result))};
@@ -148,7 +148,7 @@ struct __fold_left_first_with_iter {
using _Up = decltype(fold_left(ranges::begin(__r), ranges::end(__r), range_value_t<_Rp>(*ranges::begin(__r)), __f));
return fold_left_first_with_iter_result<borrowed_iterator_t<_Rp>, optional<_Up>>{
- std::move(__result.in), optional(std::move(__result.value))};
+ std::move(__result.in), std::move(__result.value)};
}
};
>From 3816c4c4a42275f31d2d36a821f28497467a4fef Mon Sep 17 00:00:00 2001
From: JCGoran <jcgoran at protonmail.com>
Date: Sun, 5 Jan 2025 19:55:53 +0100
Subject: [PATCH 07/13] Add missing header
---
libcxx/include/__algorithm/ranges_fold.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/libcxx/include/__algorithm/ranges_fold.h b/libcxx/include/__algorithm/ranges_fold.h
index 3e9ddf13e4454..b94fb59f6090a 100644
--- a/libcxx/include/__algorithm/ranges_fold.h
+++ b/libcxx/include/__algorithm/ranges_fold.h
@@ -28,6 +28,7 @@
#include <__type_traits/invoke.h>
#include <__utility/forward.h>
#include <__utility/move.h>
+#include <optional>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
>From 7bac67a97e42a4878b69a1eca214425b6cc64895 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Wed, 19 Mar 2025 17:07:55 +0800
Subject: [PATCH 08/13] Add release notes
---
libcxx/docs/ReleaseNotes/21.rst | 1 +
libcxx/docs/Status/Cxx23Papers.csv | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/libcxx/docs/ReleaseNotes/21.rst b/libcxx/docs/ReleaseNotes/21.rst
index f41cf9b51c292..a6df99b522c50 100644
--- a/libcxx/docs/ReleaseNotes/21.rst
+++ b/libcxx/docs/ReleaseNotes/21.rst
@@ -42,6 +42,7 @@ Implemented Papers
- P0767R1: Deprecate POD (`Github <https://github.com/llvm/llvm-project/issues/104013>`__)
- P1361R2: Integration of chrono with text formatting (`Github <https://github.com/llvm/llvm-project/issues/100014>`__)
- P2255R2: A type trait to detect reference binding to temporary (implemented the type traits only) (`Github <https://github.com/llvm/llvm-project/issues/105180>`__)
+- P2322R6: ``ranges::fold`` (implemented ``ranges::fold_left_first`` and ``ranges::fold_left_first_with_iter``) (`Github <https://github.com/llvm/llvm-project/issues/105208>`__)
Improvements and New Features
-----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 2589954729a30..27a9dd03d9210 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -65,7 +65,7 @@
"`P2286R8 <https://wg21.link/P2286R8>`__","Formatting Ranges","2022-07 (Virtual)","|Complete|","16",""
"`P2291R3 <https://wg21.link/P2291R3>`__","Add Constexpr Modifiers to Functions ``to_chars`` and ``from_chars`` for Integral Types in ``<charconv>`` Header","2022-07 (Virtual)","|Complete|","16",""
"`P2302R4 <https://wg21.link/P2302R4>`__","``std::ranges::contains``","2022-07 (Virtual)","|Complete|","19",""
-"`P2322R6 <https://wg21.link/P2322R6>`__","``ranges::fold``","2022-07 (Virtual)","","",""
+"`P2322R6 <https://wg21.link/P2322R6>`__","``ranges::fold``","2022-07 (Virtual)","|Partial|","","Only ``in_value_result``, ``fold_left_with_iter``, ``fold_left``, ``fold_left_first_with_iter``, and ``fold_left_first`` are implemented."
"`P2374R4 <https://wg21.link/P2374R4>`__","``views::cartesian_product``","2022-07 (Virtual)","","",""
"`P2404R3 <https://wg21.link/P2404R3>`__","Move-only types for ``equality_comparable_with``, ``totally_ordered_with``, and ``three_way_comparable_with``","2022-07 (Virtual)","","",""
"`P2408R5 <https://wg21.link/P2408R5>`__","Ranges iterators as inputs to non-Ranges algorithms","2022-07 (Virtual)","","",""
>From abf6df7118cb5a2b783f759391749d2d0e752eb5 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Fri, 21 Mar 2025 16:59:10 +0800
Subject: [PATCH 09/13] Adjust release notes per review comments
---
libcxx/docs/ReleaseNotes/21.rst | 1 -
libcxx/docs/Status/Cxx23Papers.csv | 2 +-
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/libcxx/docs/ReleaseNotes/21.rst b/libcxx/docs/ReleaseNotes/21.rst
index a6df99b522c50..f41cf9b51c292 100644
--- a/libcxx/docs/ReleaseNotes/21.rst
+++ b/libcxx/docs/ReleaseNotes/21.rst
@@ -42,7 +42,6 @@ Implemented Papers
- P0767R1: Deprecate POD (`Github <https://github.com/llvm/llvm-project/issues/104013>`__)
- P1361R2: Integration of chrono with text formatting (`Github <https://github.com/llvm/llvm-project/issues/100014>`__)
- P2255R2: A type trait to detect reference binding to temporary (implemented the type traits only) (`Github <https://github.com/llvm/llvm-project/issues/105180>`__)
-- P2322R6: ``ranges::fold`` (implemented ``ranges::fold_left_first`` and ``ranges::fold_left_first_with_iter``) (`Github <https://github.com/llvm/llvm-project/issues/105208>`__)
Improvements and New Features
-----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 27a9dd03d9210..46f4d91825dfa 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -65,7 +65,7 @@
"`P2286R8 <https://wg21.link/P2286R8>`__","Formatting Ranges","2022-07 (Virtual)","|Complete|","16",""
"`P2291R3 <https://wg21.link/P2291R3>`__","Add Constexpr Modifiers to Functions ``to_chars`` and ``from_chars`` for Integral Types in ``<charconv>`` Header","2022-07 (Virtual)","|Complete|","16",""
"`P2302R4 <https://wg21.link/P2302R4>`__","``std::ranges::contains``","2022-07 (Virtual)","|Complete|","19",""
-"`P2322R6 <https://wg21.link/P2322R6>`__","``ranges::fold``","2022-07 (Virtual)","|Partial|","","Only ``in_value_result``, ``fold_left_with_iter``, ``fold_left``, ``fold_left_first_with_iter``, and ``fold_left_first`` are implemented."
+"`P2322R6 <https://wg21.link/P2322R6>`__","``ranges::fold``","2022-07 (Virtual)","|Partial|","","Only ``fold_left_with_iter``, ``fold_left``, ``fold_left_first_with_iter``, and ``fold_left_first`` are implemented."
"`P2374R4 <https://wg21.link/P2374R4>`__","``views::cartesian_product``","2022-07 (Virtual)","","",""
"`P2404R3 <https://wg21.link/P2404R3>`__","Move-only types for ``equality_comparable_with``, ``totally_ordered_with``, and ``three_way_comparable_with``","2022-07 (Virtual)","","",""
"`P2408R5 <https://wg21.link/P2408R5>`__","Ranges iterators as inputs to non-Ranges algorithms","2022-07 (Virtual)","","",""
>From 2da3541e76f45ceb9bdd1f42d198c0067123cdaa Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Fri, 21 Mar 2025 17:00:57 +0800
Subject: [PATCH 10/13] Modify general test coverage for ranges algorithms
---
.../libcxx/diagnostics/algorithm.nodiscard.verify.cpp | 8 ++++++--
.../customization.point.object/niebloid.compile.pass.cpp | 2 ++
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/libcxx/test/libcxx/diagnostics/algorithm.nodiscard.verify.cpp b/libcxx/test/libcxx/diagnostics/algorithm.nodiscard.verify.cpp
index f4f14e513308b..ca111494bdeb5 100644
--- a/libcxx/test/libcxx/diagnostics/algorithm.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/diagnostics/algorithm.nodiscard.verify.cpp
@@ -388,13 +388,17 @@ void test() {
// expected-warning at -1{{ignoring return value of function declared with 'nodiscard' attribute}}
std::ranges::fold_left(iter, iter, 0, std::plus());
// expected-warning at -1{{ignoring return value of function declared with 'nodiscard' attribute}}
- std::ranges::fold_left_with_iter(range, 0, std::plus());
+ std::ranges::fold_left_first(range, std::plus());
// expected-warning at -1{{ignoring return value of function declared with 'nodiscard' attribute}}
- std::ranges::fold_left_with_iter(iter, iter, 0, std::plus());
+ std::ranges::fold_left_first(iter, iter, std::plus());
// expected-warning at -1{{ignoring return value of function declared with 'nodiscard' attribute}}
std::ranges::fold_left_first_with_iter(range, std::plus());
// expected-warning at -1{{ignoring return value of function declared with 'nodiscard' attribute}}
std::ranges::fold_left_first_with_iter(iter, iter, std::plus());
// expected-warning at -1{{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::fold_left_with_iter(range, 0, std::plus());
+ // expected-warning at -1{{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::fold_left_with_iter(iter, iter, 0, std::plus());
+ // expected-warning at -1{{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
}
diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp
index 5a2ee189641c7..faa1123f9caac 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp
@@ -94,6 +94,8 @@ static_assert(test(std::ranges::find_last, a, 42));
static_assert(test(std::ranges::find_last_if, a, odd));
static_assert(test(std::ranges::find_last_if_not, a, odd));
static_assert(test(std::ranges::fold_left, a, 0, std::plus()));
+static_assert(test(std::ranges::fold_left_first(a, std::plus())));
+static_assert(test(std::ranges::fold_left_first_with_iter(a, std::plus())));
static_assert(test(std::ranges::fold_left_with_iter, a, 0, std::plus()));
#endif
static_assert(test(std::ranges::for_each, a, odd));
>From 8396d9e28e74a32859cc1324e238ca5a1ac5b555 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Fri, 21 Mar 2025 17:01:30 +0800
Subject: [PATCH 11/13] Code style change in `<__algorithm/ranges_fold.h>`
---
libcxx/include/__algorithm/ranges_fold.h | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/libcxx/include/__algorithm/ranges_fold.h b/libcxx/include/__algorithm/ranges_fold.h
index b94fb59f6090a..6fb6e2319aaf8 100644
--- a/libcxx/include/__algorithm/ranges_fold.h
+++ b/libcxx/include/__algorithm/ranges_fold.h
@@ -86,14 +86,12 @@ struct __fold_left_with_iter {
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Ip __first, _Sp __last, _Tp __init, _Fp __f) {
using _Up = decay_t<invoke_result_t<_Fp&, _Tp, iter_reference_t<_Ip>>>;
- if (__first == __last) {
+ if (__first == __last)
return fold_left_with_iter_result<_Ip, _Up>{std::move(__first), _Up(std::move(__init))};
- }
_Up __result = std::invoke(__f, std::move(__init), *__first);
- for (++__first; __first != __last; ++__first) {
+ for (++__first; __first != __last; ++__first)
__result = std::invoke(__f, std::move(__result), *__first);
- }
return fold_left_with_iter_result<_Ip, _Up>{std::move(__first), std::move(__result)};
}
@@ -130,14 +128,12 @@ struct __fold_left_first_with_iter {
using _Up = decltype(fold_left(std::move(__first), __last, iter_value_t<_Ip>(*__first), __f));
// case of empty range
- if (__first == __last) {
+ if (__first == __last)
return fold_left_first_with_iter_result<_Ip, optional<_Up>>{std::move(__first), optional<_Up>()};
- }
_Up __result(*__first);
- for (++__first; __first != __last; ++__first) {
+ for (++__first; __first != __last; ++__first)
__result = std::invoke(__f, std::move(__result), *__first);
- }
return fold_left_first_with_iter_result<_Ip, optional<_Up>>{std::move(__first), optional<_Up>(std::move(__result))};
}
>From 9ea524df398f379a2e2e6deb9fab8f396b72fa63 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Fri, 21 Mar 2025 17:03:23 +0800
Subject: [PATCH 12/13] Split `left_folds.pass.cpp` into four files; each for
an algorithm
---
...lds.pass.cpp => ranges.fold_left.pass.cpp} | 164 ++----------
.../alg.fold/ranges.fold_left_first.pass.cpp | 162 +++++++++++
.../ranges.fold_left_first_with_iter.pass.cpp | 170 ++++++++++++
.../ranges.fold_left_with_iter.pass.cpp | 253 ++++++++++++++++++
4 files changed, 602 insertions(+), 147 deletions(-)
rename libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/{left_folds.pass.cpp => ranges.fold_left.pass.cpp} (56%)
create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/ranges.fold_left_first.pass.cpp
create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/ranges.fold_left_first_with_iter.pass.cpp
create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/ranges.fold_left_with_iter.pass.cpp
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/left_folds.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/ranges.fold_left.pass.cpp
similarity index 56%
rename from libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/left_folds.pass.cpp
rename to libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/ranges.fold_left.pass.cpp
index c5b75a2e4ca3c..21fe985d5e67a 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/left_folds.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/ranges.fold_left.pass.cpp
@@ -8,24 +8,17 @@
// <algorithm>
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-
-// MSVC warning C4244: 'argument': conversion from 'double' to 'const int', possible loss of data
-// ADDITIONAL_COMPILE_FLAGS(cl-style-warnings): /wd4244
-
// template<input_iterator I, sentinel_for<I> S, class T,
-// indirectly-binary-left-foldable<T, I> F>
-// constexpr see below ranges::fold_left_with_iter(I first, S last, T init, F f);
-//
-// template<input_range R, class T, indirectly-binary-left-foldable<T, iterator_t<R>> F>
-// constexpr see below ranges::fold_left_with_iter(R&& r, T init, F f);
+// indirectly-binary-left-foldable<T, I> F>
+// constexpr auto fold_left(I first, S last, T init, F f); // since C++23
-// template<input_iterator I, sentinel_for<I> S, class T,
-// indirectly-binary-left-foldable<T, I> F>
-// constexpr see below ranges::fold_left(I first, S last, T init, F f);
-//
// template<input_range R, class T, indirectly-binary-left-foldable<T, iterator_t<R>> F>
-// constexpr see below ranges::fold_left(R&& r, T init, F f);
+// constexpr auto fold_left(R&& r, T init, F f); // since C++23
+
+// REQUIRES: std-at-least-c++23
+
+// MSVC warning C4244: 'argument': conversion from 'double' to 'const int', possible loss of data
+// ADDITIONAL_COMPILE_FLAGS(cl-style-warnings): /wd4244
#include <algorithm>
#include <cassert>
@@ -39,7 +32,6 @@
#include <string_view>
#include <string>
#include <vector>
-#include <optional>
#include "test_macros.h"
#include "test_range.h"
@@ -50,18 +42,10 @@
# include <sstream>
#endif
-using std::ranges::fold_left;
-using std::ranges::fold_left_first;
-using std::ranges::fold_left_first_with_iter;
-using std::ranges::fold_left_with_iter;
-
template <class Result, class Range, class T>
concept is_in_value_result =
std::same_as<Result, std::ranges::fold_left_with_iter_result<std::ranges::iterator_t<Range>, T>>;
-template <class Result, class T>
-concept is_dangling_with = std::same_as<Result, std::ranges::fold_left_with_iter_result<std::ranges::dangling, T>>;
-
struct Integer {
int value;
@@ -76,31 +60,14 @@ template <std::ranges::input_range R, class T, class F, std::equality_comparable
requires std::copyable<R>
constexpr void check_iterator(R& r, T const& init, F f, Expected const& expected) {
{
- is_in_value_result<R, Expected> decltype(auto) result = fold_left_with_iter(r.begin(), r.end(), init, f);
- assert(result.in == r.end());
- assert(result.value == expected);
- }
-
- {
- auto telemetry = invocable_telemetry();
- auto f2 = invocable_with_telemetry(f, telemetry);
- is_in_value_result<R, Expected> decltype(auto) result = fold_left_with_iter(r.begin(), r.end(), init, f2);
- assert(result.in == r.end());
- assert(result.value == expected);
- assert(telemetry.invocations == std::ranges::distance(r));
- assert(telemetry.moves == 0);
- assert(telemetry.copies == 1);
- }
-
- {
- std::same_as<Expected> decltype(auto) result = fold_left(r.begin(), r.end(), init, f);
+ std::same_as<Expected> decltype(auto) result = std::ranges::fold_left(r.begin(), r.end(), init, f);
assert(result == expected);
}
{
auto telemetry = invocable_telemetry();
auto f2 = invocable_with_telemetry(f, telemetry);
- std::same_as<Expected> decltype(auto) result = fold_left(r.begin(), r.end(), init, f2);
+ std::same_as<Expected> decltype(auto) result = std::ranges::fold_left(r.begin(), r.end(), init, f2);
assert(result == expected);
assert(telemetry.invocations == std::ranges::distance(r));
assert(telemetry.moves == 0);
@@ -108,61 +75,13 @@ constexpr void check_iterator(R& r, T const& init, F f, Expected const& expected
}
}
-template <std::ranges::input_range R, class F, std::equality_comparable Expected>
- requires std::copyable<R>
-constexpr void check_iterator(R& r, F f, std::optional<Expected> const& expected) {
- {
- is_in_value_result<R, std::optional<Expected>> decltype(auto) result =
- fold_left_first_with_iter(r.begin(), r.end(), f);
- assert(result.in == r.end());
- assert(result.value == expected);
- }
-
- {
- auto telemetry = invocable_telemetry();
- auto f2 = invocable_with_telemetry(f, telemetry);
- is_in_value_result<R, std::optional<Expected>> decltype(auto) result =
- fold_left_first_with_iter(r.begin(), r.end(), f2);
- assert(result.in == r.end());
- assert(result.value == expected);
- if (result.value.has_value()) {
- assert(telemetry.invocations == std::ranges::distance(r) - 1);
- assert(telemetry.moves == 0);
- assert(telemetry.copies == 1);
- }
- }
-
- {
- std::same_as<std::optional<Expected>> decltype(auto) result = fold_left_first(r.begin(), r.end(), f);
- assert(result == expected);
- }
-
- {
- auto telemetry = invocable_telemetry();
- auto f2 = invocable_with_telemetry(f, telemetry);
- std::same_as<std::optional<Expected>> decltype(auto) result = fold_left_first(r.begin(), r.end(), f2);
- assert(result == expected);
- if (result.has_value()) {
- assert(telemetry.invocations == std::ranges::distance(r) - 1);
- assert(telemetry.moves == 0);
- assert(telemetry.copies == 1);
- }
- }
-}
-
template <std::ranges::input_range R, class T, class F, std::equality_comparable Expected>
requires std::copyable<R>
constexpr void check_lvalue_range(R& r, T const& init, F f, Expected const& expected) {
- {
- is_in_value_result<R, Expected> decltype(auto) result = fold_left_with_iter(r, init, f);
- assert(result.in == r.end());
- assert(result.value == expected);
- }
-
{
auto telemetry = invocable_telemetry();
auto f2 = invocable_with_telemetry(f, telemetry);
- std::same_as<Expected> decltype(auto) result = fold_left(r, init, f2);
+ std::same_as<Expected> decltype(auto) result = std::ranges::fold_left(r, init, f2);
assert(result == expected);
assert(telemetry.invocations == std::ranges::distance(r));
assert(telemetry.moves == 0);
@@ -170,14 +89,14 @@ constexpr void check_lvalue_range(R& r, T const& init, F f, Expected const& expe
}
{
- std::same_as<Expected> decltype(auto) result = fold_left(r, init, f);
+ std::same_as<Expected> decltype(auto) result = std::ranges::fold_left(r, init, f);
assert(result == expected);
}
{
auto telemetry = invocable_telemetry();
auto f2 = invocable_with_telemetry(f, telemetry);
- std::same_as<Expected> decltype(auto) result = fold_left(r, init, f2);
+ std::same_as<Expected> decltype(auto) result = std::ranges::fold_left(r, init, f2);
assert(result == expected);
assert(telemetry.invocations == std::ranges::distance(r));
assert(telemetry.moves == 0);
@@ -188,26 +107,9 @@ constexpr void check_lvalue_range(R& r, T const& init, F f, Expected const& expe
template <std::ranges::input_range R, class T, class F, std::equality_comparable Expected>
requires std::copyable<R>
constexpr void check_rvalue_range(R& r, T const& init, F f, Expected const& expected) {
- {
- auto r2 = r;
- is_dangling_with<Expected> decltype(auto) result = fold_left_with_iter(std::move(r2), init, f);
- assert(result.value == expected);
- }
-
- {
- auto telemetry = invocable_telemetry();
- auto f2 = invocable_with_telemetry(f, telemetry);
- auto r2 = r;
- is_dangling_with<Expected> decltype(auto) result = fold_left_with_iter(std::move(r2), init, f2);
- assert(result.value == expected);
- assert(telemetry.invocations == std::ranges::distance(r));
- assert(telemetry.moves == 0);
- assert(telemetry.copies == 1);
- }
-
{
auto r2 = r;
- std::same_as<Expected> decltype(auto) result = fold_left(std::move(r2), init, f);
+ std::same_as<Expected> decltype(auto) result = std::ranges::fold_left(std::move(r2), init, f);
assert(result == expected);
}
@@ -215,7 +117,7 @@ constexpr void check_rvalue_range(R& r, T const& init, F f, Expected const& expe
auto telemetry = invocable_telemetry();
auto f2 = invocable_with_telemetry(f, telemetry);
auto r2 = r;
- std::same_as<Expected> decltype(auto) result = fold_left(std::move(r2), init, f2);
+ std::same_as<Expected> decltype(auto) result = std::ranges::fold_left(std::move(r2), init, f2);
assert(result == expected);
assert(telemetry.invocations == std::ranges::distance(r));
assert(telemetry.moves == 0);
@@ -231,29 +133,19 @@ constexpr void check(R r, T const& init, F f, Expected const& expected) {
check_rvalue_range(r, init, f, expected);
}
-template <std::ranges::input_range R, class F, std::equality_comparable Expected>
- requires std::copyable<R>
-constexpr void check(R r, F f, std::optional<Expected> const& expected) {
- check_iterator(r, f, expected);
-}
-
constexpr void empty_range_test_case() {
auto const data = std::vector<int>{};
check(data, 100, std::plus(), 100);
check(data, -100, std::multiplies(), -100);
- check(data, std::plus(), std::optional<int>());
check(data | std::views::take_while([](auto) { return false; }), 1.23, std::plus(), 1.23);
check(data, Integer(52), &Integer::plus, Integer(52));
- check(data | std::views::take_while([](auto) { return false; }), std::plus(), std::optional<int>());
}
constexpr void common_range_test_case() {
auto const data = std::vector<int>{1, 2, 3, 4};
check(data, 0, std::plus(), triangular_sum(data));
check(data, 1, std::multiplies(), factorial(data.back()));
- check(data, std::plus(), std::optional(triangular_sum(data)));
- check(data, std::multiplies(), std::optional(factorial(data.back())));
auto multiply_with_prev = [n = 1](auto const x, auto const y) mutable {
auto const result = x * y * n;
@@ -261,7 +153,6 @@ constexpr void common_range_test_case() {
return static_cast<std::size_t>(result);
};
check(data, 1, multiply_with_prev, factorial(data.size()) * factorial(data.size() - 1));
- check(data, multiply_with_prev, std::optional(factorial(data.size()) * factorial(data.size() - 1)));
auto fib = [n = 1](auto x, auto) mutable {
auto old_x = x;
@@ -293,7 +184,6 @@ constexpr void non_common_range_test_case() {
auto data = std::vector<std::string>{"five", "three", "two", "six", "one", "four"};
auto range = data | std::views::transform(parse);
check(range, 0, std::plus(), triangular_sum(range));
- check(range, std::plus(), std::optional(triangular_sum(range)));
}
{
@@ -305,7 +195,6 @@ constexpr void non_common_range_test_case() {
auto range =
std::views::lazy_split(data, ' ') | std::views::transform(to_string_view) | std::views::transform(parse);
check(range, 0, std::plus(), triangular_sum(range));
- check(range, std::plus(), std::optional(triangular_sum(range)));
}
}
@@ -327,32 +216,13 @@ void runtime_only_test_case() {
{
auto input = std::istringstream(raw_data);
auto data = std::views::istream<std::string>(input);
- is_in_value_result<std::ranges::basic_istream_view<std::string, char>, std::string> decltype(auto) result =
- fold_left_with_iter(data.begin(), data.end(), init, std::plus());
-
- assert(result.in == data.end());
- assert(result.value == expected);
- }
-
- {
- auto input = std::istringstream(raw_data);
- auto data = std::views::istream<std::string>(input);
- is_in_value_result<std::ranges::basic_istream_view<std::string, char>, std::string> decltype(auto) result =
- fold_left_with_iter(data, init, std::plus());
- assert(result.in == data.end());
- assert(result.value == expected);
- }
-
- {
- auto input = std::istringstream(raw_data);
- auto data = std::views::istream<std::string>(input);
- assert(fold_left(data.begin(), data.end(), init, std::plus()) == expected);
+ assert(std::ranges::fold_left(data.begin(), data.end(), init, std::plus()) == expected);
}
{
auto input = std::istringstream(raw_data);
auto data = std::views::istream<std::string>(input);
- assert(fold_left(data, init, std::plus()) == expected);
+ assert(std::ranges::fold_left(data, init, std::plus()) == expected);
}
}
#endif
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/ranges.fold_left_first.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/ranges.fold_left_first.pass.cpp
new file mode 100644
index 0000000000000..e0a666b11c426
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/ranges.fold_left_first.pass.cpp
@@ -0,0 +1,162 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// template<input_iterator I, sentinel_for<I> S,
+// indirectly-binary-left-foldable<T, I> F>
+// requires constructible_from<iter_value_t<I>, iter_reference_t<I>>
+// constexpr see below fold_left_first(I first, S last, F f); // since C++23
+
+// template<input_range R, indirectly-binary-left-foldable<T, iterator_t<R>> F>
+// requires constructible_from<range_value_t<R>, range_reference_t<R>>
+// constexpr see below fold_left_first(R&& r, F f); // since C++23
+
+// REQUIRES: std-at-least-c++23
+
+// MSVC warning C4244: 'argument': conversion from 'double' to 'const int', possible loss of data
+// ADDITIONAL_COMPILE_FLAGS(cl-style-warnings): /wd4244
+
+#include <algorithm>
+#include <cassert>
+#include <concepts>
+#include <functional>
+#include <iterator>
+#include <ranges>
+#include <string_view>
+#include <string>
+#include <vector>
+#include <optional>
+
+#include "test_macros.h"
+#include "test_range.h"
+#include "invocable_with_telemetry.h"
+#include "maths.h"
+
+template <class Result, class Range, class T>
+concept is_in_value_result =
+ std::same_as<Result, std::ranges::fold_left_first_with_iter_result<std::ranges::iterator_t<Range>, T>>;
+
+struct Integer {
+ int value;
+
+ constexpr Integer(int const x) : value(x) {}
+
+ constexpr Integer plus(int const x) const { return Integer{value + x}; }
+
+ friend constexpr bool operator==(Integer const& x, Integer const& y) = default;
+};
+
+template <std::ranges::input_range R, class F, std::equality_comparable Expected>
+ requires std::copyable<R>
+constexpr void check_iterator(R& r, F f, std::optional<Expected> const& expected) {
+ {
+ std::same_as<std::optional<Expected>> decltype(auto) result = std::ranges::fold_left_first(r.begin(), r.end(), f);
+ assert(result == expected);
+ }
+
+ {
+ std::same_as<std::optional<Expected>> decltype(auto) result = std::ranges::fold_left_first(r, f);
+ assert(result == expected);
+ }
+
+ {
+ auto telemetry = invocable_telemetry();
+ auto f2 = invocable_with_telemetry(f, telemetry);
+ std::same_as<std::optional<Expected>> decltype(auto) result = std::ranges::fold_left_first(r.begin(), r.end(), f2);
+ assert(result == expected);
+ if (result.has_value()) {
+ assert(telemetry.invocations == std::ranges::distance(r) - 1);
+ assert(telemetry.moves == 0);
+ assert(telemetry.copies == 1);
+ }
+ }
+
+ {
+ auto telemetry = invocable_telemetry();
+ auto f2 = invocable_with_telemetry(f, telemetry);
+ std::same_as<std::optional<Expected>> decltype(auto) result = std::ranges::fold_left_first(r, f2);
+ assert(result == expected);
+ if (result.has_value()) {
+ assert(telemetry.invocations == std::ranges::distance(r) - 1);
+ assert(telemetry.moves == 0);
+ assert(telemetry.copies == 1);
+ }
+ }
+}
+
+template <std::ranges::input_range R, class F, std::equality_comparable Expected>
+ requires std::copyable<R>
+constexpr void check(R r, F f, std::optional<Expected> const& expected) {
+ check_iterator(r, f, expected);
+}
+
+constexpr void empty_range_test_case() {
+ auto const data = std::vector<int>{};
+ check(data, std::plus(), std::optional<int>());
+ check(data | std::views::take_while([](auto) { return false; }), std::plus(), std::optional<int>());
+}
+
+constexpr void common_range_test_case() {
+ auto const data = std::vector<int>{1, 2, 3, 4};
+ check(data, std::plus(), std::optional(triangular_sum(data)));
+ check(data, std::multiplies(), std::optional(factorial(data.back())));
+
+ auto multiply_with_prev = [n = 1](auto const x, auto const y) mutable {
+ auto const result = x * y * n;
+ n = y;
+ return static_cast<std::size_t>(result);
+ };
+ check(data, multiply_with_prev, std::optional(factorial(data.size()) * factorial(data.size() - 1)));
+}
+
+constexpr void non_common_range_test_case() {
+ auto parse = [](std::string_view const s) {
+ return s == "zero" ? 0.0
+ : s == "one" ? 1.0
+ : s == "two" ? 2.0
+ : s == "three" ? 3.0
+ : s == "four" ? 4.0
+ : s == "five" ? 5.0
+ : s == "six" ? 6.0
+ : s == "seven" ? 7.0
+ : s == "eight" ? 8.0
+ : s == "nine" ? 9.0
+ : (assert(false), 10.0); // the number here is arbitrary
+ };
+
+ {
+ auto data = std::vector<std::string>{"five", "three", "two", "six", "one", "four"};
+ auto range = data | std::views::transform(parse);
+ check(range, std::plus(), std::optional(triangular_sum(range)));
+ }
+
+ {
+ auto data = std::string("five three two six one four");
+ auto to_string_view = [](auto&& r) {
+ auto const n = std::ranges::distance(r);
+ return std::string_view(&*r.begin(), n);
+ };
+ auto range =
+ std::views::lazy_split(data, ' ') | std::views::transform(to_string_view) | std::views::transform(parse);
+ check(range, std::plus(), std::optional(triangular_sum(range)));
+ }
+}
+
+constexpr bool test_case() {
+ empty_range_test_case();
+ common_range_test_case();
+ non_common_range_test_case();
+ return true;
+}
+
+int main(int, char**) {
+ test_case();
+ static_assert(test_case());
+ return 0;
+}
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/ranges.fold_left_first_with_iter.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/ranges.fold_left_first_with_iter.pass.cpp
new file mode 100644
index 0000000000000..9eed14c0039d9
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/ranges.fold_left_first_with_iter.pass.cpp
@@ -0,0 +1,170 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// template<input_iterator I, sentinel_for<I> S,
+// indirectly-binary-left-foldable<T, I> F>
+// requires constructible_from<iter_value_t<I>, iter_reference_t<I>>
+// constexpr see below fold_left_first_with_iter(I first, S last, F f); // since C++23
+
+// template<input_range R, indirectly-binary-left-foldable<T, iterator_t<R>> F>
+// requires constructible_from<range_value_t<R>, range_reference_t<R>>
+// constexpr see below fold_left_first_with_iter(R&& r, F f); // since C++23
+
+// REQUIRES: std-at-least-c++23
+
+// MSVC warning C4244: 'argument': conversion from 'double' to 'const int', possible loss of data
+// ADDITIONAL_COMPILE_FLAGS(cl-style-warnings): /wd4244
+
+#include <algorithm>
+#include <cassert>
+#include <concepts>
+#include <functional>
+#include <iterator>
+#include <ranges>
+#include <string_view>
+#include <string>
+#include <vector>
+#include <optional>
+
+#include "test_macros.h"
+#include "test_range.h"
+#include "invocable_with_telemetry.h"
+#include "maths.h"
+
+template <class Result, class Range, class T>
+concept is_in_value_result =
+ std::same_as<Result, std::ranges::fold_left_first_with_iter_result<std::ranges::iterator_t<Range>, T>>;
+
+struct Integer {
+ int value;
+
+ constexpr Integer(int const x) : value(x) {}
+
+ constexpr Integer plus(int const x) const { return Integer{value + x}; }
+
+ friend constexpr bool operator==(Integer const& x, Integer const& y) = default;
+};
+
+template <std::ranges::input_range R, class F, std::equality_comparable Expected>
+ requires std::copyable<R>
+constexpr void check_iterator(R& r, F f, std::optional<Expected> const& expected) {
+ {
+ is_in_value_result<R, std::optional<Expected>> decltype(auto) result =
+ std::ranges::fold_left_first_with_iter(r.begin(), r.end(), f);
+ assert(result.in == r.end());
+ assert(result.value == expected);
+ }
+
+ {
+ is_in_value_result<R, std::optional<Expected>> decltype(auto) result =
+ std::ranges::fold_left_first_with_iter(r, f);
+ assert(result.in == r.end());
+ assert(result.value == expected);
+ }
+
+ {
+ auto telemetry = invocable_telemetry();
+ auto f2 = invocable_with_telemetry(f, telemetry);
+ is_in_value_result<R, std::optional<Expected>> decltype(auto) result =
+ std::ranges::fold_left_first_with_iter(r.begin(), r.end(), f2);
+ assert(result.in == r.end());
+ assert(result.value == expected);
+ if (result.value.has_value()) {
+ assert(telemetry.invocations == std::ranges::distance(r) - 1);
+ assert(telemetry.moves == 0);
+ assert(telemetry.copies == 1);
+ }
+ }
+
+ {
+ auto telemetry = invocable_telemetry();
+ auto f2 = invocable_with_telemetry(f, telemetry);
+ is_in_value_result<R, std::optional<Expected>> decltype(auto) result =
+ std::ranges::fold_left_first_with_iter(r, f2);
+ assert(result.in == r.end());
+ assert(result.value == expected);
+ if (result.value.has_value()) {
+ assert(telemetry.invocations == std::ranges::distance(r) - 1);
+ assert(telemetry.moves == 0);
+ assert(telemetry.copies == 1);
+ }
+ }
+}
+
+template <std::ranges::input_range R, class F, std::equality_comparable Expected>
+ requires std::copyable<R>
+constexpr void check(R r, F f, std::optional<Expected> const& expected) {
+ check_iterator(r, f, expected);
+}
+
+constexpr void empty_range_test_case() {
+ auto const data = std::vector<int>{};
+ check(data, std::plus(), std::optional<int>());
+ check(data | std::views::take_while([](auto) { return false; }), std::plus(), std::optional<int>());
+}
+
+constexpr void common_range_test_case() {
+ auto const data = std::vector<int>{1, 2, 3, 4};
+ check(data, std::plus(), std::optional(triangular_sum(data)));
+ check(data, std::multiplies(), std::optional(factorial(data.back())));
+
+ auto multiply_with_prev = [n = 1](auto const x, auto const y) mutable {
+ auto const result = x * y * n;
+ n = y;
+ return static_cast<std::size_t>(result);
+ };
+ check(data, multiply_with_prev, std::optional(factorial(data.size()) * factorial(data.size() - 1)));
+}
+
+constexpr void non_common_range_test_case() {
+ auto parse = [](std::string_view const s) {
+ return s == "zero" ? 0.0
+ : s == "one" ? 1.0
+ : s == "two" ? 2.0
+ : s == "three" ? 3.0
+ : s == "four" ? 4.0
+ : s == "five" ? 5.0
+ : s == "six" ? 6.0
+ : s == "seven" ? 7.0
+ : s == "eight" ? 8.0
+ : s == "nine" ? 9.0
+ : (assert(false), 10.0); // the number here is arbitrary
+ };
+
+ {
+ auto data = std::vector<std::string>{"five", "three", "two", "six", "one", "four"};
+ auto range = data | std::views::transform(parse);
+ check(range, std::plus(), std::optional(triangular_sum(range)));
+ }
+
+ {
+ auto data = std::string("five three two six one four");
+ auto to_string_view = [](auto&& r) {
+ auto const n = std::ranges::distance(r);
+ return std::string_view(&*r.begin(), n);
+ };
+ auto range =
+ std::views::lazy_split(data, ' ') | std::views::transform(to_string_view) | std::views::transform(parse);
+ check(range, std::plus(), std::optional(triangular_sum(range)));
+ }
+}
+
+constexpr bool test_case() {
+ empty_range_test_case();
+ common_range_test_case();
+ non_common_range_test_case();
+ return true;
+}
+
+int main(int, char**) {
+ test_case();
+ static_assert(test_case());
+ return 0;
+}
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/ranges.fold_left_with_iter.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/ranges.fold_left_with_iter.pass.cpp
new file mode 100644
index 0000000000000..dd8663a512520
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/ranges.fold_left_with_iter.pass.cpp
@@ -0,0 +1,253 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// template<input_iterator I, sentinel_for<I> S, class T,
+// indirectly-binary-left-foldable<T, I> F>
+// constexpr see below fold_left_with_iter(I first, S last, T init, F f); // since C++23
+
+// template<input_range R, class T, indirectly-binary-left-foldable<T, iterator_t<R>> F>
+// constexpr see below fold_left_with_iter(R&& r, T init, F f); // since C++23
+
+// REQUIRES: std-at-least-c++23
+
+// MSVC warning C4244: 'argument': conversion from 'double' to 'const int', possible loss of data
+// ADDITIONAL_COMPILE_FLAGS(cl-style-warnings): /wd4244
+
+#include <algorithm>
+#include <cassert>
+#include <concepts>
+#include <deque>
+#include <forward_list>
+#include <functional>
+#include <iterator>
+#include <list>
+#include <ranges>
+#include <string_view>
+#include <string>
+#include <vector>
+
+#include "test_macros.h"
+#include "test_range.h"
+#include "invocable_with_telemetry.h"
+#include "maths.h"
+
+#if !defined(TEST_HAS_NO_LOCALIZATION)
+# include <sstream>
+#endif
+
+template <class Result, class Range, class T>
+concept is_in_value_result =
+ std::same_as<Result, std::ranges::fold_left_with_iter_result<std::ranges::iterator_t<Range>, T>>;
+
+template <class Result, class T>
+concept is_dangling_with = std::same_as<Result, std::ranges::fold_left_with_iter_result<std::ranges::dangling, T>>;
+
+struct Integer {
+ int value;
+
+ constexpr Integer(int const x) : value(x) {}
+
+ constexpr Integer plus(int const x) const { return Integer{value + x}; }
+
+ friend constexpr bool operator==(Integer const& x, Integer const& y) = default;
+};
+
+template <std::ranges::input_range R, class T, class F, std::equality_comparable Expected>
+ requires std::copyable<R>
+constexpr void check_iterator(R& r, T const& init, F f, Expected const& expected) {
+ {
+ is_in_value_result<R, Expected> decltype(auto) result =
+ std::ranges::fold_left_with_iter(r.begin(), r.end(), init, f);
+ assert(result.in == r.end());
+ assert(result.value == expected);
+ }
+
+ {
+ auto telemetry = invocable_telemetry();
+ auto f2 = invocable_with_telemetry(f, telemetry);
+ is_in_value_result<R, Expected> decltype(auto) result =
+ std::ranges::fold_left_with_iter(r.begin(), r.end(), init, f2);
+ assert(result.in == r.end());
+ assert(result.value == expected);
+ assert(telemetry.invocations == std::ranges::distance(r));
+ assert(telemetry.moves == 0);
+ assert(telemetry.copies == 1);
+ }
+}
+
+template <std::ranges::input_range R, class T, class F, std::equality_comparable Expected>
+ requires std::copyable<R>
+constexpr void check_lvalue_range(R& r, T const& init, F f, Expected const& expected) {
+ is_in_value_result<R, Expected> decltype(auto) result = std::ranges::fold_left_with_iter(r, init, f);
+ assert(result.in == r.end());
+ assert(result.value == expected);
+}
+
+template <std::ranges::input_range R, class T, class F, std::equality_comparable Expected>
+ requires std::copyable<R>
+constexpr void check_rvalue_range(R& r, T const& init, F f, Expected const& expected) {
+ {
+ auto r2 = r;
+ is_dangling_with<Expected> decltype(auto) result = std::ranges::fold_left_with_iter(std::move(r2), init, f);
+ assert(result.value == expected);
+ }
+
+ {
+ auto telemetry = invocable_telemetry();
+ auto f2 = invocable_with_telemetry(f, telemetry);
+ auto r2 = r;
+ is_dangling_with<Expected> decltype(auto) result = std::ranges::fold_left_with_iter(std::move(r2), init, f2);
+ assert(result.value == expected);
+ assert(telemetry.invocations == std::ranges::distance(r));
+ assert(telemetry.moves == 0);
+ assert(telemetry.copies == 1);
+ }
+}
+
+template <std::ranges::input_range R, class T, class F, std::equality_comparable Expected>
+ requires std::copyable<R>
+constexpr void check(R r, T const& init, F f, Expected const& expected) {
+ check_iterator(r, init, f, expected);
+ check_lvalue_range(r, init, f, expected);
+ check_rvalue_range(r, init, f, expected);
+}
+
+constexpr void empty_range_test_case() {
+ auto const data = std::vector<int>{};
+ check(data, 100, std::plus(), 100);
+ check(data, -100, std::multiplies(), -100);
+
+ check(data | std::views::take_while([](auto) { return false; }), 1.23, std::plus(), 1.23);
+ check(data, Integer(52), &Integer::plus, Integer(52));
+}
+
+constexpr void common_range_test_case() {
+ auto const data = std::vector<int>{1, 2, 3, 4};
+ check(data, 0, std::plus(), triangular_sum(data));
+ check(data, 1, std::multiplies(), factorial(data.back()));
+
+ auto multiply_with_prev = [n = 1](auto const x, auto const y) mutable {
+ auto const result = x * y * n;
+ n = y;
+ return static_cast<std::size_t>(result);
+ };
+ check(data, 1, multiply_with_prev, factorial(data.size()) * factorial(data.size() - 1));
+
+ auto fib = [n = 1](auto x, auto) mutable {
+ auto old_x = x;
+ x += n;
+ n = old_x;
+ return x;
+ };
+ check(data, 0, fib, fibonacci(data.back()));
+
+ check(data, Integer(0), &Integer::plus, Integer(triangular_sum(data)));
+}
+
+constexpr void non_common_range_test_case() {
+ auto parse = [](std::string_view const s) {
+ return s == "zero" ? 0.0
+ : s == "one" ? 1.0
+ : s == "two" ? 2.0
+ : s == "three" ? 3.0
+ : s == "four" ? 4.0
+ : s == "five" ? 5.0
+ : s == "six" ? 6.0
+ : s == "seven" ? 7.0
+ : s == "eight" ? 8.0
+ : s == "nine" ? 9.0
+ : (assert(false), 10.0); // the number here is arbitrary
+ };
+
+ {
+ auto data = std::vector<std::string>{"five", "three", "two", "six", "one", "four"};
+ auto range = data | std::views::transform(parse);
+ check(range, 0, std::plus(), triangular_sum(range));
+ }
+
+ {
+ auto data = std::string("five three two six one four");
+ auto to_string_view = [](auto&& r) {
+ auto const n = std::ranges::distance(r);
+ return std::string_view(&*r.begin(), n);
+ };
+ auto range =
+ std::views::lazy_split(data, ' ') | std::views::transform(to_string_view) | std::views::transform(parse);
+ check(range, 0, std::plus(), triangular_sum(range));
+ }
+}
+
+constexpr bool test_case() {
+ empty_range_test_case();
+ common_range_test_case();
+ non_common_range_test_case();
+ return true;
+}
+
+// Most containers aren't constexpr
+void runtime_only_test_case() {
+#if !defined(TEST_HAS_NO_LOCALIZATION)
+ { // istream_view is a genuine input range and needs specific handling.
+ constexpr auto raw_data = "Shells Orange Syrup Baratie Cocoyashi Loguetown";
+ constexpr auto expected = "WindmillShellsOrangeSyrupBaratieCocoyashiLoguetown";
+ auto const init = std::string("Windmill");
+
+ {
+ auto input = std::istringstream(raw_data);
+ auto data = std::views::istream<std::string>(input);
+ is_in_value_result<std::ranges::basic_istream_view<std::string, char>, std::string> decltype(auto) result =
+ std::ranges::fold_left_with_iter(data.begin(), data.end(), init, std::plus());
+
+ assert(result.in == data.end());
+ assert(result.value == expected);
+ }
+
+ {
+ auto input = std::istringstream(raw_data);
+ auto data = std::views::istream<std::string>(input);
+ is_in_value_result<std::ranges::basic_istream_view<std::string, char>, std::string> decltype(auto) result =
+ std::ranges::fold_left_with_iter(data, init, std::plus());
+ assert(result.in == data.end());
+ assert(result.value == expected);
+ }
+ }
+#endif
+ {
+ auto const data = std::forward_list<int>{1, 3, 5, 7, 9};
+ auto const n = std::ranges::distance(data);
+ auto const expected = static_cast<float>(n * n); // sum of n consecutive odd numbers = n^2
+ check(data, 0.0f, std::plus(), expected);
+ }
+
+ {
+ auto const data = std::list<int>{2, 4, 6, 8, 10, 12};
+ auto const expected = triangular_sum(data);
+ check(data, 0, std::plus<long>(), static_cast<long>(expected));
+ }
+
+ {
+ auto const data = std::deque<double>{-1.1, -2.2, -3.3, -4.4, -5.5, -6.6};
+ auto plus = [](int const x, double const y) { return x + y; };
+ auto const expected = -21.6; // int( 0.0) + -1.1 = 0 + -1.1 = -1.1
+ // int(- 1.1) + -2.2 = - 1 + -2.2 = -3.2
+ // int(- 3.2) + -3.3 = - 3 + -3.3 = -6.3
+ // int(- 6.3) + -4.4 = - 6 + -4.4 = -10.4
+ // int(-10.4) + -5.5 = -10 + -5.5 = -15.5
+ // int(-15.5) + -6.6 = -15 + -6.6 = -21.6.
+ check(data, 0.0, plus, expected);
+ }
+}
+
+int main(int, char**) {
+ test_case();
+ static_assert(test_case());
+ runtime_only_test_case();
+ return 0;
+}
>From ef107b70f53d4f0839015f57913fb7d42b9f4e18 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Fri, 21 Mar 2025 17:10:22 +0800
Subject: [PATCH 13/13] Missed clang-formatting
---
.../alg.fold/ranges.fold_left_first_with_iter.pass.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/ranges.fold_left_first_with_iter.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/ranges.fold_left_first_with_iter.pass.cpp
index 9eed14c0039d9..15f04e8d6975f 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/ranges.fold_left_first_with_iter.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/ranges.fold_left_first_with_iter.pass.cpp
@@ -63,8 +63,7 @@ constexpr void check_iterator(R& r, F f, std::optional<Expected> const& expected
}
{
- is_in_value_result<R, std::optional<Expected>> decltype(auto) result =
- std::ranges::fold_left_first_with_iter(r, f);
+ is_in_value_result<R, std::optional<Expected>> decltype(auto) result = std::ranges::fold_left_first_with_iter(r, f);
assert(result.in == r.end());
assert(result.value == expected);
}
More information about the libcxx-commits
mailing list