Skip to content

Commit 6359ec7

Browse files
committed
Initial impl of fold_left_first_with_iter
1 parent 1135d36 commit 6359ec7

File tree

4 files changed

+124
-0
lines changed

4 files changed

+124
-0
lines changed

libcxx/include/__algorithm/ranges_fold.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ struct in_value_result {
6262
template <class _Ip, class _Tp>
6363
using fold_left_with_iter_result = in_value_result<_Ip, _Tp>;
6464

65+
template <class _Ip, class _Tp>
66+
using fold_left_first_with_iter_result = in_value_result<_Ip, _Tp>;
67+
6568
template <class _Fp, class _Tp, class _Ip, class _Rp, class _Up = decay_t<_Rp>>
6669
concept __indirectly_binary_left_foldable_impl =
6770
convertible_to<_Rp, _Up> && //
@@ -118,6 +121,54 @@ struct __fold_left {
118121
};
119122

120123
inline constexpr auto fold_left = __fold_left();
124+
125+
struct __fold_left_first_with_iter {
126+
template <input_iterator _Ip, sentinel_for<_Ip> _Sp, __indirectly_binary_left_foldable<iter_value_t<_Ip>, _Ip> _Fp>
127+
requires constructible_from<iter_value_t<_Ip>, iter_reference_t<_Ip>>
128+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Ip __first, _Sp __last, _Fp __f) {
129+
using _Up = decltype(fold_left(std::move(__first), __last, iter_value_t<_Ip>(*__first), __f));
130+
131+
// case of empty range
132+
if (__first == __last) {
133+
return fold_left_first_with_iter_result<_Ip, optional<_Up>>{std::move(__first), optional<_Up>()};
134+
}
135+
136+
optional<_Up> __result(std::in_place, *__first);
137+
for (++__first; __first != __last; ++__first) {
138+
*__result = std::invoke(__f, std::move(*__result), *__first);
139+
}
140+
141+
return fold_left_first_with_iter_result<_Ip, optional<_Up>>{std::move(__first), optional<_Up>(std::move(__result))};
142+
}
143+
144+
template <input_range _Rp, __indirectly_binary_left_foldable<range_value_t<_Rp>, iterator_t<_Rp>> _Fp>
145+
requires constructible_from<range_value_t<_Rp>, range_reference_t<_Rp>>
146+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Rp&& __r, _Fp __f) {
147+
auto __result = operator()(ranges::begin(__r), ranges::end(__r), std::ref(__f));
148+
149+
using _Up = decltype(fold_left(ranges::begin(__r), ranges::end(__r), range_value_t<_Rp>(*ranges::begin(__r)), __f));
150+
return fold_left_first_with_iter_result<borrowed_iterator_t<_Rp>, optional<_Up>>{std::move(__result.in), optional(std::move(__result.value))};
151+
}
152+
};
153+
154+
inline constexpr auto fold_left_first_with_iter = __fold_left_first_with_iter();
155+
156+
struct __fold_left_first {
157+
template <input_iterator _Ip, sentinel_for<_Ip> _Sp, __indirectly_binary_left_foldable<iter_value_t<_Ip>, _Ip> _Fp>
158+
requires constructible_from<iter_value_t<_Ip>, iter_reference_t<_Ip>>
159+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Ip __first, _Sp __last, _Fp __f) {
160+
return fold_left_first_with_iter(std::move(__first), std::move(__last), std::ref(__f)).value;
161+
}
162+
163+
template <input_range _Rp, __indirectly_binary_left_foldable<range_value_t<_Rp>, iterator_t<_Rp>> _Fp>
164+
requires constructible_from<range_value_t<_Rp>, range_reference_t<_Rp>>
165+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Rp&& __r, _Fp __f) {
166+
return fold_left_first_with_iter(ranges::begin(__r), ranges::end(__r), std::ref(__f)).value;
167+
}
168+
};
169+
170+
inline constexpr auto fold_left_first = __fold_left_first();
171+
121172
} // namespace ranges
122173

123174
#endif // _LIBCPP_STD_VER >= 23

libcxx/include/algorithm

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,18 @@ namespace ranges {
948948
template<input_range R, class T, indirectly-binary-left-foldable<T, iterator_t<R>> F>
949949
constexpr see below fold_left_with_iter(R&& r, T init, F f); // since C++23
950950
951+
template<class I, class T>
952+
using fold_left_first_with_iter_result = in_value_result<I, optional<T>>; // since C++23
953+
954+
template<input_iterator I, sentinel_for<I> S,
955+
indirectly-binary-left-foldable<T, I> F>
956+
requires constructible_from<iter_value_t<I>, iter_reference_t<I>>
957+
constexpr see below fold_left_first_with_iter(I first, S last, F f); // since C++23
958+
959+
template<input_range R, indirectly-binary-left-foldable<T, iterator_t<R>> F>
960+
requires constructible_from<range_value_t<R>, range_reference_t<R>>
961+
constexpr see below fold_left_first_with_iter(R&& r, F f); // since C++23
962+
951963
template<forward_iterator I1, sentinel_for<I1> S1, forward_iterator I2, sentinel_for<I2> S2,
952964
class Pred = ranges::equal_to, class Proj1 = identity, class Proj2 = identity>
953965
requires indirectly_comparable<I1, I2, Pred, Proj1, Proj2>

libcxx/test/libcxx/diagnostics/algorithm.nodiscard.verify.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,5 +392,9 @@ void test() {
392392
// expected-warning@-1{{ignoring return value of function declared with 'nodiscard' attribute}}
393393
std::ranges::fold_left_with_iter(iter, iter, 0, std::plus());
394394
// expected-warning@-1{{ignoring return value of function declared with 'nodiscard' attribute}}
395+
std::ranges::fold_left_first_with_iter(range, std::plus());
396+
// expected-warning@-1{{ignoring return value of function declared with 'nodiscard' attribute}}
397+
std::ranges::fold_left_first_with_iter(iter, iter, std::plus());
398+
// expected-warning@-1{{ignoring return value of function declared with 'nodiscard' attribute}}
395399
#endif
396400
}

libcxx/test/std/algorithms/alg.nonmodifying/alg.fold/left_folds.pass.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include <string_view>
4040
#include <string>
4141
#include <vector>
42+
#include <optional>
4243

4344
#include "test_macros.h"
4445
#include "test_range.h"
@@ -50,7 +51,9 @@
5051
#endif
5152

5253
using std::ranges::fold_left;
54+
using std::ranges::fold_left_first;
5355
using std::ranges::fold_left_with_iter;
56+
using std::ranges::fold_left_first_with_iter;
5457

5558
template <class Result, class Range, class T>
5659
concept is_in_value_result =
@@ -105,6 +108,47 @@ constexpr void check_iterator(R& r, T const& init, F f, Expected const& expected
105108
}
106109
}
107110

111+
template <std::ranges::input_range R, class F, std::equality_comparable Expected>
112+
requires std::copyable<R>
113+
constexpr void check_iterator(R& r, F f, std::optional<Expected> const& expected) {
114+
{
115+
is_in_value_result<R, std::optional<Expected>> decltype(auto) result = fold_left_first_with_iter(r.begin(), r.end(), f);
116+
assert(result.in == r.end());
117+
assert(result.value == expected);
118+
}
119+
120+
{
121+
auto telemetry = invocable_telemetry();
122+
auto f2 = invocable_with_telemetry(f, telemetry);
123+
is_in_value_result<R, std::optional<Expected>> decltype(auto) result = fold_left_first_with_iter(r.begin(), r.end(), f2);
124+
assert(result.in == r.end());
125+
assert(result.value == expected);
126+
if (result.value.has_value()) {
127+
assert(telemetry.invocations == std::ranges::distance(r) - 1);
128+
assert(telemetry.moves == 0);
129+
assert(telemetry.copies == 1);
130+
}
131+
}
132+
133+
{
134+
std::same_as<std::optional<Expected>> decltype(auto) result = fold_left_first(r.begin(), r.end(), f);
135+
assert(result == expected);
136+
}
137+
138+
{
139+
auto telemetry = invocable_telemetry();
140+
auto f2 = invocable_with_telemetry(f, telemetry);
141+
std::same_as<std::optional<Expected>> decltype(auto) result = fold_left_first(r.begin(), r.end(), f2);
142+
assert(result == expected);
143+
if (result.has_value()) {
144+
assert(telemetry.invocations == std::ranges::distance(r) - 1);
145+
assert(telemetry.moves == 0);
146+
assert(telemetry.copies == 1);
147+
}
148+
}
149+
}
150+
151+
108152
template <std::ranges::input_range R, class T, class F, std::equality_comparable Expected>
109153
requires std::copyable<R>
110154
constexpr void check_lvalue_range(R& r, T const& init, F f, Expected const& expected) {
@@ -186,26 +230,37 @@ constexpr void check(R r, T const& init, F f, Expected const& expected) {
186230
check_rvalue_range(r, init, f, expected);
187231
}
188232

233+
template <std::ranges::input_range R, class F, std::equality_comparable Expected>
234+
requires std::copyable<R>
235+
constexpr void check(R r, F f, std::optional<Expected> const& expected) {
236+
check_iterator(r, f, expected);
237+
}
238+
189239
constexpr void empty_range_test_case() {
190240
auto const data = std::vector<int>{};
191241
check(data, 100, std::plus(), 100);
192242
check(data, -100, std::multiplies(), -100);
243+
check(data, std::plus(), std::optional<int>());
193244

194245
check(data | std::views::take_while([](auto) { return false; }), 1.23, std::plus(), 1.23);
195246
check(data, Integer(52), &Integer::plus, Integer(52));
247+
check(data | std::views::take_while([](auto) { return false; }), std::plus(), std::optional<int>());
196248
}
197249

198250
constexpr void common_range_test_case() {
199251
auto const data = std::vector<int>{1, 2, 3, 4};
200252
check(data, 0, std::plus(), triangular_sum(data));
201253
check(data, 1, std::multiplies(), factorial(data.back()));
254+
check(data, std::plus(), std::optional(triangular_sum(data)));
255+
check(data, std::multiplies(), std::optional(factorial(data.back())));
202256

203257
auto multiply_with_prev = [n = 1](auto const x, auto const y) mutable {
204258
auto const result = x * y * n;
205259
n = y;
206260
return static_cast<std::size_t>(result);
207261
};
208262
check(data, 1, multiply_with_prev, factorial(data.size()) * factorial(data.size() - 1));
263+
check(data, multiply_with_prev, std::optional(factorial(data.size()) * factorial(data.size() - 1)));
209264

210265
auto fib = [n = 1](auto x, auto) mutable {
211266
auto old_x = x;
@@ -237,6 +292,7 @@ constexpr void non_common_range_test_case() {
237292
auto data = std::vector<std::string>{"five", "three", "two", "six", "one", "four"};
238293
auto range = data | std::views::transform(parse);
239294
check(range, 0, std::plus(), triangular_sum(range));
295+
check(range, std::plus(), std::optional(triangular_sum(range)));
240296
}
241297

242298
{
@@ -248,6 +304,7 @@ constexpr void non_common_range_test_case() {
248304
auto range =
249305
std::views::lazy_split(data, ' ') | std::views::transform(to_string_view) | std::views::transform(parse);
250306
check(range, 0, std::plus(), triangular_sum(range));
307+
check(range, std::plus(), std::optional(triangular_sum(range)));
251308
}
252309
}
253310

0 commit comments

Comments
 (0)