Skip to content

Commit 8f61d82

Browse files
committed
Make scan_first() work with iterables
1 parent b14c5f1 commit 8f61d82

File tree

2 files changed

+136
-74
lines changed

2 files changed

+136
-74
lines changed

include/flux/adaptor/scan_first.hpp

Lines changed: 116 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ namespace flux {
1515

1616
namespace detail {
1717

18+
template <typename, typename, typename>
19+
struct scan_first_iterable_traits;
20+
21+
template <typename, typename, typename>
22+
struct scan_first_sequence_traits;
23+
1824
template <typename Base, typename Func, typename R>
1925
struct scan_first_adaptor : inline_sequence_base<scan_first_adaptor<Base, Func, R>> {
2026
private:
@@ -28,104 +34,140 @@ struct scan_first_adaptor : inline_sequence_base<scan_first_adaptor<Base, Func,
2834
func_(std::move(func))
2935
{}
3036

31-
struct flux_sequence_traits : default_sequence_traits {
32-
private:
33-
struct cursor_type {
34-
cursor_type(cursor_type&&) = default;
35-
cursor_type& operator=(cursor_type&&) = default;
37+
friend struct scan_first_iterable_traits<Base, Func, R>;
38+
friend struct scan_first_sequence_traits<Base, Func, R>;
39+
};
3640

37-
private:
38-
friend struct flux_sequence_traits;
41+
template <typename Base, typename Func, typename R>
42+
struct scan_first_iterable_traits : default_sequence_traits {
43+
private:
44+
using self_t = scan_first_adaptor<Base, Func, R>;
3945

40-
constexpr explicit cursor_type(cursor_t<Base>&& base_cur)
41-
: base_cur(std::move(base_cur))
42-
{}
46+
public:
47+
static consteval auto element_type(self_t&) -> R const&;
4348

44-
cursor_t<Base> base_cur;
45-
};
49+
static constexpr auto iterate(self_t& self, auto&& pred) -> bool
50+
{
51+
return flux::iterate(self.base_, [&](auto&& elem) {
52+
if (self.accum_.has_value()) {
53+
self.accum_.emplace(
54+
std::invoke(self.func_,
55+
std::move(self.accum_.value_unchecked()),
56+
FLUX_FWD(elem)));
57+
} else {
58+
self.accum_.emplace(FLUX_FWD(elem));
59+
}
60+
return std::invoke(pred, self.accum_.value_unchecked());
61+
});
62+
}
4663

47-
using self_t = scan_first_adaptor;
64+
static constexpr auto size(self_t& self) -> distance_t
65+
requires sized_iterable<Base>
66+
{
67+
return flux::size(self.base_);
68+
}
69+
};
4870

49-
public:
50-
static constexpr auto first(self_t& self) -> cursor_type
51-
{
52-
auto cur = flux::first(self.base_);
53-
if (!flux::is_last(self.base_, cur)) {
54-
self.accum_.emplace(flux::read_at(self.base_, cur));
55-
}
56-
return cursor_type(std::move(cur));
57-
}
71+
template <typename Base, typename Func, typename R>
72+
struct scan_first_sequence_traits : scan_first_iterable_traits<Base, Func, R> {
73+
private:
74+
struct cursor_type {
75+
cursor_type(cursor_type&&) = default;
76+
cursor_type& operator=(cursor_type&&) = default;
5877

59-
static constexpr auto is_last(self_t& self, cursor_type const& cur) -> bool
60-
{
61-
return flux::is_last(self.base_, cur.base_cur);
62-
}
78+
private:
79+
friend struct scan_first_sequence_traits;
6380

64-
static constexpr auto inc(self_t& self, cursor_type& cur) -> void
65-
{
66-
flux::inc(self.base_, cur.base_cur);
67-
if (!flux::is_last(self.base_, cur.base_cur)) {
68-
self.accum_.emplace(
69-
std::invoke(self.func_,
70-
std::move(self.accum_.value_unchecked()),
71-
flux::read_at(self.base_, cur.base_cur)));
72-
}
73-
}
81+
constexpr explicit cursor_type(cursor_t<Base>&& base_cur)
82+
: base_cur(std::move(base_cur))
83+
{}
7484

75-
static constexpr auto read_at(self_t& self, cursor_type const&) -> R const&
76-
{
77-
return self.accum_.value();
78-
}
85+
cursor_t<Base> base_cur;
86+
};
7987

80-
static constexpr auto read_at_unchecked(self_t& self, cursor_type const&)
81-
-> R const&
82-
{
83-
return self.accum_.value_unchecked();
84-
}
88+
using self_t = scan_first_adaptor<Base, Func, R>;
8589

86-
static constexpr auto last(self_t& self) -> cursor_type
87-
requires bounded_sequence<Base>
88-
{
89-
return cursor_type(flux::last(self.base_));
90+
public:
91+
static constexpr auto first(self_t& self) -> cursor_type
92+
{
93+
auto cur = flux::first(self.base_);
94+
if (!flux::is_last(self.base_, cur)) {
95+
self.accum_.emplace(flux::read_at(self.base_, cur));
9096
}
97+
return cursor_type(std::move(cur));
98+
}
9199

92-
static constexpr auto size(self_t& self) -> distance_t
93-
requires sized_iterable<Base>
94-
{
95-
return flux::size(self.base_);
96-
}
100+
static constexpr auto is_last(self_t& self, cursor_type const& cur) -> bool
101+
{
102+
return flux::is_last(self.base_, cur.base_cur);
103+
}
97104

98-
static constexpr auto for_each_while(self_t& self, auto&& pred) -> cursor_type
99-
{
100-
return cursor_type(flux::for_each_while(self.base_, [&](auto&& elem) {
101-
if (self.accum_.has_value()) {
102-
self.accum_.emplace(
103-
std::invoke(self.func_,
104-
std::move(self.accum_.value_unchecked()),
105-
FLUX_FWD(elem)));
106-
} else {
107-
self.accum_.emplace(FLUX_FWD(elem));
108-
}
109-
return std::invoke(pred, self.accum_.value_unchecked());
110-
}));
105+
static constexpr auto inc(self_t& self, cursor_type& cur) -> void
106+
{
107+
flux::inc(self.base_, cur.base_cur);
108+
if (!flux::is_last(self.base_, cur.base_cur)) {
109+
self.accum_.emplace(
110+
std::invoke(self.func_,
111+
std::move(self.accum_.value_unchecked()),
112+
flux::read_at(self.base_, cur.base_cur)));
111113
}
112-
};
114+
}
115+
116+
static constexpr auto read_at(self_t& self, cursor_type const&) -> R const&
117+
{
118+
return self.accum_.value();
119+
}
120+
121+
static constexpr auto read_at_unchecked(self_t& self, cursor_type const&)
122+
-> R const&
123+
{
124+
return self.accum_.value_unchecked();
125+
}
126+
127+
static constexpr auto last(self_t& self) -> cursor_type
128+
requires bounded_sequence<Base>
129+
{
130+
return cursor_type(flux::last(self.base_));
131+
}
132+
133+
static constexpr auto for_each_while(self_t& self, auto&& pred) -> cursor_type
134+
{
135+
return cursor_type(flux::for_each_while(self.base_, [&](auto&& elem) {
136+
if (self.accum_.has_value()) {
137+
self.accum_.emplace(
138+
std::invoke(self.func_,
139+
std::move(self.accum_.value_unchecked()),
140+
FLUX_FWD(elem)));
141+
} else {
142+
self.accum_.emplace(FLUX_FWD(elem));
143+
}
144+
return std::invoke(pred, self.accum_.value_unchecked());
145+
}));
146+
}
113147
};
114148

115149
struct scan_first_fn {
116-
template <adaptable_sequence Seq, typename Func>
117-
requires foldable<Seq, Func, element_t<Seq>>
150+
template <sink_iterable It, typename Func>
151+
requires foldable<It, Func, element_t<It>>
118152
[[nodiscard]]
119-
constexpr auto operator()(Seq&& seq, Func func) const -> sequence auto
153+
constexpr auto operator()(It&& it, Func func) const -> iterable auto
120154
{
121-
using R = fold_result_t<Seq, Func, element_t<Seq>>;
122-
return scan_first_adaptor<std::decay_t<Seq>, Func, R>(
123-
FLUX_FWD(seq), std::move(func));
155+
using R = fold_result_t<It, Func, element_t<It>>;
156+
return scan_first_adaptor<std::decay_t<It>, Func, R>(
157+
FLUX_FWD(it), std::move(func));
124158
}
125159
};
126160

127161
} // namespace detail
128162

163+
template <iterable Base, typename Func, typename R>
164+
struct sequence_traits<detail::scan_first_adaptor<Base, Func, R>>
165+
: detail::scan_first_iterable_traits<Base, Func, R> {};
166+
167+
template <sequence Base, typename Func, typename R>
168+
struct sequence_traits<detail::scan_first_adaptor<Base, Func, R>>
169+
: detail::scan_first_sequence_traits<Base, Func, R> {};
170+
129171
FLUX_EXPORT inline constexpr auto scan_first = detail::scan_first_fn{};
130172

131173
template <typename Derived>

test/test_scan.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,26 @@ constexpr bool test_scan_first()
239239
STATIC_CHECK(seq[seq.inc(cur)] == 15);
240240
STATIC_CHECK(seq.is_last(seq.inc(cur)));
241241
}
242+
243+
// scan_first works correctly on iterables
244+
{
245+
auto scanner = []{ return flux::scan_first(
246+
std::views::transform(std::array{1, 2, 3, 4, 5}, std::identity{}),
247+
flux::num::add); };
248+
249+
using S = decltype(scanner());
250+
251+
static_assert(flux::iterable<S>);
252+
static_assert(flux::sized_iterable<S>);
253+
static_assert(not flux::sequence<S>);
254+
static_assert(std::same_as<flux::element_t<S>, int const&>);
255+
256+
STATIC_CHECK(flux::size(scanner()) == 5);
257+
STATIC_CHECK(check_equal(scanner(), {1, 3, 6, 10, 15}));
258+
STATIC_CHECK(flux::contains(scanner(), 10) == true);
259+
STATIC_CHECK(flux::contains(scanner(), 99) == false);
260+
}
261+
242262
return true;
243263
}
244264
static_assert(test_scan_first());

0 commit comments

Comments
 (0)