1 //
2 // composed_3.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 
11 #include <boost/asio/bind_executor.hpp>
12 #include <boost/asio/io_context.hpp>
13 #include <boost/asio/ip/tcp.hpp>
14 #include <boost/asio/use_future.hpp>
15 #include <boost/asio/write.hpp>
16 #include <cstring>
17 #include <functional>
18 #include <iostream>
19 #include <string>
20 #include <type_traits>
21 #include <utility>
22 
23 using boost::asio::ip::tcp;
24 
25 // NOTE: This example requires the new boost::asio::async_initiate function. For
26 // an example that works with the Networking TS style of completion tokens,
27 // please see an older version of asio.
28 
29 //------------------------------------------------------------------------------
30 
31 // In this composed operation we repackage an existing operation, but with a
32 // different completion handler signature. The asynchronous operation
33 // requirements are met by delegating responsibility to the underlying
34 // operation.
35 
36 // In addition to determining the mechanism by which an asynchronous operation
37 // delivers its result, a completion token also determines the time when the
38 // operation commences. For example, when the completion token is a simple
39 // callback the operation commences before the initiating function returns.
40 // However, if the completion token's delivery mechanism uses a future, we
41 // might instead want to defer initiation of the operation until the returned
42 // future object is waited upon.
43 //
44 // To enable this, when implementing an asynchronous operation we must package
45 // the initiation step as a function object.
46 struct async_write_message_initiation
47 {
48   // The initiation function object's call operator is passed the concrete
49   // completion handler produced by the completion token. This completion
50   // handler matches the asynchronous operation's completion handler signature,
51   // which in this example is:
52   //
53   //   void(boost::system::error_code error)
54   //
55   // The initiation function object also receives any additional arguments
56   // required to start the operation. (Note: We could have instead passed these
57   // arguments as members in the initiaton function object. However, we should
58   // prefer to propagate them as function call arguments as this allows the
59   // completion token to optimise how they are passed. For example, a lazy
60   // future which defers initiation would need to make a decay-copy of the
61   // arguments, but when using a simple callback the arguments can be trivially
62   // forwarded straight through.)
63   template <typename CompletionHandler>
operator ()async_write_message_initiation64   void operator()(CompletionHandler&& completion_handler,
65       tcp::socket& socket, const char* message) const
66   {
67     // The async_write operation has a completion handler signature of:
68     //
69     //   void(boost::system::error_code error, std::size n)
70     //
71     // This differs from our operation's signature in that it is also passed
72     // the number of bytes transferred as an argument of type std::size_t. We
73     // will adapt our completion handler to async_write's completion handler
74     // signature by using std::bind, which drops the additional argument.
75     //
76     // However, it is essential to the correctness of our composed operation
77     // that we preserve the executor of the user-supplied completion handler.
78     // The std::bind function will not do this for us, so we must do this by
79     // first obtaining the completion handler's associated executor (defaulting
80     // to the I/O executor - in this case the executor of the socket - if the
81     // completion handler does not have its own) ...
82     auto executor = boost::asio::get_associated_executor(
83         completion_handler, socket.get_executor());
84 
85     // ... and then binding this executor to our adapted completion handler
86     // using the boost::asio::bind_executor function.
87     boost::asio::async_write(socket,
88         boost::asio::buffer(message, std::strlen(message)),
89         boost::asio::bind_executor(executor,
90           std::bind(std::forward<CompletionHandler>(
91             completion_handler), std::placeholders::_1)));
92   }
93 };
94 
95 template <typename CompletionToken>
async_write_message(tcp::socket & socket,const char * message,CompletionToken && token)96 auto async_write_message(tcp::socket& socket,
97     const char* message, CompletionToken&& token)
98   // The return type of the initiating function is deduced from the combination
99   // of CompletionToken type and the completion handler's signature. When the
100   // completion token is a simple callback, the return type is always void.
101   // In this example, when the completion token is boost::asio::yield_context
102   // (used for stackful coroutines) the return type would be also be void, as
103   // there is no non-error argument to the completion handler. When the
104   // completion token is boost::asio::use_future it would be std::future<void>.
105   -> typename boost::asio::async_result<
106     typename std::decay<CompletionToken>::type,
107     void(boost::system::error_code)>::return_type
108 {
109   // The boost::asio::async_initiate function takes:
110   //
111   // - our initiation function object,
112   // - the completion token,
113   // - the completion handler signature, and
114   // - any additional arguments we need to initiate the operation.
115   //
116   // It then asks the completion token to create a completion handler (i.e. a
117   // callback) with the specified signature, and invoke the initiation function
118   // object with this completion handler as well as the additional arguments.
119   // The return value of async_initiate is the result of our operation's
120   // initiating function.
121   //
122   // Note that we wrap non-const reference arguments in std::reference_wrapper
123   // to prevent incorrect decay-copies of these objects.
124   return boost::asio::async_initiate<
125     CompletionToken, void(boost::system::error_code)>(
126       async_write_message_initiation(),
127       token, std::ref(socket), message);
128 }
129 
130 //------------------------------------------------------------------------------
131 
test_callback()132 void test_callback()
133 {
134   boost::asio::io_context io_context;
135 
136   tcp::acceptor acceptor(io_context, {tcp::v4(), 55555});
137   tcp::socket socket = acceptor.accept();
138 
139   // Test our asynchronous operation using a lambda as a callback.
140   async_write_message(socket, "Testing callback\r\n",
141       [](const boost::system::error_code& error)
142       {
143         if (!error)
144         {
145           std::cout << "Message sent\n";
146         }
147         else
148         {
149           std::cout << "Error: " << error.message() << "\n";
150         }
151       });
152 
153   io_context.run();
154 }
155 
156 //------------------------------------------------------------------------------
157 
test_future()158 void test_future()
159 {
160   boost::asio::io_context io_context;
161 
162   tcp::acceptor acceptor(io_context, {tcp::v4(), 55555});
163   tcp::socket socket = acceptor.accept();
164 
165   // Test our asynchronous operation using the use_future completion token.
166   // This token causes the operation's initiating function to return a future,
167   // which may be used to synchronously wait for the result of the operation.
168   std::future<void> f = async_write_message(
169       socket, "Testing future\r\n", boost::asio::use_future);
170 
171   io_context.run();
172 
173   // Get the result of the operation.
174   try
175   {
176     // Get the result of the operation.
177     f.get();
178     std::cout << "Message sent\n";
179   }
180   catch (const std::exception& e)
181   {
182     std::cout << "Error: " << e.what() << "\n";
183   }
184 }
185 
186 //------------------------------------------------------------------------------
187 
main()188 int main()
189 {
190   test_callback();
191   test_future();
192 }
193