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