【问题标题】:How to enumerate all member variables of a class / struct in c++如何在c ++中枚举类/结构的所有成员变量
【发布时间】:2017-10-10 20:16:55
【问题描述】:

我正在为 c++ 结构进行某种简单的反射,我想在其中递归迭代所有成员变量。 下面的代码几乎可以满足我的要求,但我的编译器符合:“递归类型或函数依赖上下文太复杂”来自aggregate_arity<MemberType>::size(),它基于Orients aggregate_arity implementation

示例用例:

struct B
{
    SPVStruct;
    var_t<float2_t, true> f4;
};

struct A
{
    SPVStruct;
    var_t<float2_t, true> f2;
    var_t<float3_t, true> f3;
    float d;

    B b;
};

A a{};
InitializeStruct<A, true>(a);

实施:

struct TSPVStructTag {};

#ifndef SPVStruct
#define SPVStruct typedef TSPVStructTag SPVStructTag;
#endif

    template< class, class = std::void_t<> >
    struct has_spv_tag : std::false_type { };

    template< class T >
    struct has_spv_tag<T, std::void_t<typename T::SPVStructTag>> : std::true_type { };

    template <class T>
    void InitVar(T& _Member) {}

    template <class T, bool Assemble>
    void InitVar(var_t<T, Assemble>& _Member)
    {
        // actual stuff happening here
    }

    template <size_t N, class T, bool Assemble>
    void InitStruct(T& _Struct)
    {
        if constexpr(N > 0u)
        {
            auto& member = get<N-1>(_Struct);
            using MemberType = typename std::decay_t<decltype(member)>;
            if constexpr(has_spv_tag<MemberType>::value)
            {
                constexpr size_t n = aggregate_arity<MemberType>::size(); // this is the complex recursion that blows up
                InitStruct<n, MemberType, Assemble>(member);                
            }
            else
            {
                InitVar(member);
                InitStruct<N - 1, T, Assemble>(_Struct);
            }
        }
    }

    template <class T, bool Assemble>
    void InitializeStruct(T& _Struct)
    {
        constexpr size_t N = aggregate_arity<T>::size();
        InitStruct<N, T, Assemble>(_Struct);
    }

Example

我使用 has_spv_tag 来标记应该反映的结构。我等不及 c++20 支持实际的反射了 :(

感谢您的帮助!

编辑: 我让它编译并更改了迭代顺序。现在出现了一个不同的问题: constexpr size_t M = aggregate_arity::size() 返回 0 即使对于它之前返回正确值的相同类型。我通过比较来自 typeid 的哈希来验证类型实际上是相同的(第一个结构类型 B)。该聚合如何为完全相同的类型返回两个不同的值?

    template <class T, bool Assemble>
    constexpr bool is_var_t(var_t<T, Assemble>& _Member) { return true; }

    template <class T>
    constexpr bool is_var_t(T& _Member) { return false; }

    template <class T>
    void InitVar(T& _Member) { std::cout << typeid(T).name() << std::endl; }

    template <class T, bool Assemble>
    void InitVar(var_t<T, Assemble>& _Member)
    {
        // actual stuff happening here
        std::cout << typeid(T).name() << std::endl;
    }

    template <size_t n, size_t N, class T>
    void InitStruct(T& _Struct)
    {
        std::cout << "n " << n << " N " << N << std::endl;
        if constexpr(n < N)
        {
            decltype(auto) member = get<n>(_Struct);
            using MemberType = std::remove_cv_t<decltype(member)>;
            std::cout << typeid(MemberType).hash_code() << std::endl;

            if (is_var_t(member))
            {
                InitVar(member);
                InitStruct<n + 1, N, T>(_Struct);
            }
            else
            {
                constexpr size_t M = aggregate_arity<MemberType>::size();
                InitStruct<0, M, MemberType>(member);
            }
        }
    }

编辑 2: 新版本示例:http://coliru.stacked-crooked.com/a/b25a84454d53d8de

【问题讨论】:

  • "我已经等不及 c++20 实际支持反射了" 你知道反射提案实际上还没有被批准,对吧?它可能不会进入 C++20。
  • @NicolBolas 坏消息。
  • @Orient 我得到了要编译的代码,但是现在 aggregate_arity 返回相同类型的不同值,这怎么可能?尼可波拉斯,哦,不,那不好:(
  • 不是您问题的答案,但可能高度相关:存在一个早期阶段的library for reflection without macros,其技术在conference talk recording 中进行了解释。
  • @Julius 感谢您的链接,我检查了一下:pre boost 版本似乎只适用于扁平结构。 boost 版本(如果我包含 pfr.hpp)不能在最新的 msvc 编译器版本上编译。如果我只是为constexpr size_t M = boost::pfr::tuple_size_v&lt;decltype(member)&gt;; 使用 boost\pfr\precise\tuple_size.hpp 标头,我会收到一条很好的消息:“错误 C2338:出了点问题。请将此问题连同您反映的结构一起报告给 github。”

标签: struct enumeration member c++17


【解决方案1】:

Antony Polukhin 指出了问题:MemberType 仍然有来自 get(_Struct) 的引用。该代码适用于

MemberType = std::remove_reference_t&lt;std::remove_cv_t&lt;decltype(member)&gt;&gt;;

template <size_t n, size_t N, class T>
void InitStruct(T& _Struct)
{
    if constexpr(n < N)
    {
        decltype(auto) member = get<n>(_Struct);
        using MemberType = std::remove_reference_t<std::remove_cv_t<decltype(member)>>;

        if constexpr(has_spv_tag<MemberType>::value)
        {
            InitStruct<0, aggregate_arity<MemberType>::size(), MemberType>(member);
        }
        else
        {
            InitVar(member);
        }
        InitStruct<n + 1, N, T>(_Struct);
    }
}

我现在使用has_spv_tag&lt;MemberType&gt;::value 来识别哪个成员是我要枚举的结构。还有InitStruct&lt;n + 1, N, T&gt;(_Struct);顺序的bug

【讨论】:

    猜你喜欢
    • 2019-03-02
    • 2021-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-28
    • 1970-01-01
    • 2014-05-22
    相关资源
    最近更新 更多