获取类中的字段数

时间:2017-08-22 14:27:17

标签: c++ c++11 templates template-meta-programming typetraits

有没有办法获得一个类的字段数?

SELECT 
    a.KeyField, a.AlphaValue AS Compostable 
INTO 
    #DAT
FROM 
    [SysproCompanyA].[dbo].AdmFormData a
WHERE 
    a.FieldName = 'DAT001'

SELECT 
    b.KeyField, b.AlphaValue AS Trial 
INTO 
    #PAS
FROM 
    [SysproCompanyA].[dbo].AdmFormData b
WHERE 
    b.FieldName = 'PAS001'

SELECT 
    c.KeyField AS JobNumber, c.Compostable, d.Trial
FROM 
    #DAT c 
INNER JOIN 
    #PAS d ON c.KeyField = d.KeyField
WHERE 
    c.KeyField = '00170579'

DROP TABLE #DAT
DROP TABLE #PAS

我已经找到了this question但它已经过时了 - 我希望可以用C ++ 14/17拼接一些东西(毕竟我们现在有类似magic_get的东西 - 也许它的一些子集......?)

编辑: - 编译器挂钩也可以工作,即使它仅适用于MSVC或GCC或Clang - 我使用全部3。

3 个答案:

答案 0 :(得分:7)

你不能这样做(开箱即用),因为C ++中还没有反映(还)。您需要探索其他选项,例如第三方库。

答案 1 :(得分:5)

事实上,Antony Polukhin向我们展示了C ++ 有(某些)反射,因为C ++ 14,而不知道它;并且您可以提取有关字段的信息。 ......好吧,至少对于普通的数据结构/类。观看他的CppCon 2016演讲:

C++14 Reflections Without Macros, Markup nor External Tooling / Antony Polukhin

然后你写道:

template <class T, std::size_t I0, std::size_t... I>
constexpr auto detect_fields_count(std::size_t& out, std::index_sequence<I0, I...>)
    -> decltype( T{ ubiq_constructor<I0>{}, ubiq_constructor<I>{}... } )
{ out = sizeof...(I) + 1;      /*...*/ }

template <class T, std::size_t... I>
constexpr void detect_fields_count(std::size_t& out, std::index_sequence<I...>) {
    detect_fields_count<T>(out, std::make_index_sequence<sizeof...(I) - 1>{});
}

可以获得字段数。当然,您还需要巧妙构思的ubiq结构。您实际需要使用的是这两个文件:

https://github.com/apolukhin/magic_get/blob/develop/include/boost/pfr/detail/config.hpp
https://github.com/apolukhin/magic_get/blob/develop/include/boost/pfr/detail/fields_count.hpp

这应该足够了。

答案 2 :(得分:0)

这是einpoklum的答案的修改版本:

template <size_t I>
struct ubiq_constructor
{
    template <typename Type>
    constexpr operator Type&() const noexcept
    {
        return Type(*this);
    }
};

template <size_t fields, class POD>
struct fields_count
{
    constexpr static size_t count = fields;
    typedef POD type;
};


// why use size_t& in the constexpr function in the first place?
template <class T, size_t I0, size_t ... I>
constexpr auto fields_total(const std::index_sequence<I0, I...>&)
    -> fields_count<sizeof...(I) + 1, decltype(T{ ubiq_constructor<I0>(), ubiq_constructor<I>()...})>
{
    return fields_count<sizeof...(I) + 1, decltype(T{ ubiq_constructor<I0>(), ubiq_constructor<I>()... })>();
}

template <class T, size_t ... I>
constexpr auto fields_total(const std::index_sequence<I...>&)
{
    return fields_total<T>(std::make_index_sequence<sizeof...(I) - 1>());
}

//use this for convinience to return number of fields at compile time 
template <class T>
constexpr size_t fields_total(const T&)
{
    auto counted = fields_total<T>(std::make_index_sequence<sizeof(T) / sizeof(char)>());
    return decltype(counted)::count;
}

另外,在CppCon 2016视频中提到的获取文件类型的方法在我看来似乎相当困难,而且据我所知,这取决于BOOST库。

因此,这是获取提供的类的类型的元组的完整代码(假设它没有私有或受保护的非静态数据成员,用户定义/提供的构造函数等。请参见aggregate initialization以供参考)

#include <tuple>

template <size_t I>
struct ubiq_constructor
{
    template <typename Type>
    constexpr operator Type&() const noexcept
    {
        return Type(*this);
    }
};

template <size_t fields, class POD>
struct fields_count
{
    constexpr static size_t count = fields;
    typedef POD type;
};

template <class T, size_t I0, size_t ... I>
constexpr auto fields_total(const std::index_sequence<I0, I...>&)
    -> fields_count<sizeof...(I) + 1, decltype(T{ ubiq_constructor<I0>(), ubiq_constructor<I>()...})>
{
    return fields_count<sizeof...(I) + 1, decltype(T{ ubiq_constructor<I0>(), ubiq_constructor<I>()... })>();
}

template <class T, size_t ... I>
constexpr auto fields_total(const std::index_sequence<I...>&)
{
    return fields_total<T>(std::make_index_sequence<sizeof...(I) - 1>());
}

template <class T>
constexpr size_t fields_total(const T&)
{
    auto counted = fields_total<T>(std::make_index_sequence<sizeof(T) / sizeof(char)>());
    return decltype(counted)::count;
}

template <class allowed>
struct ubiq_explicit
{
    template <class other>
    constexpr operator other&() = delete;
    constexpr operator allowed&() noexcept;
};


template <class owner, class field>
struct owner_field
{
    typedef field type;
};

template <class T, class cur, template <class ...> class pack, class ... prev>
constexpr auto guess_field(const T&, const cur&, const pack<prev...>&) noexcept
->owner_field<decltype(T{ prev() ... , ubiq_explicit<cur>() }), cur >
{
    return owner_field<decltype(T{ prev() ... , ubiq_explicit<cur>() }), cur > ();
}

// if substitution fails
template <class T, class cur, class pack>
constexpr auto guess_field(const T&, const cur&, const pack&) noexcept
{
    return owner_field<T, void>();
}

template <class T, class cur, class pack>
class is_field
{
    using interm = decltype(guess_field(T(), cur(), pack()));

public:
    constexpr static bool value = !std::is_same_v<owner_field<T, void>, interm>;
};


//function to detect if a given class at given position is same as an original field
template <class T, class cur /*type at current position*/, class pack /*previous types*/>
constexpr bool is_field_v(const T&, const cur&, const pack&) noexcept
{
    if constexpr (std::is_same_v<owner_field<T, void>, decltype(guess_field(T(), cur(), pack()))>)
        return false;
    else return true;
}


#include <string>
typedef std::tuple<bool,
    char, unsigned char,
    short, unsigned short,
    int, unsigned int,
    long, unsigned long,
    long long, unsigned long long,
    float, double, long double, long double
    /* std::string will not compile as not constexpr*/> pod_map;


//traverse given map!
template <size_t indx>
using pod_elem = std::remove_reference_t<decltype(std::get<indx>(pod_map()))>;

template <template <class ... > class pack, class ... args>
constexpr size_t args_count(const pack<args...>&) noexcept
{
    return sizeof...(args);
}

template <size_t curr_iter, class T, class prev_pack, template <class ...> class types_map, class ... types>
constexpr auto traverse_map(T, prev_pack, types_map<types...>) noexcept
{
    static_assert(curr_iter < args_count(types_map<types...>()), "Failed to traverse types map! Check if type is registered in the map!");
    if constexpr (!is_field_v(T(), pod_elem<curr_iter>(), prev_pack()))
        return traverse_map<curr_iter + 1>(T(), prev_pack(), types_map<types...>());
    else return owner_field<T, pod_elem<curr_iter>>();
}

template <class T, class t_map, class ... prev_fields>
constexpr auto get_fields(T, t_map, prev_fields...)
{
    //guess cur_field
    auto found = traverse_map<0>(T(), std::tuple<prev_fields...>(), t_map());

    if constexpr (sizeof...(prev_fields) < fields_total(T()) - 1)
        return get_fields(T(), t_map(), prev_fields()..., decltype(found)::type());

    //bottom level
    else
        return std::make_tuple(prev_fields()..., decltype(found)::type());
}


struct abc
{
    int a;
    float b;
    char c, d;
    int e;
    double f;

};



int main(int argc, char** argv)
{
//checking that intermediate functions work
    ubiq_explicit<int> ub_int;
    //abc{ ubiq_explicit<int>() };
    //abc{ ubiq_explicit<double>() }; will not compile!

    is_field_v(abc(), int(), std::tuple<>()); // works, valid
    is_field_v(abc(), float(), std::tuple<int>()); // works, valid
    is_field_v(abc(), char(), std::tuple<int, float>()); // works, valid
    is_field_v(abc(), double(), std::tuple<int, float>()); // invalid

    traverse_map<0>(abc(), std::tuple<>(), pod_map());
    traverse_map<0>(abc(), std::tuple<int>(), pod_map());
    traverse_map<0>(abc(), std::tuple<int, float>(), pod_map());
   // traverse_map<0>(abc(), std::tuple<int, float, char>(), pod_map()); //will not compile

    fields_total<abc>(std::make_index_sequence<10>());
    fields_total(abc());


    // decltype using aggregate initialization with explicit conversion of the arguments
    owner_field<decltype(abc{ ubiq_explicit<int>() }), int > f0{};
    owner_field<decltype(abc{ int(), ubiq_explicit<float>() }), float > f1{};
    owner_field<decltype(abc{ int(), float(), ubiq_explicit<char>() }), char > f2{};

    auto igi0 = guess_field(abc(), int(), std::tuple<>());
    auto igi1 = guess_field(abc(), float(), std::tuple<int>());
    auto igi2 = guess_field(abc(), char(), std::tuple<int, float>());
    auto igi99 = guess_field(abc(), double(), std::tuple<int, float>());

//returns tuple
    get_fields(abc(), pod_map());

    return 0;
}