composed_2.cpp 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. //
  2. // composed_2.cpp
  3. // ~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
  6. //
  7. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. //
  10. #include <boost/asio/io_context.hpp>
  11. #include <boost/asio/ip/tcp.hpp>
  12. #include <boost/asio/use_future.hpp>
  13. #include <boost/asio/write.hpp>
  14. #include <cstring>
  15. #include <iostream>
  16. #include <string>
  17. #include <type_traits>
  18. #include <utility>
  19. using boost::asio::ip::tcp;
  20. //------------------------------------------------------------------------------
  21. // This next simplest example of a composed asynchronous operation involves
  22. // repackaging multiple operations but choosing to invoke just one of them. All
  23. // of these underlying operations have the same completion signature. The
  24. // asynchronous operation requirements are met by delegating responsibility to
  25. // the underlying operations.
  26. template <typename CompletionToken>
  27. auto async_write_message(tcp::socket& socket,
  28. const char* message, bool allow_partial_write,
  29. CompletionToken&& token)
  30. // The return type of the initiating function is deduced from the combination
  31. // of CompletionToken type and the completion handler's signature. When the
  32. // completion token is a simple callback, the return type is void. However,
  33. // when the completion token is boost::asio::yield_context (used for stackful
  34. // coroutines) the return type would be std::size_t, and when the completion
  35. // token is boost::asio::use_future it would be std::future<std::size_t>.
  36. -> typename boost::asio::async_result<
  37. typename std::decay<CompletionToken>::type,
  38. void(boost::system::error_code, std::size_t)>::return_type
  39. {
  40. // As the return type of the initiating function is deduced solely from the
  41. // CompletionToken and completion signature, we know that two different
  42. // asynchronous operations having the same completion signature will produce
  43. // the same return type, when passed the same CompletionToken. This allows us
  44. // to trivially delegate to alternate implementations.
  45. if (allow_partial_write)
  46. {
  47. // When delegating to an underlying operation we must take care to
  48. // perfectly forward the completion token. This ensures that our operation
  49. // works correctly with move-only function objects as callbacks, as well as
  50. // other completion token types.
  51. return socket.async_write_some(
  52. boost::asio::buffer(message, std::strlen(message)),
  53. std::forward<CompletionToken>(token));
  54. }
  55. else
  56. {
  57. // As above, we must perfectly forward the completion token when calling
  58. // the alternate underlying operation.
  59. return boost::asio::async_write(socket,
  60. boost::asio::buffer(message, std::strlen(message)),
  61. std::forward<CompletionToken>(token));
  62. }
  63. }
  64. //------------------------------------------------------------------------------
  65. void test_callback()
  66. {
  67. boost::asio::io_context io_context;
  68. tcp::acceptor acceptor(io_context, {tcp::v4(), 55555});
  69. tcp::socket socket = acceptor.accept();
  70. // Test our asynchronous operation using a lambda as a callback.
  71. async_write_message(socket, "Testing callback\r\n", false,
  72. [](const boost::system::error_code& error, std::size_t n)
  73. {
  74. if (!error)
  75. {
  76. std::cout << n << " bytes transferred\n";
  77. }
  78. else
  79. {
  80. std::cout << "Error: " << error.message() << "\n";
  81. }
  82. });
  83. io_context.run();
  84. }
  85. //------------------------------------------------------------------------------
  86. void test_future()
  87. {
  88. boost::asio::io_context io_context;
  89. tcp::acceptor acceptor(io_context, {tcp::v4(), 55555});
  90. tcp::socket socket = acceptor.accept();
  91. // Test our asynchronous operation using the use_future completion token.
  92. // This token causes the operation's initiating function to return a future,
  93. // which may be used to synchronously wait for the result of the operation.
  94. std::future<std::size_t> f = async_write_message(
  95. socket, "Testing future\r\n", false, boost::asio::use_future);
  96. io_context.run();
  97. try
  98. {
  99. // Get the result of the operation.
  100. std::size_t n = f.get();
  101. std::cout << n << " bytes transferred\n";
  102. }
  103. catch (const std::exception& e)
  104. {
  105. std::cout << "Error: " << e.what() << "\n";
  106. }
  107. }
  108. //------------------------------------------------------------------------------
  109. int main()
  110. {
  111. test_callback();
  112. test_future();
  113. }