...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
Until now we've read the rows generated by our queries into results
objects. As we've seen, results
is a 2D structure that contains variant-like values. Throughout the documentation,
these variant-based APIs are called the
dynamic interface.
Working with variant-like objects can be cumbersome. Recall the following lines from our previous tutorial, where we used the retrieved rows:
// Did we find an employee with that ID? if (result.rows().empty()) { std::cout << "Employee not found" << std::endl; } else { // Print the retrieved details. The first field is the first name, // and the second, the last name. mysql::row_view employee = result.rows().at(0); std::cout << "Employee's name is: " << employee.at(0) << ' ' << employee.at(1) << std::endl; }
An employee is represented here by a row_view
,
which is a collection of field_view
objects. field_view
is a variant-like
type that can represent all the types supported by MySQL.
Since field_view
supports streaming,
this code doesn't require any casting. However, consider refactoring our code
to split the printing logic to a separate function:
void print_employee(std::string_view first_name, std::string_view last_name) { std::cout << "Employee's name is: " << first_name << ' ' << last_name << std::endl; }
Looking at our database schema, we know that both values are strings. We can
use field_view::as_string
to perform the casts:
mysql::row_view employee = result.rows().at(0); print_employee(employee.at(0).as_string(), employee.at(1).as_string());
While this code works, it can create maintenance problems:
employee.at(0)
holds
the employee's first_name
.
However, if we refactor our query, we might forget updating the indices.
at
and as_string
throw on error. Attempting
to avoid exceptions while still performing the required safety checks quickly
becomes unmanageable.
If we know the types returned by our queries at compile time, we can use the static interface, instead. This interface can parse the rows returned by a query into instances of a C++ struct defined by us.
![]() |
Note |
---|---|
The static interface requires C++14 or later. |
In C++20 and later, we can use Boost.Pfr
and the static_results
class to parse the rows. The first step is to define a plain C++ struct with
the fields we expect in our query:
// Should contain a member for each field of interest present in our query. // Declaration order doesn't need to match field order in the query. // Field names should match the ones in our query struct employee { std::string first_name; std::string last_name; };
We now replace the results
object by a static_results
.
The marker type pfr_by_name
indicates Boot.MySQL that it should use Boost.Pfr for reflection.
// Using static_results will parse the result of our query // into instances of the employee type. Fields will be matched // by name, instead of by position. // pfr_by_name tells the library to use Boost.Pfr for reflection, // and to match fields by name. mysql::static_results<mysql::pfr_by_name<employee>> result; // Execute the query with the given parameters, performing the required // escaping to prevent SQL injection. co_await conn.async_execute( mysql::with_params("SELECT first_name, last_name FROM employee WHERE id = {}", employee_id), result );
Using the retrieved data is now much easier, since static_results::rows
returns a span<const employee>
:
// Did we find an employee with that ID? if (result.rows().empty()) { std::cout << "Employee not found" << std::endl; } else { // Print the retrieved details const employee& emp = result.rows()[0]; print_employee(emp.first_name, emp.last_name); }
When using the static interface, async_execute
will perform a set of checks on the query results to ensure compatibility between
the C++ types and what MySQL returns. These checks aim to discover potential
problems as early as possible, and are called metadata
checks.
C++20 makes it possible to use Boost.Pfr as described here, which is the easiest
option. Boost.MySQL also supports Boost.Describe
and std::tuple
's, which can be used in C++14. The
mechanics are quite similar to what's been explained here.
This section contains more information about the static interface.
Full program listing for this tutorial is here.
You can now proceed to the next tutorial.