Boost C++ 库

...世界上最受推崇和专业设计的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu,《C++ 编码标准

PrevUpHomeNext

第 26 章。Boost.PFR 2.2

根据 Boost 软件许可协议 1.0 版分发。(请参阅随附文件 LICENSE_1_0.txt 或在 https://boost.ac.cn/LICENSE_1_0.txt 复制)

目录

简介
给心急的人的简短示例
教程
为什么元组不好,聚合更可取?
通过索引访问结构成员
聚合的自定义打印
获取运算符的三种方式
联合体的反射
字段名称的反射
限制和配置
PFR 作为 C++20 模块
它是如何工作的
鸣谢
PFR 的参考部分
头文件 <boost/pfr.hpp>
头文件 <boost/pfr/config.hpp>
头文件 <boost/pfr/core.hpp>
头文件 <boost/pfr/core_name.hpp>
头文件 <boost/pfr/functions_for.hpp>
头文件 <boost/pfr/functors.hpp>
头文件 <boost/pfr/io.hpp>
头文件 <boost/pfr/io_fields.hpp>
头文件 <boost/pfr/ops.hpp>
头文件 <boost/pfr/ops_fields.hpp>
头文件 <boost/pfr/traits.hpp>
头文件 <boost/pfr/traits_fwd.hpp>
头文件 <boost/pfr/tuple_size.hpp>

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 协作的成员名称方法
  • 访问结构体每个字段的方法
  • 用于检测类型潜在反射能力以及在用户端代码中覆盖 trait 决策的能力的 trait

Boost.PFR 是一个仅包含头文件的库,不依赖于 Boost。您可以将 “include” 文件夹的内容 从 Boost.PFR github 复制到您的项目中,该库就可以正常工作。对于没有 boost:: 命名空间的库版本,请参阅 PFR

[Caution] 注意

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


PrevUpHomeNext