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