Skip to content

Commit f9546e1

Browse files
committed
basic: add Sender, Receives and Operation concepts
1 parent d5f9f08 commit f9546e1

File tree

6 files changed

+204
-22
lines changed

6 files changed

+204
-22
lines changed

docs/src/SUMMARY.md

+3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
- [detached](headers/basic/detached.md)
2323
- [spawn](headers/basic/spawn.md)
2424
- [Waitable](headers/basic/waitable.md)
25+
- [Sender](headers/basic/sender.md)
26+
- [Operation](headers/basic/operation.md)
27+
- [Receives<T>](headers/basic/receives.md)
2528
- [async/result.hpp](headers/result.md)
2629
- [async/oneshot.hpp](headers/oneshot-event.md)
2730
- [async/wait-group.hpp](headers/wait-group.md)

docs/src/headers/basic/operation.md

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# `concept Operation`
2+
3+
The `Operation` concept holds all the requirements for an
4+
[operation](/sender-receiver.md).
5+
6+
## Prototype
7+
8+
```cpp
9+
template<typename T>
10+
concept Operation = ...;
11+
```
12+
13+
### Requirements
14+
15+
`T` can be started via the [start\_inline](/headers/execution.md) CPO.
16+
17+
## Examples
18+
19+
```cpp
20+
template <typename Receiver>
21+
struct write_operation {
22+
write_operation(write_sender s, Receiver r)
23+
: req_{}, handle_{s.handle}, bufs_{s.bufs}, nbufs_{s.nbufs}, r_{std::move(r)} { }
24+
25+
write_operation(const write_operation &) = delete;
26+
write_operation &operator=(const write_operation &) = delete;
27+
write_operation(write_operation &&) = delete;
28+
write_operation &operator=(write_operation &&) = delete;
29+
30+
bool start_inline() { /* omitted for brevity */ }
31+
32+
private:
33+
uv_write_t req_;
34+
uv_stream_t *handle_;
35+
const uv_buf_t *bufs_;
36+
size_t nbufs_;
37+
38+
Receiver r_;
39+
};
40+
static_assert(async::Operation<write_operation<noop_receiver>>);
41+
```

docs/src/headers/basic/receives.md

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# `concept Receives`
2+
3+
The `Receives<T>` concept holds all the requirements for a
4+
[receiver](/sender-receiver.md) that can receive a `T` value (or none, when `T`
5+
is `void`).
6+
7+
## Prototype
8+
9+
```cpp
10+
template<typename T, typename Receives>
11+
concept Receives = ...;
12+
```
13+
14+
### Requirements
15+
16+
A `set_value_inline` and `set_value_noinline` members, which can be called with
17+
a `T&&` value, or no parameters, if `T` is `void`.
18+
19+
## Examples
20+
21+
```cpp
22+
struct discard_receiver {
23+
template<typename T>
24+
void set_value_inline(T) {
25+
assert(std::is_constant_evaluated());
26+
}
27+
void set_value_inline() {
28+
assert(std::is_constant_evaluated());
29+
}
30+
31+
template<typename T>
32+
void set_value_noinline(T) {
33+
assert(std::is_constant_evaluated());
34+
}
35+
void set_value_noinline() {
36+
assert(std::is_constant_evaluated());
37+
}
38+
};
39+
static_assert(async::Receives<discard_receiver, void>);
40+
static_assert(async::Receives<discard_receiver, int>);
41+
static_assert(async::Receives<discard_receiver, std::string>);
42+
```

docs/src/headers/basic/sender.md

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# `concept Sender`
2+
3+
The `Sender` concept holds all the requirements for a
4+
[sender](/sender-receiver.md).
5+
6+
## Prototype
7+
8+
```cpp
9+
template<typename T>
10+
concept Sender = ...;
11+
```
12+
13+
### Requirements
14+
15+
`T` has a `value_type`, is move constructible, and can be
16+
[connected](/headers/execution.md).
17+
18+
## Examples
19+
20+
```cpp
21+
struct [[nodiscard]] write_sender {
22+
using value_type = int; // Status code
23+
24+
uv_stream_t *handle;
25+
const uv_buf_t *bufs;
26+
size_t nbufs;
27+
};
28+
29+
/* operation omitted for brevity */
30+
template <typename Receiver>
31+
/*operation*/<Receiver> connect(write_sender s, Receiver r) {
32+
return {s, std::move(r)};
33+
}
34+
static_assert(async::Sender<write_sender>);
35+
```

include/async/algorithm.hpp

+23-16
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
namespace async {
1212

13-
template<typename Sender, typename Receiver>
13+
template<Sender Sender, Receives<typename Sender::value_type> Receiver>
1414
struct connect_helper {
1515
using operation = execution::operation_t<Sender, Receiver>;
1616

@@ -22,7 +22,7 @@ struct connect_helper {
2222
Receiver r;
2323
};
2424

25-
template<typename Sender, typename Receiver>
25+
template<Sender Sender, Receives<typename Sender::value_type> Receiver>
2626
connect_helper<Sender, Receiver> make_connect_helper(Sender s, Receiver r) {
2727
return {std::move(s), std::move(r)};
2828
}
@@ -59,7 +59,7 @@ template<typename F>
5959
struct [[nodiscard]] invocable_sender {
6060
using value_type = std::invoke_result_t<F>;
6161

62-
template<typename R>
62+
template<Receives<value_type> R>
6363
invocable_operation<F, R> connect(R r) {
6464
return {std::move(f), std::move(r)};
6565
}
@@ -165,7 +165,7 @@ requires std::same_as<typename Sender::value_type, void>
165165
struct [[nodiscard]] transform_sender<Sender, F> {
166166
using value_type = std::invoke_result_t<F>;
167167

168-
template<typename Receiver>
168+
template<Receives<value_type> Receiver>
169169
friend auto connect(transform_sender s, Receiver dr) {
170170
return execution::connect(std::move(s.ds),
171171
void_transform_receiver<Receiver, F>{std::move(dr), std::move(s.f)});
@@ -234,7 +234,7 @@ struct [[nodiscard]] ite_sender {
234234
ite_sender(C cond, ST then_s, SE else_s)
235235
: cond_{std::move(cond)}, then_s_{std::move(then_s)}, else_s_{std::move(else_s)} { }
236236

237-
template<typename R>
237+
template<Receives<value_type> R>
238238
ite_operation<C, ST, SE, R> connect(R dr) {
239239
return {std::move(cond_), std::move(then_s_), std::move(else_s_), std::move(dr)};
240240
}
@@ -249,7 +249,8 @@ struct [[nodiscard]] ite_sender {
249249
SE else_s_;
250250
};
251251

252-
template<typename C, typename ST, typename SE>
252+
template<std::invocable<> C, Sender ST, Sender SE>
253+
requires std::same_as<typename ST::value_type, typename SE::value_type>
253254
ite_sender<C, ST, SE> ite(C cond, ST then_s, SE else_s) {
254255
return {std::move(cond), std::move(then_s), std::move(else_s)};
255256
}
@@ -326,7 +327,7 @@ struct repeat_while_sender {
326327
return {std::move(*this)};
327328
}
328329

329-
template<typename R>
330+
template<Receives<value_type> R>
330331
repeat_while_operation<C, SF, R> connect(R receiver) {
331332
return {std::move(cond), std::move(factory), std::move(receiver)};
332333
}
@@ -335,7 +336,10 @@ struct repeat_while_sender {
335336
SF factory;
336337
};
337338

338-
template<typename C, typename SF>
339+
template<typename C, Sender SF>
340+
requires std::movable<C> && requires (C c) {
341+
{ c() } -> std::convertible_to<bool>;
342+
}
339343
repeat_while_sender<C, SF> repeat_while(C cond, SF factory) {
340344
return {std::move(cond), std::move(factory)};
341345
}
@@ -351,7 +355,7 @@ template<typename... Functors>
351355
struct race_and_cancel_sender {
352356
using value_type = void;
353357

354-
template<typename Receiver>
358+
template<Receives<value_type> Receiver>
355359
friend race_and_cancel_operation<Receiver, frg::tuple<Functors...>,
356360
std::index_sequence_for<Functors...>>
357361
connect(race_and_cancel_sender s, Receiver r) {
@@ -450,7 +454,8 @@ operator co_await(race_and_cancel_sender<Functors...> s) {
450454
return {std::move(s)};
451455
}
452456

453-
template<typename... Functors>
457+
template<std::invocable<cancellation_token>... Functors>
458+
requires ((Sender<std::invoke_result_t<Functors, cancellation_token>>) && ...)
454459
race_and_cancel_sender<Functors...> race_and_cancel(Functors... fs) {
455460
return {{fs...}};
456461
}
@@ -495,7 +500,7 @@ struct [[nodiscard]] let_sender {
495500
using imm_type = std::invoke_result_t<Pred>;
496501
using value_type = typename std::invoke_result_t<Func, std::add_lvalue_reference_t<imm_type>>::value_type;
497502

498-
template<typename Receiver>
503+
template<Receives<value_type> Receiver>
499504
friend let_operation<Receiver, Pred, Func>
500505
connect(let_sender s, Receiver r) {
501506
return {std::move(s.pred), std::move(s.func), std::move(r)};
@@ -511,7 +516,8 @@ operator co_await(let_sender<Pred, Func> s) {
511516
return {std::move(s)};
512517
}
513518

514-
template <typename Pred, typename Func>
519+
template <std::invocable<> Pred, typename Func>
520+
requires std::invocable<Func, std::add_lvalue_reference_t<std::invoke_result_t<Pred>>>
515521
let_sender<Pred, Func> let(Pred pred, Func func) {
516522
return {std::move(pred), std::move(func)};
517523
}
@@ -689,7 +695,7 @@ template <typename ...Senders> requires (sizeof...(Senders) > 0)
689695
struct [[nodiscard]] sequence_sender {
690696
using value_type = typename std::tuple_element_t<sizeof...(Senders) - 1, frg::tuple<Senders...>>::value_type;
691697

692-
template<typename Receiver>
698+
template<Receives<value_type> Receiver>
693699
friend sequence_operation<Receiver, Senders...>
694700
connect(sequence_sender s, Receiver r) {
695701
return {std::move(s.senders), std::move(r)};
@@ -703,7 +709,7 @@ sequence_sender<Senders...> sequence(Senders ...senders) {
703709
return {frg::tuple<Senders...>{std::move(senders)...}};
704710
}
705711

706-
template <typename ...Senders>
712+
template <Sender ...Senders>
707713
sender_awaiter<sequence_sender<Senders...>, typename sequence_sender<Senders...>::value_type>
708714
operator co_await(sequence_sender<Senders...> s) {
709715
return {std::move(s)};
@@ -778,7 +784,7 @@ template <typename ...Senders> requires (sizeof...(Senders) > 0)
778784
struct [[nodiscard]] when_all_sender {
779785
using value_type = void;
780786

781-
template<typename Receiver>
787+
template<Receives<value_type> Receiver>
782788
friend when_all_operation<Receiver, Senders...>
783789
connect(when_all_sender s, Receiver r) {
784790
return {std::move(s.senders), std::move(r)};
@@ -787,7 +793,8 @@ struct [[nodiscard]] when_all_sender {
787793
frg::tuple<Senders...> senders;
788794
};
789795

790-
template <typename ...Senders> requires (sizeof...(Senders) > 0)
796+
template <Sender ...Senders>
797+
requires (sizeof...(Senders) > 0)
791798
when_all_sender<Senders...> when_all(Senders ...senders) {
792799
return {frg::tuple<Senders...>{std::move(senders)...}};
793800
}

0 commit comments

Comments
 (0)