Skip to content

Commit 303382f

Browse files
authored
Merge pull request #262 from elbeno/terse-repeat-until
🎨 Add terse forms of `*_until`
2 parents 0e57c9e + db74097 commit 303382f

7 files changed

Lines changed: 162 additions & 11 deletions

File tree

docs/sender_adaptors.adoc

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,18 @@ NOTE: `periodic` never completes other than by error or cancellation, but
296296
`periodic_n` and `periodic_until` both complete successfully with the same
297297
completion as the adapted sender.
298298

299+
`periodic_until` can take a plain value rather than a predicate, and this is a
300+
terse way to repeat until the argument is equal to that value.
301+
302+
[source,cpp]
303+
----
304+
// these senders are equivalent
305+
auto s1 = some_sender | async::periodic_until(1s, [] (auto const&x) { return x == 42; });
306+
auto s2 = some_sender | async::periodic_until(1s, 42);
307+
----
308+
309+
NOTE: A "plain value" given to `periodic_until` must be movable.
310+
299311
=== `repeat`
300312

301313
Found in the header: `async/repeat.hpp`
@@ -375,6 +387,18 @@ NOTE: `repeat` never completes other than by error or cancellation, but
375387
`repeat_n` and `repeat_until` both complete successfully with the same
376388
completion as the adapted sender.
377389

390+
`repeat_until` can take a plain value rather than a predicate, and this is a
391+
terse way to repeat until the argument is equal to that value.
392+
393+
[source,cpp]
394+
----
395+
// these senders are equivalent
396+
auto s1 = some_sender | async::repeat_until([] (auto const&x) { return x == 42; });
397+
auto s2 = some_sender | async::repeat_until(42);
398+
----
399+
400+
NOTE: A "plain value" given to `repeat_until` must be movable.
401+
378402
=== `retry`
379403

380404
Found in the header: `async/retry.hpp`
@@ -409,6 +433,19 @@ auto s = some_sender | async::retry_until([] (auto&&) { return true; });
409433
NOTE: The arguments passed to the predicate are those in the error completion(s)
410434
of the sender.
411435

436+
Like `repeat_until`, `retry_until` can take a plain value rather than a
437+
predicate, and this is a terse way to retry until the (error channel) argument
438+
is equal to that value.
439+
440+
[source,cpp]
441+
----
442+
// these senders are equivalent
443+
auto s1 = some_sender | async::retry_until([] (auto const&x) { return x == 42; });
444+
auto s2 = some_sender | async::retry_until(42);
445+
----
446+
447+
NOTE: A "plain value" given to `retry_until` must be movable.
448+
412449
=== `sequence`
413450

414451
Found in the header: `async/sequence.hpp`

include/async/periodic.hpp

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -238,18 +238,43 @@ struct sender {
238238
}
239239
};
240240

241+
template <stdx::ct_string, typename, typename, typename> struct pipeable;
242+
243+
template <stdx::ct_string Name, typename Expiry, typename... Args>
244+
constexpr auto make_sender(Args &&...args)
245+
-> sender<Name, Expiry, std::remove_cvref_t<Args>...> {
246+
return {std::forward<Args>(args)...};
247+
}
248+
241249
template <stdx::ct_string Name, typename Expiry, typename Duration,
242-
stdx::callable Pred>
250+
typename MatchValue>
243251
struct pipeable {
244252
Duration d;
245-
Pred p;
253+
MatchValue v;
246254

247255
private:
248256
template <async::sender S, stdx::same_as_unqualified<pipeable> Self>
249257
friend constexpr auto operator|(S &&s, Self &&self) -> async::sender auto {
250-
return sender<Name, Expiry, std::remove_cvref_t<S>, Duration, Pred>{
258+
return make_sender<Name, Expiry>(
251259
std::forward<S>(s), std::forward<Self>(self).d,
252-
std::forward<Self>(self).p};
260+
[value = std::forward<Self>(self).v](auto const &x) {
261+
return x == value;
262+
});
263+
}
264+
};
265+
266+
template <stdx::ct_string Name, typename Expiry, typename Duration,
267+
stdx::callable Pred>
268+
struct pipeable<Name, Expiry, Duration, Pred> {
269+
Duration d;
270+
Pred p;
271+
272+
private:
273+
template <async::sender S, stdx::same_as_unqualified<pipeable> Self>
274+
friend constexpr auto operator|(S &&s, Self &&self) -> async::sender auto {
275+
return make_sender<Name, Expiry>(std::forward<S>(s),
276+
std::forward<Self>(self).d,
277+
std::forward<Self>(self).p);
253278
}
254279
};
255280
} // namespace _periodic

include/async/repeat.hpp

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,23 +190,48 @@ struct sender {
190190
}
191191
};
192192

193-
template <stdx::ct_string Name, stdx::callable Pred, stdx::callable LoopFn>
193+
template <stdx::ct_string, typename, stdx::callable> struct pipeable;
194+
195+
template <stdx::ct_string Name, typename... Args>
196+
constexpr auto make_sender(Args &&...args)
197+
-> sender<Name, std::remove_cvref_t<Args>...> {
198+
return {std::forward<Args>(args)...};
199+
}
200+
201+
template <stdx::ct_string Name, typename MatchValue, stdx::callable LoopFn>
194202
struct pipeable {
203+
MatchValue v;
204+
LoopFn f;
205+
206+
private:
207+
template <async::sender S, stdx::same_as_unqualified<pipeable> Self>
208+
friend constexpr auto operator|(S &&s, Self &&self) -> async::sender auto {
209+
return make_sender<Name>(
210+
std::forward<S>(s),
211+
[value = std::forward<Self>(self).v](auto const &x) {
212+
return x == value;
213+
},
214+
std::forward<Self>(self).f);
215+
}
216+
};
217+
218+
template <stdx::ct_string Name, stdx::callable Pred, stdx::callable LoopFn>
219+
struct pipeable<Name, Pred, LoopFn> {
195220
Pred p;
196221
LoopFn f;
197222

198223
private:
199224
template <async::sender S, stdx::same_as_unqualified<pipeable> Self>
200225
friend constexpr auto operator|(S &&s, Self &&self) -> async::sender auto {
201-
return sender<Name, std::remove_cvref_t<S>, Pred, LoopFn>{
202-
std::forward<S>(s), std::forward<Self>(self).p,
203-
std::forward<Self>(self).f};
226+
return make_sender<Name>(std::forward<S>(s), std::forward<Self>(self).p,
227+
std::forward<Self>(self).f);
204228
}
205229
};
206230
} // namespace _repeat
207231

208232
template <stdx::ct_string Name = "repeat_until", typename P,
209233
typename F = std::remove_cvref_t<decltype(_repeat::no_loop_fn)>>
234+
requires(not sender<P>)
210235
[[nodiscard]] constexpr auto repeat_until(P &&p, F &&f = {})
211236
-> _repeat::pipeable<Name, std::remove_cvref_t<P>, std::remove_cvref_t<F>> {
212237
return {std::forward<P>(p), std::forward<F>(f)};

include/async/retry.hpp

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,14 +180,35 @@ template <stdx::ct_string Name, typename Sndr, typename Pred> struct sender {
180180
}
181181
};
182182

183-
template <stdx::ct_string Name, typename Pred> struct pipeable {
183+
template <stdx::ct_string, typename> struct pipeable;
184+
185+
template <stdx::ct_string Name, typename... Args>
186+
constexpr auto make_sender(Args &&...args)
187+
-> sender<Name, std::remove_cvref_t<Args>...> {
188+
return {std::forward<Args>(args)...};
189+
}
190+
191+
template <stdx::ct_string Name, typename MatchValue> struct pipeable {
192+
MatchValue v;
193+
194+
private:
195+
template <async::sender S, stdx::same_as_unqualified<pipeable> Self>
196+
friend constexpr auto operator|(S &&s, Self &&self) -> async::sender auto {
197+
return make_sender<Name>(std::forward<S>(s),
198+
[value = std::forward<Self>(self).v](
199+
auto const &x) { return x == value; });
200+
}
201+
};
202+
203+
template <stdx::ct_string Name, stdx::callable Pred>
204+
struct pipeable<Name, Pred> {
184205
Pred p;
185206

186207
private:
187208
template <async::sender S, stdx::same_as_unqualified<pipeable> Self>
188209
friend constexpr auto operator|(S &&s, Self &&self) -> async::sender auto {
189-
return sender<Name, std::remove_cvref_t<S>, Pred>{
190-
std::forward<S>(s), std::forward<Self>(self).p};
210+
return make_sender<Name>(std::forward<S>(s),
211+
std::forward<Self>(self).p);
191212
}
192213
};
193214
} // namespace _retry

test/periodic.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,23 @@ TEST_CASE("periodic repeats periodically", "[periodic]") {
132132
CHECK(var == 42);
133133
}
134134

135+
TEST_CASE("periodic_until terse form", "[periodic]") {
136+
int var{};
137+
[[maybe_unused]] auto s = async::time_scheduler{}.schedule() |
138+
async::then([&] { return ++var; }) |
139+
async::periodic_until(1s, 2);
140+
auto op = async::connect(s, receiver{[&](auto) { var = 42; }});
141+
async::start(op);
142+
CHECK(enabled<default_domain>);
143+
CHECK(not async::timer_mgr::is_idle());
144+
async::timer_mgr::service_task();
145+
CHECK(var == 1);
146+
CHECK(not async::timer_mgr::is_idle());
147+
async::timer_mgr::service_task();
148+
CHECK(async::timer_mgr::is_idle());
149+
CHECK(var == 42);
150+
}
151+
135152
TEST_CASE("periodic allows continue_on another scheduler", "[periodic]") {
136153
int var{};
137154
[[maybe_unused]] auto s =

test/repeat.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,19 @@ TEST_CASE("repeat_until is pipeable", "[repeat]") {
130130
CHECK(var == 43);
131131
}
132132

133+
TEST_CASE("repeat_until terse form", "[repeat]") {
134+
int var{};
135+
136+
auto sub = async::just() | async::sequence([&] {
137+
++var;
138+
return async::just(42);
139+
});
140+
auto s = sub | async::repeat_until(42);
141+
auto op = async::connect(s, receiver{[&](auto i) { var += i; }});
142+
async::start(op);
143+
CHECK(var == 43);
144+
}
145+
133146
TEST_CASE("repeat can be cancelled", "[repeat]") {
134147
int var{};
135148
stoppable_receiver r{[&] { var += 42; }};

test/retry.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,19 @@ TEST_CASE("retry_until retries on error", "[retry]") {
103103
CHECK(var == 3);
104104
}
105105

106+
TEST_CASE("retry_until terse form", "[retry]") {
107+
int var{};
108+
109+
auto sub = async::just() | async::sequence([&] {
110+
++var;
111+
return async::just_error(var);
112+
});
113+
auto s = sub | async::retry_until(3);
114+
auto op = async::connect(s, receiver{[] {}});
115+
async::start(op);
116+
CHECK(var == 3);
117+
}
118+
106119
TEST_CASE("retry can be cancelled", "[retry]") {
107120
int var{};
108121
stoppable_receiver r{[&] { var += 42; }};

0 commit comments

Comments
 (0)