]>
Commit | Line | Data |
---|---|---|
92f5a8d4 TL |
1 | // |
2 | // composed_2.cpp | |
3 | // ~~~~~~~~~~~~~~ | |
4 | // | |
1e59de90 | 5 | // Copyright (c) 2003-2022 Christopher M. Kohlhoff (chris at kohlhoff dot com) |
92f5a8d4 TL |
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> | |
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 | -> typename boost::asio::async_result< | |
42 | typename std::decay<CompletionToken>::type, | |
43 | void(boost::system::error_code, std::size_t)>::return_type | |
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 | ||
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 | ||
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 | ||
127 | int main() | |
128 | { | |
129 | test_callback(); | |
130 | test_future(); | |
131 | } |