Boost C++ Libraries Home Libraries People FAQ More

Next

Chapter 1. Boost.PFR 2.0

Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

Table of Contents

Intro
Short Examples for the Impatient
Tutorial
Why tuples are bad and aggregates are more preferable?
Accessing structure member by index
Custom printing of aggregates
Three ways of getting operators
Reflection of unions
Limitations and Configuration
How it works
Acknowledgements
Reference Section
Header <boost/pfr.hpp>
Header <boost/pfr/core.hpp>
Header <boost/pfr/functions_for.hpp>
Header <boost/pfr/functors.hpp>
Header <boost/pfr/io.hpp>
Header <boost/pfr/io_fields.hpp>
Header <boost/pfr/ops.hpp>
Header <boost/pfr/ops_fields.hpp>
Header <boost/pfr/tuple_size.hpp>

Boost.PFR is a C++14 library for a very basic reflection. It gives you access to structure elements by index and provides other std::tuple like methods for user defined types without macro or boilerplate code:

#include <iostream>
#include <string>

#include "boost/pfr.hpp"

struct some_person {
    std::string name;
    unsigned birth_year;
};

int main() {
    some_person val{"Edgar Allan Poe", 1809};

    std::cout << boost::pfr::get<0>(val)                // No macro!
        << " was born in " << boost::pfr::get<1>(val);  // Works with any aggregate initializables!

    std::cout << boost::pfr::io(val);                   // Outputs: {"Edgar Allan Poe", 1809}
}

See limitations.

Usecase example

Imagine that you are writing the wrapper library for a database. Depending on the usage of Boost.PFR users code will look differently:

Without Boost.PFR

With Boost.PFR

#include <db/api.hpp>

struct user_info {
    std::int64_t id;
    std::string name, email, login;
};

user_info retrieve_friend(std::string_view name) {
    std::tuple info_tuple
      = db::one_row_as<std::int64_t, std::string, std::string, std::string>(
        "SELECT id, name, email, login FROM user_infos WHERE name=$0",
        name
    );

    ////////////////////////////////////////////////////////////////////////////////
    user_info info {
        std::move(std::get<0>(info_tuple)),
        std::move(std::get<1>(info_tuple)),
        std::move(std::get<2>(info_tuple)),
        std::move(std::get<3>(info_tuple)),
    }
    ////////////////////////////////////////////////////////////////////////////////

    auto friend_info = ask_user_for_friend(std::move(info));

    db::insert(
        "INSERT INTO user_infos(id, name, email, login) VALUES ($0, $1, $2, $3)",
        std::move(friend_info.id),    //////////////////////////////////////////////
        std::move(friend_info.name),  // Users are forced to move individual fields
        std::move(friend_info.email), // because your library can not iterate over
        std::move(friend_info.login)  // the fields of a user provided structure
    );

    return friend_info;
}
#include <db/api.hpp>

struct user_info {
    std::int64_t id;
    std::string name, email, login;
};

user_info retrieve_friend(std::string_view name) {
    // With Boost.PFR you can put data directly into user provided structures
    user_info info = db::one_row_as<user_info>(
        "SELECT id, name, email, login FROM user_infos WHERE name=$0",
        name
    );

    ////////////////// No boilerplate code to move data around /////////////////////






    ////////////////////////////////////////////////////////////////////////////////

    auto friend_info = ask_user_for_friend(std::move(info));

    db::insert(
        "INSERT INTO user_infos(id, name, email, login) VALUES ($0, $1, $2, $3)",
        friend_info     ////////////////////////////////////////////////////////////
                        // Boost.PFR allows you to iterate over all the fields of a
                        // user provided structure
                        //
    );

    return friend_info;
}

Otherwise your library could require a customization point for a user type:

Without Boost.PFR

With Boost.PFR

#include <db/api.hpp>

struct user_info {
    std::int64_t id;
    std::string name, email, login;
};

/// Customizations via hand-written code or macro like BOOST_FUSION_ADAPT_STRUCT ///
auto db_api_tie(user_info& ui) noexcept {
    return std::tie(ui.id, ui.name, ui.email, ui.login);
}

auto db_api_tie(const user_info& ui) noexcept {
    return std::tie(ui.id, ui.name, ui.email, ui.login);
}
////////////////////////////////////////////////////////////////////////////////////
#include <db/api.hpp>

struct user_info {
    std::int64_t id;
    std::string name, email, login;
};

//////// With Boost.PFR there's no need in hand written customizations /////////////







////////////////////////////////////////////////////////////////////////////////////

With Boost.PFR the code is shorter, more readable and more pleasant to write.

Out of the box functionality

Boost.PFR adds the following out-of-the-box functionality for aggregate initializable structures:

  • comparison functions
  • heterogeneous comparators
  • hash
  • IO streaming
  • access to members by index
  • member type retrieval
  • methods for cooperation with std::tuple
  • methods to visit each field of the structure

Boost.PFR is a header only library that does not depend on Boost. You can just copy the content of the "include" folder from the github into your project, and the library will work fine.

[Caution] Caution

Recommended C++ Standards are C++17 and above. Library requires at least C++14! Pre C++14 compilers (C++11, C++03...) are not supported

Last revised: October 21, 2020 at 07:50:53 GMT


Next