1 //
2 // composed_1.cpp
3 // ~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2021 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 is the simplest example of a composed asynchronous operation, where we
26 // simply repackage an existing operation. The asynchronous operation
27 // requirements are met by delegating responsibility to the underlying
28 // operation.
29
30 template <typename CompletionToken>
async_write_message(tcp::socket & socket,const char * message,CompletionToken && token)31 auto async_write_message(tcp::socket& socket,
32 const char* message, CompletionToken&& token)
33 // The return type of the initiating function is deduced from the combination
34 // of CompletionToken type and the completion handler's signature. When the
35 // completion token is a simple callback, the return type is void. However,
36 // when the completion token is boost::asio::yield_context (used for stackful
37 // coroutines) the return type would be std::size_t, and when the completion
38 // token is boost::asio::use_future it would be std::future<std::size_t>.
39 //
40 // In C++14 we can omit the return type as it is automatically deduced from
41 // the return type of our underlying asynchronous operation
42 {
43 // When delegating to the underlying operation we must take care to perfectly
44 // forward the completion token. This ensures that our operation works
45 // correctly with move-only function objects as callbacks, as well as other
46 // completion token types.
47 return boost::asio::async_write(socket,
48 boost::asio::buffer(message, std::strlen(message)),
49 std::forward<CompletionToken>(token));
50 }
51
52 //------------------------------------------------------------------------------
53
test_callback()54 void test_callback()
55 {
56 boost::asio::io_context io_context;
57
58 tcp::acceptor acceptor(io_context, {tcp::v4(), 55555});
59 tcp::socket socket = acceptor.accept();
60
61 // Test our asynchronous operation using a lambda as a callback.
62 async_write_message(socket, "Testing callback\r\n",
63 [](const boost::system::error_code& error, std::size_t n)
64 {
65 if (!error)
66 {
67 std::cout << n << " bytes transferred\n";
68 }
69 else
70 {
71 std::cout << "Error: " << error.message() << "\n";
72 }
73 });
74
75 io_context.run();
76 }
77
78 //------------------------------------------------------------------------------
79
test_future()80 void test_future()
81 {
82 boost::asio::io_context io_context;
83
84 tcp::acceptor acceptor(io_context, {tcp::v4(), 55555});
85 tcp::socket socket = acceptor.accept();
86
87 // Test our asynchronous operation using the use_future completion token.
88 // This token causes the operation's initiating function to return a future,
89 // which may be used to synchronously wait for the result of the operation.
90 std::future<std::size_t> f = async_write_message(
91 socket, "Testing future\r\n", boost::asio::use_future);
92
93 io_context.run();
94
95 try
96 {
97 // Get the result of the operation.
98 std::size_t n = f.get();
99 std::cout << n << " bytes transferred\n";
100 }
101 catch (const std::exception& e)
102 {
103 std::cout << "Error: " << e.what() << "\n";
104 }
105 }
106
107 //------------------------------------------------------------------------------
108
main()109 int main()
110 {
111 test_callback();
112 test_future();
113 }
114