boost pfr 如何获取结构体的字段名称?

Too*_*one 4 c++ reflection boost c++20

在 Boost 1.84 中(目前正在进行中):

大新功能:字段名称反射。添加了新的 constexpr boost::pfr::get_name<N, T>() 函数,该函数返回 std::string_view,其字段名称位于聚合 T 的索引 N 处。需要 C++20。

使用github 上最新版本的 pfr,您可以编写如下代码

#include <boost/pfr/core.hpp>
#include <boost/pfr/core_name.hpp>

struct S
{
    int i = 1;
    double d = 2;
    std::string s = "three";
};

const S s;
constexpr auto names = boost::pfr::names_as_array<S>();
boost::pfr::for_each_field(
    s,
    [&names](const auto& field, std::size_t idx)
    { std::cout << idx << ": " << names[idx] << " = " << field << '\n'; });
Run Code Online (Sandbox Code Playgroud)

输出:

0: i = 1
1: d = 2
2: s = three
Run Code Online (Sandbox Code Playgroud)

这是如何运作的?这篇博文解释了如何重新利用聚合初始化来获取字段,但获取字段名称似乎很神奇!但我在三大编译器(最新的 Visual C++、gcc 13.2、clang 16)上得到了上述输出。我并没有更明智地查看core_name20_static.hpp中的代码。

Art*_*yer 6

您可能熟悉boost::typeindex::type_id<T>().pretty_name()各种自动“枚举到字符串”。这些使用__PRETTY_FUNCTION__/__FUNCSIG__来获取“美化的”函数名称(其中包括完整写出模板参数)。使用它,我们可以获得模板参数的名称:

template<typename T>
void test() {
    std::cout << __PRETTY_FUNCTION__ << '\n';
}

template<auto V>
void test() {
    std::cout << __PRETTY_FUNCTION__ << '\n';
}

int main() {
    test<std::string>();
    enum { a };
    test<a>();
}
Run Code Online (Sandbox Code Playgroud)
// GCC output
void test() [with T = std::__cxx11::basic_string<char>]
void test() [with auto V = main::a]
Run Code Online (Sandbox Code Playgroud)

您将删除适当的字符以获得您想要的“名称”。

在 C++20 之前,指针/引用非类型模板参数必须指向完整的对象。在 C++20 中,它们现在可以指向子对象。因此,您创建一个对象并指向它:

// GCC output
void test() [with T = std::__cxx11::basic_string<char>]
void test() [with auto V = main::a]
Run Code Online (Sandbox Code Playgroud)
// GCC output
void test() [with auto* P = (& fake_object.S::this_is_the_name_we_want)]
Run Code Online (Sandbox Code Playgroud)

(并且您可以使用与 相同的方法获得对每个成员的引用boost::pfr::for_each_field

  • 这真是一个很棒的教程。我见过 [使用 td::source_location::current() / __PRETTY_FUNCTION__ 获取类型名称](/sf/answers/4514340491/) 并理解它,但我还没有跳到想象使用与 NTTP 相同的技术!(我正在使用 [magic_enum](https://github.com/Neargye/magic_enum),但还没有研究其实现。也感谢您的解释!)遗憾的是我们不能使用像 ` std::declval` 代替 `fake_object`,或将其扩展为成员函数名称。在 C++26 中进行反射! (2认同)