Skip to content

Commit 5ae1c8f

Browse files
Cleanup
Start thinking about `f = start(then(fut))` problem
1 parent 620e246 commit 5ae1c8f

File tree

2 files changed

+55
-35
lines changed

2 files changed

+55
-35
lines changed

include/chains/tuple.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,11 @@ constexpr auto tuple_consume(Tuple&& values) {
127127

128128
if constexpr (consumed == 0) {
129129
// Remaining is original tuple (no elements consumed)
130-
return std::tuple_cat(std::make_tuple(std::move(result)), std::move(_values));
130+
return std::tuple_cat(std::tuple{std::move(result)}, std::move(_values));
131131
} else {
132132
auto remaining = tuple_tail_at<tuple_t, consumed>(
133133
std::move(_values), std::make_index_sequence<N - consumed>{});
134-
return std::tuple_cat(std::make_tuple(std::move(result)), std::move(remaining));
134+
return std::tuple_cat(std::tuple{std::move(result)}, std::move(remaining));
135135
}
136136
};
137137
}
@@ -156,7 +156,7 @@ constexpr auto calc_step(F& f, T t) {
156156

157157
template <typename F, typename... Args>
158158
constexpr auto calc(F f, Args&&... args) {
159-
return calc_step<0>(f, std::make_tuple(std::forward<Args>(args)...));
159+
return calc_step<0>(f, std::tuple{std::forward<Args>(args)...});
160160
}
161161

162162
template <class... Fs>

test/initial_draft.cpp

Lines changed: 52 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ class segment {
7777
The basic operations should follow those from C++ lambdas, for now default everything.
7878
and see if the compiler gets it correct.
7979
*/
80-
segment(const segment&) = default;
80+
explicit segment(const segment&) = default;
8181
segment(segment&&) noexcept = default;
8282
segment& operator=(const segment&) = default;
8383
segment& operator=(segment&&) noexcept = default;
@@ -86,7 +86,7 @@ class segment {
8686
auto append(F&& f) && {
8787
return chains::segment{
8888
type<Injects>{}, std::move(_apply),
89-
std::tuple_cat(std::move(_functions), std::make_tuple(std::forward<F>(f)))};
89+
std::tuple_cat(std::move(_functions), std::tuple{std::forward<F>(f)})};
9090
}
9191

9292
#if 0
@@ -177,14 +177,14 @@ class chain {
177177
return [_receiver,
178178
_segment = std::forward<First>(first).append([_receiver]<typename V>(V&& val) {
179179
_receiver->operator()(std::forward<V>(val));
180-
})]<typename... T>(T&&... args) mutable {
181-
return std::move(_segment).invoke(_receiver, std::forward<T>(args)...);
180+
})]<typename... Args>(Args&&... args) mutable {
181+
return std::move(_segment).invoke(_receiver, std::forward<Args>(args)...);
182182
};
183183
} else {
184184
return [_receiver, _segment = std::forward<First>(first).append(
185-
fold(fold, std::forward<Rest>(rest)...))]<typename... T>(
186-
T&&... args) mutable {
187-
return std::move(_segment).invoke(_receiver, std::forward<T>(args)...);
185+
fold(fold, std::forward<Rest>(rest)...))]<typename... Args>(
186+
Args&&... args) mutable {
187+
return std::move(_segment).invoke(_receiver, std::forward<Args>(args)...);
188188
};
189189
}
190190
},
@@ -232,7 +232,7 @@ class chain {
232232

233233
template <class Jnjects, class I, class... Gs>
234234
auto append(segment<Jnjects, I, Gs...>&& head) && {
235-
return chains::chain{std::tuple_cat(std::move(_tail), std::make_tuple(std::move(_head))),
235+
return chains::chain{std::tuple_cat(std::move(_tail), std::tuple{std::move(_head)}),
236236
std::move(head)};
237237
}
238238

@@ -314,51 +314,58 @@ auto on(E&& executor) {
314314
`auto f = start(then(future));`
315315
316316
And we destruct f. We need to _delete_ the (detached) future. Where is this held? f is only
317-
holding the promise.
317+
holding the promise.
318318
*/
319319

320320
template <class F>
321321
auto then(F&& future) {
322322
return chain{std::tuple<>{},
323323
segment{type<typename std::decay_t<F>::result_type>{},
324-
[_future = std::forward<F>(future)](auto&& f) mutable {
325-
return std::move(_future).then(std::forward<decltype(f)>(f));
324+
[_future = std::forward<F>(future)]<typename C>(C&& continuation) mutable {
325+
return std::move(_future).then(std::forward<C>(continuation));
326326
}}};
327327
}
328328

329329
// TODO: (sean-parent) - should we make this pipeable?
330330
// TODO: (sean-parent) - fix case where invoke_t is void.
331331

332332
template <class Chain, class... Args>
333-
auto start(Chain&& chain, Args&&... args) {
334-
using result_t = typename Chain::template result_type<Args...>;
335-
333+
inline auto start(Chain&& chain, Args&&... args) {
334+
using result_t = Chain::template result_type<Args...>;
336335
using package_task_t = stlab::packaged_task<result_t>;
337-
auto shared = std::shared_ptr<package_task_t>();
338336

339-
// Build the receiver and future first.
340-
auto [receiver, future] = stlab::package<result_t(result_t)>(
341-
stlab::immediate_executor,
342-
[_shared = shared]<typename T>(T&& val) { return std::forward<T>(val); });
337+
using invoke_t = decltype(std::forward<Chain>(chain).invoke(
338+
std::declval<std::shared_ptr<stlab::packaged_task<result_t>>>(),
339+
std::forward<Args>(args)...));
340+
341+
auto shared_receiver = std::shared_ptr<package_task_t>();
343342

344-
// Promote receiver to shared_ptr to extend lifetime beyond this scope.
345-
shared = std::make_shared<package_task_t>(std::move(receiver));
343+
if constexpr (std::is_same_v<invoke_t, void>) {
344+
auto [receiver, future] = stlab::package<result_t(result_t)>(
345+
stlab::immediate_executor, []<typename T>(T&& val) { return std::forward<T>(val); });
346346

347-
// Recompute invoke_t based on passing the shared_ptr (pointer semantics).
348-
using invoke_t =
349-
decltype(std::forward<Chain>(chain).invoke(shared, std::forward<Args>(args)...));
347+
// Promote receiver to shared_ptr to extend lifetime beyond this scope and circumvent the
348+
// move only capabilities of package_task.
349+
shared_receiver = std::make_shared<package_task_t>(std::move(receiver));
350350

351-
if constexpr (std::is_void_v<invoke_t>) {
352-
// Just invoke; lifetime of receiver is now owned by captures inside the async chain.
353-
std::forward<Chain>(chain).invoke(shared, std::forward<Args>(args)...);
351+
std::forward<Chain>(chain).invoke(std::move(shared_receiver), std::forward<Args>(args)...);
352+
353+
return std::move(future);
354354
} else {
355-
// Keep any handle the chain returns (e.g. continuation future or cancellation handle).
356-
auto hold = std::forward<Chain>(chain).invoke(shared, std::forward<Args>(args)...);
357-
(void)hold; // store or return if you later need it
355+
auto p = std::make_shared<std::optional<invoke_t>>();
356+
auto [receiver, future] = stlab::package<result_t(result_t)>(
357+
stlab::immediate_executor,
358+
[p]<typename V>(V&& value) { return std::forward<V>(value); });
359+
360+
shared_receiver = std::make_shared<package_task_t>(std::move(receiver));
361+
362+
*p = std::forward<Chain>(chain).invoke(std::move(shared_receiver),
363+
std::forward<Args>(args)...);
364+
return std::move(future);
358365
}
359-
return std::move(future);
360366
}
361367

368+
362369
template <class Chain, class... Args>
363370
auto sync_wait(Chain&& chain, Args&&... args) {
364371
using result_t = typename Chain::template result_type<Args...>;
@@ -465,7 +472,7 @@ auto on_with_cancellation(E&& executor, cancellation_source source) {
465472
cancellation_token token{_source._state};
466473
std::move(_executor)(
467474
[_f = std::forward<F>(f), _token = token,
468-
_args = std::make_tuple(std::forward<Args>(args)...)]() mutable noexcept {
475+
_args = std::tuple{std::forward<Args>(args)...}]() mutable noexcept {
469476
std::apply(
470477
[&_f, &_token]<typename... As>(As&&... as) {
471478
std::forward<decltype(_f)>(_f)(_token, std::forward<As>(as)...);
@@ -788,3 +795,16 @@ TEST_CASE("Initial draft", "[initial_draft]") {
788795
REQUIRE(val == string("84!"));
789796
}
790797
}
798+
799+
800+
TEST_CASE("Cancellation of then()", "[initial_draft]") {
801+
annotate_counters cnt;
802+
GIVEN("that a ") {
803+
auto fut = async(default_executor, [_counter = annotate{cnt}] {
804+
std::cout << "Future run\n";
805+
return std::string("42");
806+
});
807+
auto result_f = start(then(fut));
808+
}
809+
std::cout << cnt;
810+
}

0 commit comments

Comments
 (0)