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

How does boost pfr get the names of fields of a struct?

提问人:TooTone 提问时间:11/11/2023 更新时间:11/11/2023 访问量:150

问:

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

大新功能:字段名称反射。添加了新的 constexpr boost::p fr::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'; });

输出:

0: i = 1
1: d = 2
2: s = three

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

C++ 提升 反射 C++20

评论

0赞 TooTone 11/11/2023
请注意,尝试示例代码,目前适用于 Visual C++,无法在 clang 上构建,并出现与 相关的错误,但在 gcc 上工作。get_nameextern const T fake_object-fpermissive
1赞 Red.Wave 11/11/2023
有了类型和索引,书试错算法就可以在编译时生成尊重成员指针值。将这些值作为 NTTP 提供给返回 的函数,可以修剪多余的字节并获取编译时常量。std::source_location
1赞 Red.Wave 11/11/2023
不是你所寻求的,而是这是原始的第一手资料:youtu.be/UlNUNxLtBI0?feature=shared

答:

5赞 Artyer 11/11/2023 #1

您可能熟悉 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>();
}
// GCC output
void test() [with T = std::__cxx11::basic_string<char>]
void test() [with auto V = main::a]

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

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

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

extern S fake_object;

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

int main() {
    test<&fake_object.this_is_the_name_we_want>();
}
// GCC output
void test() [with auto* P = (& fake_object.S::this_is_the_name_we_want)]

(并且您可以使用与boost::pfr::for_each_field)

评论

2赞 TooTone 11/11/2023
这是一个非常精彩的教程。我已经看到使用 td::source_location::current() / PRETTY_FUNCTION 获取类型名称并理解它,但我还没有跳到想象在 NTTP 中使用相同的技术!(我正在使用 magic_enum但尚未研究实现。也感谢您对此的解释!很遗憾,我们不能用类似的东西来代替 ,或者将其扩展到成员函数名称。在 C++26 中滚动反射!std::declvalfake_object