@@ -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
320320template <class F >
321321auto 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
332332template <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+
362369template <class Chain , class ... Args>
363370auto 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