C++20 coroutine is supported by the language.
Using C++ coroutines can avoid deeply nested callbacks, making the code easier to read.
co_spawn your application routine
int main(int argc, char* argv[]) {
am::setup_log(am::severity_level::info);
if (argc != 3) {
std::cout << "Usage: " << argv[0] << " host port" << std::endl;
return -1;
}
as::io_context ioc;
as::co_spawn(ioc, proc(ioc.get_executor(), argv[1], argv[2]), as::detached);
ioc.run();
}
To use a coroutine, you need to call as::co_spawn()
to launch the coroutine as follows:
as::co_spawn(ioc, proc(ioc.get_executor(), argv[1], argv[2]), as::detached);
define application routine
Define the application routine proc()
as follows. The return value should be wrapped in as::awaitable<>
.
template <typename Executor>
as::awaitable<void>
proc(Executor exe, std::string_view host, std::string_view port) {
...
define endpoint
auto amep = am::endpoint<am::role::client, am::protocol::mqtt>{
am::protocol_version::v3_1_1,
exe
};
std::cout << "start" << std::endl;
There is nothing special; it is the same as a non-coroutine endpoint.
chain async calls
// prepare will message if you need.
am::will will{
"WillTopic1",
"WillMessage1",
am::qos::at_most_once,
{ // properties
am::property::user_property{"key1", "val1"},
am::property::content_type{"text"},
}
};
// handshake underlying layers
co_await am::underlying_handshake(amep.next_layer(), host, port, as::use_awaitable);
std::cout << "underlying_handshaked" << std::endl;
// Send MQTT CONNECT
co_await amep.async_send(
am::v3_1_1::connect_packet{
true, // clean_session
0x1234, // keep_alive
"ClientIdentifier1",
will, // you can pass std::nullopt if you don't want to set the will message
"UserName1",
"Password1"
},
as::use_awaitable
);
// Recv MQTT CONNACK
am::packet_variant pv = co_await amep.async_recv(as::use_awaitable);
pv.visit(
am::overload {
[&](am::v3_1_1::connack_packet const& p) {
std::cout
<< "MQTT CONNACK recv"
<< " sp:" << p.session_present()
<< std::endl;
},
[](auto const&) {}
}
);
When you call async functions, add the co_await
keyword before the function call and pass as::use_awaitable
as the CompletionToken
. This is the C++20 coroutine way.
For example, sending a CONNECT packet is done as follows. When send()
is called, the async send procedure is triggered. When the procedure is finished, the context is returned. You can then access the return value ec
. If ec
evaluates to true, it means an error occurred, so the code prints an error message and uses co_return
. When you want to return from a function that has an as::awaitable<>
return value, you need to call co_return
instead of return
.
For detailed information about errors, refer to Errors for APIs.
whole code (example)
Other Completion Tokens
You can use not only as::use_awaitable
but also as::deferred
and as::experimental::use_promise
. See the Asio documentation for more details.