使用 boost::pfr 获取字段名称

flu*_*ter 3 c++ boost template-meta-programming c++17

您好,我用于boost::pfr基本反射,它工作正常,但问题是它仅打印或处理字段值,就像boost::pfr::io它打印结构的每个成员一样,但如何将其打印为名称值对,相同问题是for_each_field,函子只接受值,但不接受名称。如何获取字段名称?

struct S {
    int n;
    std::string name;
};
S o{1, "foo"};
std::cout << boost::pfr::io(o);
// Outputs: {1, "foo"}, how can I get n = 1, name = "foo"?
Run Code Online (Sandbox Code Playgroud)

seh*_*ehe 5

如果您认为调整结构并不太具有侵入性(它不会更改您现有的定义,您甚至不需要将其放在公共标头中):

BOOST_FUSION_ADAPT_STRUCT(S, n, name)
Run Code Online (Sandbox Code Playgroud)

然后你可以operator<<为序列编写一个通用的序列:

namespace BF = boost::fusion;

template <typename T,
          typename Enable = std::enable_if_t<
              // BF::traits::is_sequence<T>::type::value>
              std::is_same_v<BF::struct_tag, typename BF::traits::tag_of<T>::type>>>
std::ostream& operator<<(std::ostream& os, T const& v)
{
    bool first = true;
    auto visitor = [&]<size_t I>() {
        os << (std::exchange(first, false) ? "" : ", ")
           << BF::extension::struct_member_name<T, I>::call()
           << " = " << BF::at_c<I>(v);
    };

    // visit members
    [&]<size_t... II>(std::index_sequence<II...>)
    {
        return ((visitor.template operator()<II>(), ...);
    }
    (std::make_index_sequence<BF::result_of::size<T>::type::value>{});
    return os;
}
Run Code Online (Sandbox Code Playgroud)

(在 c++20 之前,这需要一些显式模板类型而不是 lambda,也许会使其更具可读性。我想我很懒......)

这是一个现场演示:Live On Compiler Explorer

n = 1, name = foo
Run Code Online (Sandbox Code Playgroud)

奖励:正确引用类似字符串的类型

实时编译器资源管理器

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <boost/fusion/include/at_c.hpp>
#include <iostream>
#include <iomanip>

namespace MyLib {
    struct S {
        int         n;
        std::string name;
    };

    namespace BF = boost::fusion;

    static auto inline pretty(std::string_view sv) { return std::quoted(sv); }

    template <typename T,
            typename Enable = std::enable_if_t<
                not std::is_constructible_v<std::string_view, T const&>>>
    static inline T const& pretty(T const& v)
    {
        return v;
    }

    template <typename T,
            typename Enable = std::enable_if_t<
                // BF::traits::is_sequence<T>::type::value>
                std::is_same_v<BF::struct_tag, typename BF::traits::tag_of<T>::type>>>
    std::ostream& operator<<(std::ostream& os, T const& v)
    {
        bool first = true;
        auto visitor = [&]<size_t I>() {
            os << (std::exchange(first, false) ? "" : ", ")
            << BF::extension::struct_member_name<T, I>::call()
            << " = " << pretty(BF::at_c<I>(v));
        };

        // visit members
        [&]<size_t... II>(std::index_sequence<II...>)
        {
            return (visitor.template operator()<II>(), ...);
        }
        (std::make_index_sequence<BF::result_of::size<T>::type::value>{});
        return os;
    }
} // namespace MyLib

BOOST_FUSION_ADAPT_STRUCT(MyLib::S, n, name)

int main()
{
    MyLib::S o{1, "foo"};
    std::cout << o << "\n";
}
Run Code Online (Sandbox Code Playgroud)

输出:

n = 1, name = "foo"
Run Code Online (Sandbox Code Playgroud)