Search code examples
c++templatestuples

Deduce constructor argument types for a class template in C++?


I need to write a template function in C++ that extracts the types of a class's constructor arguments and returns them as a std::tuple. Here's what I'm trying to achieve:

#include <tuple>
#include <type_traits>

template <class T>
struct get_tuple
{
    // return the types of T's constructor arguments as a tuple
    // using type = ...
};

struct Test
{
    Test(int, double) {}
};

static_assert(std::is_same_v<typename get_tuple<Test>::type, std::tuple<int, double>>);

I tried the following code, but it failed because we cannot obtain a function pointer to the constructor.

template <class T, class... Args>
std::tuple<Args...> get_tuple_impl(T (*)(Args...));

template <class T>
struct get_tuple
{
    using type = decltype(get_tuple_impl(&T::T));
};

What I want to achieve is a factory that creates instances of T based on parameters provided in a config. like

template<class T>
T construct()
{
    typename get_tuple<T>::type args;
    // fill args with config
    return std::make_from_tuple<T>(args);
}

Solution

  • Thanks to @HolyBlackCat. After studying boost.fpr, I finally worked it out

    #include <tuple>
    #include <array>
    
    struct ubiq_constructor
    {
        std::size_t ignore;
    
        template <class T>
        constexpr operator T() const { return T{}; }
    };
    
    template <class T, std::size_t I0, std::size_t... I>
    constexpr auto arg_count(std::size_t &out, std::index_sequence<I0, I...>)
        -> typename std::add_pointer<decltype(T{ubiq_constructor{I0}, ubiq_constructor{I}...})>::type
    {
        out = sizeof...(I) + 1;
        return nullptr;
    }
    
    template <class T, std::size_t... I>
    constexpr void arg_count(std::size_t &out, std::index_sequence<I...>)
    {
        arg_count<T>(out, std::make_index_sequence<sizeof...(I) - 1>{});
    }
    
    template <class T>
    constexpr std::size_t arg_count()
    {
        std::size_t count{};
        arg_count<T>(count, std::make_index_sequence<10>());
        return count;
    }
    
    //
    template <class T>
    struct identity
    {
        typedef T type;
    };
    
    template <std::size_t Index>
    using size_t_ = std::integral_constant<std::size_t, Index>;
    
    #define REGISTER_TYPE(Type, Index)                   \
        constexpr std::size_t type_to_id(identity<Type>) \
        {                                                \
            return Index;                                \
        }                                                \
        constexpr Type id_to_type(size_t_<Index>)        \
        {                                                \
            return Type{};                               \
        }
    
    REGISTER_TYPE(double, 1)
    REGISTER_TYPE(int, 2)
    
    template <std::size_t I>
    struct ubiq_val
    {
        std::size_t *ref_;
    
        template <class Type>
        constexpr operator Type() const noexcept
        {
            ref_[I] = type_to_id(identity<Type>{});
            return Type{};
        }
    };
    
    template <class T, std::size_t... I>
    constexpr auto get_type_ids(std::index_sequence<I...>)
    {
        std::array<std::size_t, sizeof...(I)> types{};
        T tmp{ubiq_val<I>{types.data()}...};
        (void)tmp;
    
        return types;
    }
    
    template <class T, std::size_t... I>
    constexpr auto get_tuple_impl(std::index_sequence<I...> seq)
    {
        constexpr auto types = get_type_ids<T>(seq);
        return std::tuple<decltype(id_to_type(size_t_<types[I]>{}))...>{};
    }
    
    template <class T>
    constexpr auto get_tuple()
    {
        constexpr std::size_t count = arg_count<T>();
        return get_tuple_impl<T>(std::make_index_sequence<count>{});
    }
    
    struct Test
    {
        constexpr Test(int, double) {}
    };
    
    int main()
    {
        static_assert(std::is_same_v<decltype(get_tuple<Test>()), std::tuple<int, double>>);
        return 0;
    }
    

    However, it requires the constructor of Test to be constexpr.