Как перечислить все переменные-члены класса/структуры в С++

Я работаю над каким-то простым отражением для структур С++, где я хочу рекурсивно перебирать все переменные-члены. Приведенный ниже код почти делает то, что я хочу, но мой компилятор соответствует требованиям: «рекурсивный тип или контекст зависимости функции слишком сложный», поступающий из формы aggregate_arity<MemberType>::size(), который основан на Ориентирует реализацию агрегата_аритета.

Пример использования:

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);
    }

Пример

Я использую has_spv_tag, чтобы отметить структуры, которые должны быть отражены. Я не могу дождаться С++ 20 с реальной поддержкой отражения :(

Спасибо за вашу помощь!

Редактировать: я получил его для компиляции и изменил порядок итерации. Теперь возникает другая проблема: constexpr size_t M =aggregate_arity::size() возвращает 0 даже для того же типа, который раньше возвращал правильное значение. Я проверил, что тип фактически одинаков (первый тип структуры B), сравнив хэш с typeid. Как возможно, чтобы этот агрегат возвращал два разных значения для одного и того же типа?

    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


person Fabian    schedule 10.10.2017    source источник
comment
Я не могу дождаться С++ 20 с реальной поддержкой отражения Вы понимаете, что предложение по отражению на самом деле еще не утверждено, верно? Скорее всего, он не войдет в C++20.   -  person Nicol Bolas    schedule 11.10.2017
comment
@NicolBolas плохие новости.   -  person Tomilov Anatoliy    schedule 11.10.2017
comment
@Orient У меня есть код для компиляции, но теперь агрегат_арность возвращает разные значения для одного и того же типа, как это возможно? Никол Болас, о нет, это плохо :(   -  person Fabian    schedule 12.10.2017
comment
Не ответ на вашу проблему, но, вероятно, очень связанный: существует библиотека ранней стадии для отражения без макросов с методами, описанными в записи выступления на конференции.   -  person Julius    schedule 12.10.2017
comment
@Julius, спасибо за ссылку, я проверил: версия до повышения работает только с плоскими структурами. версия boost (если я включаю pfr.hpp) не компилируется на последней версии компилятора msvc. если я просто использую заголовок boost\pfr\precise\tuple_size.hpp для constexpr size_t M = boost::pfr::tuple_size_v<decltype(member)>;, я получаю это приятное сообщение: error C2338: Что-то пошло не так. Пожалуйста, сообщите об этой проблеме на github вместе со структурой, которую вы отражаете.   -  person Fabian    schedule 13.10.2017


Ответы (1)


Антон Полухин указал на проблему: в MemberType осталась ссылка из get(_Struct). Код работает с

MemberType = std::remove_reference_t<std::remove_cv_t<decltype(member)>>;

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<MemberType>::value, чтобы определить, какой элемент является структурой, которую я хочу перечислить. Так же была ошибка с заказом InitStruct<n + 1, N, T>(_Struct);

person Fabian    schedule 15.10.2017