Boost C++ 库

世界上评价最高、设计最精良的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu, C++ Coding Standards

第 26 章。Boost.PFR 2.3 - Boost C++ 函数库
PrevUpHomeNext

第 26 章。Boost.PFR 2.3

根据 Boost 软件许可证版本 1.0 发布。(参见随附文件 LICENSE_1_0.txt 或在 https://boost.ac.cn/LICENSE_1_0.txt 复制)

Boost.PFR 是一个 C++14 库,用于非常基本的反射。它允许您通过索引访问结构元素,并为用户定义的类型提供其他类似 std::tuple 的方法,而无需宏或样板代码。

#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}
}

尝试在线 示例。请参阅 限制

用例示例

假设您正在为数据库编写包装库。根据 Boost.PFR 的用法,用户代码看起来会不同。

没有 Boost.PFR

使用 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;
}

否则,您的库可能需要为用户类型提供一个自定义点。

没有 Boost.PFR

使用 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 //////////







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

假设您正在编写一个序列化库。使用 Boost.PFR 序列化用户提供的结构(和嵌套结构)就像以下一样简单:

void Write(Writer& writer, int value);
void Write(Writer& writer, std::string_view value);

template <typename T>
std::enable_if_t<boost::pfr::is_implicitly_reflectable_v<T>> Write(Writer& writer, const T& value) {
  boost::pfr::for_each_field(
      value, [&writer](const auto& field) { Write(writer, field); });
}

使用 Boost.PFR,代码更短、更易读、更令人愉悦。

[Note] 注意

以上所有示例都受到 🐙 userver 框架 中 Boost.PFR 用法的启发。

开箱即用的功能

Boost.PFR 为可聚合初始化的结构提供了以下开箱即用的功能:

  • 比较函数
  • 异构比较器
  • 哈希
  • IO 流
  • 通过索引或类型访问成员
  • 通过索引访问成员名称
  • 成员类型检索
  • 用于成员与 std::tuple 协作的方法
  • 用于成员名称与 std::array 协作的方法
  • 访问结构中每个字段的方法
  • 用于检测类型反射能力的特性,以及在用户端代码中覆盖特性决定的能力

Boost.PFR 是一个仅标头库,不依赖于 Boost。您可以直接将 Boost.PFR github 的 "include" 文件夹内容 复制到您的项目中,该库即可正常工作。要获取不带 boost:: 命名空间的版本,请参阅 PFR

[Caution] 注意

推荐使用的 C++ 标准是 C++20 及更高版本。对于不想访问结构成员名称的用户来说,C++17 完全足够。该库至少需要 C++14!不支持 C++14 之前的编译器(C++11、C++03...)。


PrevUpHomeNext