2
0
mirror of https://github.com/boostorg/fiber.git synced 2026-02-13 00:12:17 +00:00
Files
fiber/examples/adapt_method_calls.cpp
Nat Goodspeed 1e3e2b8887 Add callbacks.qbk about interfacing Fiber with async callbacks.
This covers both generic callbacks (adapt_callbacks.cpp,
adapt_method_calls.cpp) and custom Asio completion tokens (yield.hpp,
promise_completion_token.hpp, detail/yield.hpp, detail/promise_handler.hpp).
Mark up the relevant source files to provide code snippets for callbacks.qbk.
2015-08-24 09:28:16 -04:00

182 lines
5.0 KiB
C++

// 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 <boost/fiber/all.hpp>
#include <memory> // std::shared_ptr
#include <thread>
#include <chrono>
#include <iostream>
#include <sstream>
#include <exception>
#include <cassert>
/*****************************************************************************
* 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<Response> 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<std::string> get_future()
{
return promise_.get_future();
}
private:
boost::fibers::promise<std::string> 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<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();
}
//]
/*****************************************************************************
* 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;
}