// Copyright Nat Goodspeed 2015. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include // std::shared_ptr #include #include #include #include #include #include /***************************************************************************** * example async API *****************************************************************************/ // introduce class-scope typedef struct AsyncAPIBase { // error callback accepts an int error code; 0 == success typedef int errorcode; }; //[Response // every async operation receives a subclass instance of this abstract base // class through which to communicate its result struct Response { typedef std::shared_ptr ptr; // called if the operation succeeds virtual void success(const std::string& data) = 0; // called if the operation fails virtual void error(AsyncAPIBase::errorcode ec) = 0; }; //] // the actual async API class AsyncAPI: public AsyncAPIBase { public: // constructor acquires some resource that can be read AsyncAPI(const std::string& data); //[method_init_read // derive Response subclass, instantiate, pass Response::ptr void init_read(Response::ptr); //] // ... other operations ... void inject_error(errorcode ec); private: std::string data_; errorcode injected_; }; /***************************************************************************** * fake AsyncAPI implementation... pay no attention to the little man behind * the curtain... *****************************************************************************/ AsyncAPI::AsyncAPI(const std::string& data): data_(data), injected_(0) {} void AsyncAPI::inject_error(errorcode ec) { injected_ = ec; } void AsyncAPI::init_read(Response::ptr response) { // make a local copy of injected_ errorcode injected(injected_); // reset it synchronously with caller injected_ = 0; // local copy of data_ so we can capture in lambda std::string data(data_); // Simulate an asynchronous I/O operation by launching a detached thread // that sleeps a bit before calling either completion method. std::thread([injected, response, data](){ std::this_thread::sleep_for(std::chrono::milliseconds(100)); if (! injected) { // no error, call success() response->success(data); } else { // injected error, call error() response->error(injected); } }).detach(); } /***************************************************************************** * adapters *****************************************************************************/ // helper function std::runtime_error make_exception(const std::string& desc, AsyncAPI::errorcode); //[PromiseResponse class PromiseResponse: public Response { public: // called if the operation succeeds virtual void success(const std::string& 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 get_future() { return promise_.get_future(); } private: boost::fibers::promise promise_; }; //] //[method_read 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()); boost::fibers::future future(promisep->get_future()); // Both 'promisep' and 'future' will survive until our lambda has been // called. api.init_read(promisep); return future.get(); } //] /***************************************************************************** * helpers *****************************************************************************/ std::runtime_error make_exception(const std::string& desc, AsyncAPI::errorcode ec) { std::ostringstream buffer; buffer << "Error in AsyncAPI::" << desc << "(): " << ec; return std::runtime_error(buffer.str()); } /***************************************************************************** * driving logic *****************************************************************************/ int main(int argc, char *argv[]) { // prime AsyncAPI with some data AsyncAPI api("abcd"); // successful read(): retrieve it std::string data(read(api)); assert(data == "abcd"); // read() with error std::string thrown; api.inject_error(1); try { data = read(api); } catch (const std::exception& e) { thrown = e.what(); } assert(thrown == make_exception("read", 1).what()); return EXIT_SUCCESS; }