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