...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
Copyright © 2016-2023 Antony Polukhin
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
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} }
Experiment with the sample online. See limitations.
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)", friend_info.id, ////////////////////////////////////////////////////// friend_info.name, // Users are forced to enumerate fields because your friend_info.email, // library can not iterate over the fields of a user friend_info.login // 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 //////////////////////////////////////// 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 ////////// ///////////////////////////////////////////////////////////////////////////////// |
Imagine that you are writing a serialization library. Serialization of user provided structures (and nested structures) with Boost.PFR it is just as simple as:
void Write(Writer& writer, int value); void Write(Writer& writer, std::string_view value); template <typename T> std::enable_if_t<std::is_aggregate_v<T>> Write(Writer& writer, const T& value) { boost::pfr::for_each_field( value, [&writer](const auto& field) { Write(writer, field); }); }
With Boost.PFR the code is shorter, more readable and more pleasant to write.
Note | |
---|---|
All the above examples were inspired by the Boost.PFR usage in 🐙 userver framework. |
Boost.PFR adds the following out-of-the-box functionality for aggregate initializable structures:
std::tuple
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 Boost.PFR github into your project, and the library will work fine.
For a version of the library without boost::
namespace see PFR.
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 |