...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
One classic approach to completion notification is to define an abstract
base class with success()
and error()
methods. Code wishing to perform async I/O must derive a subclass, override
each of these methods and pass the async operation a pointer to a subclass
instance. The abstract base class might look like this:
// every async operation receives a subclass instance of this abstract base // class through which to communicate its result struct Response { typedef std::shared_ptr< Response > ptr; // called if the operation succeeds virtual void success( std::string const& data) = 0; // called if the operation fails virtual void error( AsyncAPIBase::errorcode ec) = 0; };
Now the AsyncAPI
operation
might look more like this:
// derive Response subclass, instantiate, pass Response::ptr void init_read( Response::ptr);
We can address this by writing a one-size-fits-all PromiseResponse
:
class PromiseResponse: public Response { public: // called if the operation succeeds virtual void success( std::string const& data) { promise_.set_value( data); } // called if the operation fails virtual void error( AsyncAPIBase::errorcode ec) { promise_.set_exception( std::make_exception_ptr( make_exception("read", ec) ) ); } boost::fibers::future< std::string > get_future() { return promise_.get_future(); } private: boost::fibers::promise< std::string > promise_; };
Now we can simply obtain the future<>
from that PromiseResponse
and wait on its get()
:
std::string read( AsyncAPI & api) { // Because init_read() requires a shared_ptr, we must allocate our // ResponsePromise on the heap, even though we know its lifespan. auto promisep( std::make_shared< PromiseResponse >() ); boost::fibers::future< std::string > future( promisep->get_future() ); // Both 'promisep' and 'future' will survive until our lambda has been // called. api.init_read( promisep); return future.get(); }
The source code above is found in adapt_callbacks.cpp and adapt_method_calls.cpp.