...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
In the previous tutorial we used synchronous functions. They are simple, but have a number of limitations:
connection_pool
)
only offer an async interface.
For this reason, we recommend to always use async functions. All Asio-compatible libraries (including this one) allow async programming using a variety of styles. In this chapter, we will explain how to use C++20 coroutines because they are the simplest to use.
![]() |
Note |
---|---|
Still not using C++20? Don't worry, you can use stackful coroutines and callbacks even in C++11. |
Roughly speaking, it's a function that can suspend and resume, keeping local
variables alive in the process. Suspension happens when reaching a co_await
expression. These usually appear
when the program performs an I/O operation. When an expression like this is
encountered, the following happens:
io_context
(that is, the event loop).
io_context
may run other operations, like other coroutines.
io_context
resumes the coroutine immediately after the co_await
expression.
Recall the following code from our previous tutorial:
// The hostname, username and password to use mysql::connect_params params; params.server_address.emplace_host_and_port(hostname); params.username = username; params.password = password; // Connect to the server conn.connect(params); // Issue the SQL query to the server const char* sql = "SELECT 'Hello world!'"; mysql::results result; conn.execute(sql, result); // Print the first field in the first row std::cout << result.rows().at(0).at(0) << std::endl; // Close the connection conn.close();
To transform this code into a coroutine, we need to:
boost::asio::awaitable<void>
.
any_connection::connect
)
by async ones (like any_connection::async_connect
).
co_await
operator
in front of each I/O operation.
Doing this, we have:
asio::awaitable<void> coro_main( mysql::any_connection& conn, std::string_view server_hostname, std::string_view username, std::string_view password ) { // The hostname, username, password and database to use. // TLS is used by default. mysql::connect_params params; params.server_address.emplace_host_and_port(std::string(server_hostname)); params.username = username; params.password = password; // Connect to the server co_await conn.async_connect(params); // Issue the SQL query to the server const char* sql = "SELECT 'Hello world!'"; mysql::results result; co_await conn.async_execute(sql, result); // Print the first field in the first row std::cout << result.rows().at(0).at(0) << std::endl; // Close the connection co_await conn.async_close(); }
Note that the coroutine doesn't create or return explicitly any boost::asio::awaitable<void>
object - this is handled by the compiler. The return type actually marks the
function as being a coroutine. void
here means that the coroutine doesn't return anything.
If any of the above I/O operations fail, an exception is thrown. You can prevent
this by using asio::redirect_error
.
As in the previous tutorial, we first need to create an io_context
and a connection:
// The execution context, required to run I/O operations. asio::io_context ctx; // Represents a connection to the MySQL server. mysql::any_connection conn(ctx);
To run a coroutine, use asio::co_spawn
:
// Enqueue the coroutine for execution. // This does not wait for the coroutine to finish. asio::co_spawn( // The execution context where the coroutine will run ctx, // The coroutine to run. This must be a function taking no arguments // and returning an asio::awaitable<T> [&conn, argv] { return coro_main(conn, argv[3], argv[1], argv[2]); }, // Callback to run when the coroutine completes. // If any exception is thrown in the coroutine body, propagate it to terminate the program. [](std::exception_ptr ptr) { if (ptr) { std::rethrow_exception(ptr); } } );
Note that this will only schedule the coroutine. To actually run it, we need
to call io_context::run
. This will block the calling thread until
all the scheduled coroutines and I/O operations complete:
// Calling run will actually execute the coroutine until completion ctx.run();
Full program listing for this tutorial is here.
You can now proceed to the next tutorial.