| Home | Libraries | People | FAQ | More

## Tutorial 4: the static interface

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.

#### Dynamic interface limitations

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:

- We retrieve fields by position. That is, we know that `employee.at(0)` holds the employee's `first_name`. However, if we refactor our query, we might forget updating the indices.
- Following a similar reasoning, the casts are error-prone.
- Both `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.

#### Using static_results with Boost.Pfr

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.

#### Using the static interface in C++14

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.

#### Wrapping up

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.