
Johan Torp:
I often have some kind of top flow control mechanism for some threads which waits - without any time limit/poll - when there is nothing to do. It might be; - A message pump - An asio::io_service (calling run) - A job queue - A logical clock in reactive programming
Within this thread code is executed while processing a message, servicing some thing, executing a job or inside a logical clock tick/instant. Let's call this X's client code. Inside the client code I imagine you often will create a new pending request to some service and get a future back to wait on. Instead of waiting directly on the future and stall the entire thread, you'd typically want to get a notification from X at a later point. Let's call this future F and let's for simplicity assume there is only one of it.
This gives us the need to wait for either something to do in X or F.
The fundamental question is whether the layer you're calling should expose future<X> async_do_something( Y y ); or void async_do_something( Y y, function<void(X)> done ); Obviously, in a message-passing-based architecture, the second interface would be more convenient as it allows you to just post an appropriate message in 'done'. The "completion callback" approach tries to express the second interface in terms of the first, but I'm not quite sure that this is correct. Expressing the first in terms of the second seems more natural to me. future<X> async_do_something( Y y ) { promise<X> px; future<X> fx = px.get_future(); async_do_something( y, bind( &promise<X>::set_value, move(px), _1 ) ); return fx; } There's the small problem with the exceptions though. void async_do_something( Y y, function<void(X)> done, function<void(exception_ptr)> failed ); future<X> async_do_something( Y y ) { shared_ptr< promise<X> > px( new promise<X> ); future<X> fx = px->get_future(); async_do_something( y, bind( &promise<X>::set_value, px, _1 ) bind( &promise<X>::set_exception, px, _1 ) ); return fx; } which only reaffirms my experience that each time you see something move-only, you are going to need shared_ptr to get around its limitations. But that's another story. :-)