Skip to content

Commit 0db66f7

Browse files
authored
Merge pull request #264 from elbeno/multi-then
✨ Add multi-channel `then`
2 parents 4c221e9 + e5e3b70 commit 0db66f7

3 files changed

Lines changed: 144 additions & 52 deletions

File tree

include/async/then.hpp

Lines changed: 91 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
namespace async {
2828
namespace _then {
29-
template <stdx::ct_string Name, typename HandleTag, typename CompleteTag,
29+
template <stdx::ct_string Name, typename HandleTags, typename CompleteTag,
3030
typename S, typename R, typename... Fs>
3131
struct receiver {
3232
using is_receiver = void;
@@ -53,7 +53,7 @@ struct receiver {
5353
private:
5454
template <typename T, typename... Args>
5555
auto handle(Args &&...args) -> void {
56-
if constexpr (std::same_as<HandleTag, T>) {
56+
if constexpr (boost::mp11::mp_contains<HandleTags, T>::value) {
5757
auto results = stdx::call_by_need(
5858
std::move(fs),
5959
stdx::tuple<Args &&...>{std::forward<Args>(args)...});
@@ -76,15 +76,15 @@ template <typename Tag> struct as_signature {
7676
};
7777
} // namespace detail
7878

79-
template <stdx::ct_string Name, typename HandleTag, typename CompleteTag,
79+
template <stdx::ct_string Name, typename HandleTags, typename CompleteTag,
8080
typename S, typename... Fs>
8181
struct sender {
8282
template <async::receiver R>
8383
[[nodiscard]] constexpr auto connect(R &&r) && {
8484
check_connect<sender &&, R>();
8585
return async::connect(
8686
std::move(s),
87-
receiver<Name, HandleTag, CompleteTag, S, std::remove_cvref_t<R>,
87+
receiver<Name, HandleTags, CompleteTag, S, std::remove_cvref_t<R>,
8888
Fs...>{std::forward<R>(r), std::move(fs)});
8989
}
9090

@@ -95,8 +95,8 @@ struct sender {
9595
[[nodiscard]] constexpr auto connect(R &&r) const & {
9696
check_connect<sender const &, R>();
9797
return async::connect(
98-
s, receiver<Name, HandleTag, CompleteTag, S, std::remove_cvref_t<R>,
99-
Fs...>{std::forward<R>(r), fs});
98+
s, receiver<Name, HandleTags, CompleteTag, S,
99+
std::remove_cvref_t<R>, Fs...>{std::forward<R>(r), fs});
100100
}
101101

102102
template <typename... Ts>
@@ -107,31 +107,23 @@ struct sender {
107107
std::declval<stdx::tuple<Ts...>>()))>;
108108

109109
template <typename Env>
110-
requires std::same_as<HandleTag, set_value_t>
111110
[[nodiscard]] constexpr static auto get_completion_signatures(Env const &) {
112-
return transform_completion_signatures_of<
113-
S, Env, completion_signatures<>, signatures>{};
114-
}
115-
116-
template <typename Env>
117-
requires std::same_as<HandleTag, set_error_t>
118-
[[nodiscard]] constexpr static auto get_completion_signatures(Env const &) {
119-
return transform_completion_signatures_of<
120-
S, Env, completion_signatures<>, ::async::detail::default_set_value,
121-
signatures>{};
122-
}
123-
124-
template <typename Env>
125-
requires std::same_as<HandleTag, set_stopped_t>
126-
[[nodiscard]] constexpr static auto get_completion_signatures(Env const &) {
127-
if constexpr (not sends_stopped<S, Env>) {
128-
return completion_signatures_of_t<S, Env>{};
129-
} else {
130-
return transform_completion_signatures_of<
131-
S, Env, completion_signatures<signatures<>>,
132-
::async::detail::default_set_value,
133-
::async::detail::default_set_error, completion_signatures<>>{};
134-
}
111+
using tag_pred =
112+
boost::mp11::mp_apply<::async::detail::with_any_tag, HandleTags>;
113+
using raw_completions =
114+
boost::mp11::mp_partition_q<completion_signatures_of_t<S, Env>,
115+
tag_pred>;
116+
117+
using upstream_completions = boost::mp11::mp_first<raw_completions>;
118+
using dependent_completions =
119+
boost::mp11::mp_flatten<::async::detail::gather_signatures<
120+
tag_pred, upstream_completions, signatures,
121+
completion_signatures>>;
122+
123+
using unchanged_completions = boost::mp11::mp_second<raw_completions>;
124+
125+
return boost::mp11::mp_unique<boost::mp11::mp_append<
126+
dependent_completions, unchanged_completions>>{};
135127
}
136128

137129
using is_sender = void;
@@ -144,15 +136,15 @@ struct sender {
144136
}
145137
};
146138

147-
template <stdx::ct_string Name, typename HandleTag, typename CompleteTag,
139+
template <stdx::ct_string Name, typename HandleTags, typename CompleteTag,
148140
typename... Fs>
149141
struct pipeable {
150142
[[no_unique_address]] stdx::tuple<Fs...> fs;
151143

152144
private:
153145
template <async::sender S, stdx::same_as_unqualified<pipeable> Self>
154146
friend constexpr auto operator|(S &&s, Self &&self) -> async::sender auto {
155-
return sender<Name, HandleTag, CompleteTag, std::remove_cvref_t<S>,
147+
return sender<Name, HandleTags, CompleteTag, std::remove_cvref_t<S>,
156148
Fs...>{std::forward<S>(s), std::forward<Self>(self).fs};
157149
}
158150
};
@@ -161,7 +153,7 @@ struct pipeable {
161153
template <stdx::ct_string Name = "then", stdx::callable... Fs>
162154
[[nodiscard]] constexpr auto then(Fs &&...fs) {
163155
return compose(
164-
_then::pipeable<Name, set_value_t, set_value_t,
156+
_then::pipeable<Name, stdx::type_list<set_value_t>, set_value_t,
165157
std::remove_cvref_t<Fs>...>{std::forward<Fs>(fs)...});
166158
}
167159

@@ -173,8 +165,8 @@ template <stdx::ct_string Name = "then", sender S, stdx::callable... Fs>
173165
template <stdx::ct_string Name = "upon_error", stdx::callable F>
174166
[[nodiscard]] constexpr auto upon_error(F &&f) {
175167
return compose(
176-
_then::pipeable<Name, set_error_t, set_value_t, std::remove_cvref_t<F>>{
177-
std::forward<F>(f)});
168+
_then::pipeable<Name, stdx::type_list<set_error_t>, set_value_t,
169+
std::remove_cvref_t<F>>{std::forward<F>(f)});
178170
}
179171

180172
template <stdx::ct_string Name = "upon_error", sender S, stdx::callable F>
@@ -184,8 +176,9 @@ template <stdx::ct_string Name = "upon_error", sender S, stdx::callable F>
184176

185177
template <stdx::ct_string Name = "upon_stopped", stdx::callable F>
186178
[[nodiscard]] constexpr auto upon_stopped(F &&f) {
187-
return compose(_then::pipeable<Name, set_stopped_t, set_value_t,
188-
std::remove_cvref_t<F>>{std::forward<F>(f)});
179+
return compose(
180+
_then::pipeable<Name, stdx::type_list<set_stopped_t>, set_value_t,
181+
std::remove_cvref_t<F>>{std::forward<F>(f)});
189182
}
190183

191184
template <stdx::ct_string Name = "upon_stopped", sender S, stdx::callable F>
@@ -196,7 +189,7 @@ template <stdx::ct_string Name = "upon_stopped", sender S, stdx::callable F>
196189
template <stdx::ct_string Name = "then_error", stdx::callable... Fs>
197190
[[nodiscard]] constexpr auto then_error(Fs &&...fs) {
198191
return compose(
199-
_then::pipeable<Name, set_value_t, set_error_t,
192+
_then::pipeable<Name, stdx::type_list<set_value_t>, set_error_t,
200193
std::remove_cvref_t<Fs>...>{std::forward<Fs>(fs)...});
201194
}
202195

@@ -208,7 +201,7 @@ template <stdx::ct_string Name = "then_error", sender S, stdx::callable... Fs>
208201
template <stdx::ct_string Name = "transform_error", stdx::callable... Fs>
209202
[[nodiscard]] constexpr auto transform_error(Fs &&...fs) {
210203
return compose(
211-
_then::pipeable<Name, set_error_t, set_error_t,
204+
_then::pipeable<Name, stdx::type_list<set_error_t>, set_error_t,
212205
std::remove_cvref_t<Fs>...>{std::forward<Fs>(fs)...});
213206
}
214207

@@ -218,46 +211,93 @@ template <stdx::ct_string Name = "transform_error", sender S,
218211
return std::forward<S>(s) | transform_error<Name>(std::forward<Fs>(fs)...);
219212
}
220213

214+
template <
215+
auto Channels = stdx::type_list<set_value_t, set_error_t, set_stopped_t>{},
216+
stdx::ct_string Name = "multithen", stdx::callable... Fs>
217+
[[nodiscard]] constexpr auto multithen(Fs &&...fs) {
218+
return compose(
219+
_then::pipeable<Name, std::remove_cvref_t<decltype(Channels)>,
220+
set_value_t, std::remove_cvref_t<Fs>...>{
221+
std::forward<Fs>(fs)...});
222+
}
223+
224+
template <
225+
auto Channels = stdx::type_list<set_value_t, set_error_t, set_stopped_t>{},
226+
stdx::ct_string Name = "multithen", sender S, stdx::callable... Fs>
227+
[[nodiscard]] constexpr auto multithen(S &&s, Fs &&...fs) -> sender auto {
228+
return std::forward<S>(s) |
229+
multithen<Channels, Name>(std::forward<Fs>(fs)...);
230+
}
231+
232+
template <channel_tag T, channel_tag U>
233+
[[nodiscard]] consteval auto operator|(T, U)
234+
-> boost::mp11::mp_unique<stdx::type_list<T, U>> {
235+
return {};
236+
}
237+
238+
template <channel_tag T, channel_tag... Us>
239+
[[nodiscard]] consteval auto operator|(T, stdx::type_list<Us...>)
240+
-> boost::mp11::mp_unique<stdx::type_list<T, Us...>> {
241+
return {};
242+
}
243+
244+
template <channel_tag T, channel_tag... Us>
245+
[[nodiscard]] consteval auto operator|(stdx::type_list<Us...>, T)
246+
-> boost::mp11::mp_unique<stdx::type_list<T, Us...>> {
247+
return {};
248+
}
249+
250+
template <channel_tag... Ts, channel_tag... Us>
251+
[[nodiscard]] consteval auto operator|(stdx::type_list<Ts...>,
252+
stdx::type_list<Us...>)
253+
-> boost::mp11::mp_unique<stdx::type_list<Ts..., Us...>> {
254+
return {};
255+
}
256+
221257
struct then_t;
222258
struct upon_error_t;
223259
struct upon_stopped_t;
224260

225261
struct then_error_t;
226262
struct transform_error_t;
227263

264+
struct multithen_t;
265+
228266
namespace _then {
229267
namespace detail {
230-
template <typename HandleTag, typename CompleteTag> struct debug_tag;
268+
template <typename HandleTags, typename CompleteTag> struct debug_tag {
269+
using type = multithen_t;
270+
};
231271

232-
template <> struct debug_tag<set_value_t, set_value_t> {
272+
template <> struct debug_tag<stdx::type_list<set_value_t>, set_value_t> {
233273
using type = then_t;
234274
};
235-
template <> struct debug_tag<set_error_t, set_value_t> {
275+
template <> struct debug_tag<stdx::type_list<set_error_t>, set_value_t> {
236276
using type = upon_error_t;
237277
};
238-
template <> struct debug_tag<set_stopped_t, set_value_t> {
278+
template <> struct debug_tag<stdx::type_list<set_stopped_t>, set_value_t> {
239279
using type = upon_stopped_t;
240280
};
241281

242-
template <> struct debug_tag<set_value_t, set_error_t> {
282+
template <> struct debug_tag<stdx::type_list<set_value_t>, set_error_t> {
243283
using type = then_error_t;
244284
};
245-
template <> struct debug_tag<set_error_t, set_error_t> {
285+
template <> struct debug_tag<stdx::type_list<set_error_t>, set_error_t> {
246286
using type = transform_error_t;
247287
};
248288

249-
template <typename HandleTag, typename CompleteTag>
250-
using debug_tag_t = typename debug_tag<HandleTag, CompleteTag>::type;
289+
template <typename HandleTags, typename CompleteTag>
290+
using debug_tag_t = typename debug_tag<HandleTags, CompleteTag>::type;
251291
} // namespace detail
252292
} // namespace _then
253293

254-
template <stdx::ct_string Name, typename HandleTag, typename CompleteTag,
294+
template <stdx::ct_string Name, typename HandleTags, typename CompleteTag,
255295
typename... Ts>
256296
struct debug::context_for<
257-
_then::receiver<Name, HandleTag, CompleteTag, Ts...>> {
258-
using tag = _then::detail::debug_tag_t<HandleTag, CompleteTag>;
297+
_then::receiver<Name, HandleTags, CompleteTag, Ts...>> {
298+
using tag = _then::detail::debug_tag_t<HandleTags, CompleteTag>;
259299
constexpr static auto name = Name;
260-
using type = _then::receiver<Name, HandleTag, CompleteTag, Ts...>;
300+
using type = _then::receiver<Name, HandleTags, CompleteTag, Ts...>;
261301
using children = stdx::type_list<debug::erased_context_for<
262302
connect_result_t<typename type::sender_t &&, type &&>>>;
263303
};

include/async/type_traits.hpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,17 @@ template <typename... Tags> struct with_any_tag {
2828
std::bool_constant<(... or std::is_same_v<Tags, stdx::return_t<Sig>>)>;
2929
};
3030

31+
template <typename Sigs, typename Tag> struct signatures_by_tag_t {
32+
using type = boost::mp11::mp_copy_if_q<Sigs, with_any_tag<Tag>>;
33+
};
34+
35+
template <typename Sigs, typename... Tags>
36+
struct signatures_by_tag_t<Sigs, with_any_tag<Tags...>> {
37+
using type = boost::mp11::mp_copy_if_q<Sigs, with_any_tag<Tags...>>;
38+
};
39+
3140
template <typename Sigs, typename Tag>
32-
using signatures_by_tag = boost::mp11::mp_copy_if_q<Sigs, with_any_tag<Tag>>;
41+
using signatures_by_tag = typename signatures_by_tag_t<Sigs, Tag>::type;
3342

3443
template <bool> struct indirect_meta_apply {
3544
template <template <typename...> typename T, typename... As>

test/then.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <async/just_result_of.hpp>
99
#include <async/schedulers/inline_scheduler.hpp>
1010
#include <async/then.hpp>
11+
#include <async/variant_sender.hpp>
1112

1213
#include <catch2/catch_test_macros.hpp>
1314

@@ -297,3 +298,45 @@ TEST_CASE("then calls functions by need", "[then]") {
297298
async::start(op);
298299
CHECK(value == 5 + 7);
299300
}
301+
302+
TEST_CASE("multithen advertises what it sends", "[then]") {
303+
auto const i = 0;
304+
auto const s = async::make_variant_sender(
305+
i == 0, [] { return async::just(42.0); },
306+
[] { return async::just_error(17.0f); });
307+
308+
[[maybe_unused]] auto n = s | async::multithen([](auto) { return 42; });
309+
310+
STATIC_CHECK(
311+
std::is_same_v<async::completion_signatures_of_t<decltype(n)>,
312+
async::completion_signatures<async::set_value_t(int)>>);
313+
}
314+
315+
TEST_CASE("multithen handles multiple channels (value)", "[then]") {
316+
int value{};
317+
constexpr auto channels =
318+
async::set_value | async::set_error | async::set_stopped;
319+
auto n = async::multithen<channels>([](auto...) { return 42; });
320+
321+
{
322+
value = 0;
323+
auto op = async::connect(async::just(17) | n,
324+
receiver{[&](auto i) { value = i; }});
325+
async::start(op);
326+
CHECK(value == 42);
327+
}
328+
{
329+
value = 0;
330+
auto op = async::connect(async::just_error(17) | n,
331+
receiver{[&](auto i) { value = i; }});
332+
async::start(op);
333+
CHECK(value == 42);
334+
}
335+
{
336+
value = 0;
337+
auto op = async::connect(async::just_stopped() | n,
338+
receiver{[&](auto i) { value = i; }});
339+
async::start(op);
340+
CHECK(value == 42);
341+
}
342+
}

0 commit comments

Comments
 (0)