使用反射获取类构造函数的类型列表

问题描述 投票:0回答:0

我需要一个模板,它接收一个类作为类型,并从其构造函数返回一个类型列表。我尝试使用 this SO question 的第一个答案,但它只适用于某些类。此标头包含用于以元组形式获取类构造函数的类型列表的模板。

在我的用例中,当我尝试使用 fields_number_ctor 模板时,我最终遇到了 constexpr 扩展限制(默认为 512)和模板实例化深度限制(默认为 900)错误。这是完整的标头实现:

#include <tuple>
#include <utility>

// Based on
// * http://alexpolt.github.io/type-loophole.html
//   https://github.com/alexpolt/luple/blob/master/type-loophole.h
//   by Alexandr Poltavsky, http://alexpolt.github.io
// * https://www.youtube.com/watch?v=UlNUNxLtBI0
//   Better C++14 reflections - Antony Polukhin - Meeting C++ 2018

namespace refl {

// tag<T, N> generates friend declarations and helps with overload resolution.
// There are two types: one with the auto return type, which is the way we read types later.
// The second one is used in the detection of instantiations without which we'd get multiple
// definitions.
template <typename T, int N>
struct tag {
    friend auto loophole(tag<T, N>);
    constexpr friend int cloophole(tag<T, N>);
};

// The definitions of friend functions.
template <typename T, typename U, int N, bool B,
          typename = typename std::enable_if_t<
            !std::is_same_v<
              std::remove_cv_t<std::remove_reference_t<T>>,
              std::remove_cv_t<std::remove_reference_t<U>>>>>
struct fn_def {
    friend auto loophole(tag<T, N>) { return U{}; }
    constexpr friend int cloophole(tag<T, N>) { return 0; }
};

// This specialization is to avoid multiple definition errors.
template <typename T, typename U, int N> struct fn_def<T, U, N, true> {};

// This has a templated conversion operator which in turn triggers instantiations.
// Important point, using sizeof seems to be more reliable. Also default template
// arguments are "cached" (I think). To fix that I provide a U template parameter to
// the ins functions which do the detection using constexpr friend functions and SFINAE.
template <typename T, int N>
struct c_op {
    template <typename U, int M>
    static auto ins(...) -> int;
    template <typename U, int M, int = cloophole(tag<T, M>{})>
    static auto ins(int) -> char;

    template <typename U, int = sizeof(fn_def<T, U, N, sizeof(ins<U, N>(0)) == sizeof(char)>)>
    operator U();
};

// Here we detect the data type field number. The byproduct is instantiations.
// Uses list initialization. Won't work for types with user-provided constructors.
// Since C++17 there is std::is_aggregate which can be added later.
template <typename T, int... Ns>
constexpr int fields_number(...) { return sizeof...(Ns) - 1; }

template <typename T, int... Ns>
constexpr auto fields_number(int) -> decltype(T{c_op<T, Ns>{}...}, 0) {
    return fields_number<T, Ns..., sizeof...(Ns)>(0);
}

// Here is a version of fields_number to handle user-provided ctor.
// NOTE: It finds the first ctor having the shortest unambigious set
//       of parameters.
template <typename T, int... Ns>
constexpr auto fields_number_ctor(int) -> decltype(T(c_op<T, Ns>{}...), 0) {
    return sizeof...(Ns);
}

template <typename T, int... Ns>
constexpr int fields_number_ctor(...) {
    return fields_number_ctor<T, Ns..., sizeof...(Ns)>(0);
}

// This is a helper to turn a ctor into a tuple type.
// Usage is: refl::as_tuple<data_t>
template <typename T, typename U> struct loophole_tuple;

template <typename T, int... Ns>
struct loophole_tuple<T, std::integer_sequence<int, Ns...>> {
    using type = std::tuple<decltype(loophole(tag<T, Ns>{}))...>;
};

template <typename T>
using as_tuple =
    typename loophole_tuple<T, std::make_integer_sequence<int, fields_number_ctor<T>(0)>>::type;

}  // namespace refl

我怎样才能摆脱扩展错误?我也接受关于如何使用另一个开源反射 C++ 库执行相同操作的建议/示例。

c++ templates reflection constexpr type-deduction
© www.soinside.com 2019 - 2024. All rights reserved.