adapt_method_calls.cpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. // Copyright Nat Goodspeed 2015.
  2. // Distributed under the Boost Software License, Version 1.0.
  3. // (See accompanying file LICENSE_1_0.txt or copy at
  4. // http://www.boost.org/LICENSE_1_0.txt)
  5. #include <boost/fiber/all.hpp>
  6. #include <memory> // std::shared_ptr
  7. #include <thread>
  8. #include <chrono>
  9. #include <iostream>
  10. #include <sstream>
  11. #include <exception>
  12. #include <cassert>
  13. /*****************************************************************************
  14. * example async API
  15. *****************************************************************************/
  16. // introduce class-scope typedef
  17. struct AsyncAPIBase {
  18. // error callback accepts an int error code; 0 == success
  19. typedef int errorcode;
  20. };
  21. //[Response
  22. // every async operation receives a subclass instance of this abstract base
  23. // class through which to communicate its result
  24. struct Response {
  25. typedef std::shared_ptr< Response > ptr;
  26. // called if the operation succeeds
  27. virtual void success( std::string const& data) = 0;
  28. // called if the operation fails
  29. virtual void error( AsyncAPIBase::errorcode ec) = 0;
  30. };
  31. //]
  32. // the actual async API
  33. class AsyncAPI: public AsyncAPIBase {
  34. public:
  35. // constructor acquires some resource that can be read
  36. AsyncAPI( std::string const& data);
  37. //[method_init_read
  38. // derive Response subclass, instantiate, pass Response::ptr
  39. void init_read( Response::ptr);
  40. //]
  41. // ... other operations ...
  42. void inject_error( errorcode ec);
  43. private:
  44. std::string data_;
  45. errorcode injected_;
  46. };
  47. /*****************************************************************************
  48. * fake AsyncAPI implementation... pay no attention to the little man behind
  49. * the curtain...
  50. *****************************************************************************/
  51. AsyncAPI::AsyncAPI( std::string const& data) :
  52. data_( data),
  53. injected_( 0) {
  54. }
  55. void AsyncAPI::inject_error( errorcode ec) {
  56. injected_ = ec;
  57. }
  58. void AsyncAPI::init_read( Response::ptr response) {
  59. // make a local copy of injected_
  60. errorcode injected( injected_);
  61. // reset it synchronously with caller
  62. injected_ = 0;
  63. // local copy of data_ so we can capture in lambda
  64. std::string data( data_);
  65. // Simulate an asynchronous I/O operation by launching a detached thread
  66. // that sleeps a bit before calling either completion method.
  67. std::thread( [injected, response, data](){
  68. std::this_thread::sleep_for( std::chrono::milliseconds(100) );
  69. if ( ! injected) {
  70. // no error, call success()
  71. response->success( data);
  72. } else {
  73. // injected error, call error()
  74. response->error( injected);
  75. }
  76. }).detach();
  77. }
  78. /*****************************************************************************
  79. * adapters
  80. *****************************************************************************/
  81. // helper function
  82. std::runtime_error make_exception( std::string const& desc, AsyncAPI::errorcode);
  83. //[PromiseResponse
  84. class PromiseResponse: public Response {
  85. public:
  86. // called if the operation succeeds
  87. virtual void success( std::string const& data) {
  88. promise_.set_value( data);
  89. }
  90. // called if the operation fails
  91. virtual void error( AsyncAPIBase::errorcode ec) {
  92. promise_.set_exception(
  93. std::make_exception_ptr(
  94. make_exception("read", ec) ) );
  95. }
  96. boost::fibers::future< std::string > get_future() {
  97. return promise_.get_future();
  98. }
  99. private:
  100. boost::fibers::promise< std::string > promise_;
  101. };
  102. //]
  103. //[method_read
  104. std::string read( AsyncAPI & api) {
  105. // Because init_read() requires a shared_ptr, we must allocate our
  106. // ResponsePromise on the heap, even though we know its lifespan.
  107. auto promisep( std::make_shared< PromiseResponse >() );
  108. boost::fibers::future< std::string > future( promisep->get_future() );
  109. // Both 'promisep' and 'future' will survive until our lambda has been
  110. // called.
  111. api.init_read( promisep);
  112. return future.get();
  113. }
  114. //]
  115. /*****************************************************************************
  116. * helpers
  117. *****************************************************************************/
  118. std::runtime_error make_exception( std::string const& desc, AsyncAPI::errorcode ec) {
  119. std::ostringstream buffer;
  120. buffer << "Error in AsyncAPI::" << desc << "(): " << ec;
  121. return std::runtime_error( buffer.str() );
  122. }
  123. /*****************************************************************************
  124. * driving logic
  125. *****************************************************************************/
  126. int main(int argc, char *argv[]) {
  127. // prime AsyncAPI with some data
  128. AsyncAPI api("abcd");
  129. // successful read(): retrieve it
  130. std::string data( read( api) );
  131. assert(data == "abcd");
  132. // read() with error
  133. std::string thrown;
  134. api.inject_error(1);
  135. try {
  136. data = read( api);
  137. } catch ( std::exception const& e) {
  138. thrown = e.what();
  139. }
  140. assert(thrown == make_exception("read", 1).what() );
  141. std::cout << "done." << std::endl;
  142. return EXIT_SUCCESS;
  143. }