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

4
在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'; });

输出:

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

这个怎么工作的?这篇博客文章解释了如何重新利用聚合初始化来获取字段,但是获取字段名字似乎像是魔法!但是我在最新的三个编译器(最新的Visual C++,gcc 13.2,clang 16)上得到了上面的输出。看着core_name20_static.hpp中的代码,我也没明白多少。

注意,尝试使用get_name 示例代码,目前在Visual C++上可以正常工作,但在clang上构建时会出现与extern const T fake_object相关的错误,但在gcc上使用-fpermissive可以正常工作。 - undefined
1
有了类型和索引,一个书呆子式的试错算法可以在编译时生成相应的成员指针值。将这些值作为非类型模板参数传递给一个返回std::source_location的函数,就可以修剪掉多余的字节并得到编译时常量。 - undefined
1
不是你要找的,但这是原汁原味的第一手资料:https://youtu.be/UlNUNxLtBI0?feature=shared - undefined
1个回答

4
你可能对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
这真是一个很棒的教程。我之前看过使用td::source_location::current() / __PRETTY_FUNCTION__获取类型名称,并且理解了它,但我没有想到可以用相同的技巧来处理NTTP!(我正在使用magic_enum,但还没有深入研究它的实现。谢谢你解释了这一点!)很遗憾我们不能像在fake_object的位置使用std::declval,或者将其扩展到成员函数名称。期待C++26中的反射功能的到来! - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接