C ++ for getting the number of function arguments that will work for lambda and normal functions
I am trying to implement a mechanism that will provide me with some information about the function type of a template argument. The basic idea is to get the number of arguments, the return type, the total sum of the sizes of each argument. I have something for a lambda based on this entry .
template <typename T, typename... Args>
struct sumSizeOfArgs {
enum { totalSize = sizeof(typename std::decay<T>::type) + sumSizeOfArgs<Args...>::totalSize };
};
template<typename T>
struct sumSizeOfArgs<T> {
enum {totalSize = sizeof(typename std::decay<T>::type)};
};
}
template <typename T>
struct function_traits : public function_traits<decltype(&T::operator())>
{};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const>
{
enum { arity = sizeof...(Args) };
typedef ReturnType result_type;
enum { totalSize = sumSizeOfArgs<Args...>::totalSize };
template <size_t i>
struct arg
{
typedef typename std::tuple_element<i, std::tuple<Args...>>::type type;
};
};
However, indeed, this code does not work for normal function types such as
function_traits<void()>
it has no operator (). I would really appreciate any suggestion to make this code for both cases. Thank,
source to share
Slightly improved original detail:
#include <tuple>
#include <type_traits>
template <typename... Args>
struct sumSizeOfArgs
{
static constexpr size_t totalSize = 0;
};
template <typename T, typename... Args>
struct sumSizeOfArgs<T, Args...>
{
static constexpr size_t totalSize = sizeof(typename std::decay<T>::type)
+ sumSizeOfArgs<Args...>::totalSize;
};
template <typename T>
struct sumSizeOfArgs<T>
{
static constexpr size_t totalSize = sizeof(typename std::decay<T>::type);
};
template <typename T>
struct function_traits_impl;
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...)>
{
static constexpr size_t arity = sizeof...(Args);
using result_type = ReturnType;
static constexpr size_t totalSize = sumSizeOfArgs<Args...>::totalSize;
template <size_t i>
struct arg
{
using type = typename std::tuple_element<i, std::tuple<Args...>>::type;
};
};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const>
: function_traits_impl<ReturnType(ClassType::*)(Args...)> {};
This is where the new part begins (partial specialization of feature classes for functions):
template <typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(Args...)>
{
static constexpr size_t arity = sizeof...(Args);
using result_type = ReturnType;
static constexpr size_t totalSize = sumSizeOfArgs<Args...>::totalSize;
template <size_t i>
struct arg
{
using type = typename std::tuple_element<i, std::tuple<Args...>>::type;
};
};
template <typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(*)(Args...)>
: function_traits_impl<ReturnType(Args...)> {};
An important role here (determination of availability operator()
):
template <typename T, typename V = void>
struct function_traits
: function_traits_impl<T> {};
template <typename T>
struct function_traits<T, decltype((void)&T::operator())>
: function_traits_impl<decltype(&T::operator())> {};
Test:
int main()
{
static_assert(function_traits<void()>::arity == 0, "!");
static_assert(function_traits<void(int)>::arity == 1, "!");
static_assert(function_traits<void(*)(int, float)>::arity == 2, "!");
auto lambda = [] (int, float, char) {};
static_assert(function_traits<decltype(lambda)>::arity == 3, "!");
auto mutable_lambda = [] (int, float, char, double) mutable {};
static_assert(function_traits<decltype(mutable_lambda)>::arity == 4, "!");
}
Or even simpler, with one class of traits:
#include <tuple>
template <typename ReturnType, typename... Args>
struct function_traits_defs
{
static constexpr size_t arity = sizeof...(Args);
using result_type = ReturnType;
template <size_t i>
struct arg
{
using type = typename std::tuple_element<i, std::tuple<Args...>>::type;
};
};
template <typename T>
struct function_traits_impl;
template <typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(Args...)>
: function_traits_defs<ReturnType, Args...> {};
template <typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(*)(Args...)>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...)>
: function_traits_defs<ReturnType, Args...> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits_impl<ReturnType(ClassType::*)(Args...) const>
: function_traits_defs<ReturnType, Args...> {};
// + other cv-ref-variations
template <typename T, typename V = void>
struct function_traits
: function_traits_impl<T> {};
template <typename T>
struct function_traits<T, decltype((void)&T::operator())>
: function_traits_impl<decltype(&T::operator())> {};
int main()
{
static_assert(function_traits<void()>::arity == 0, "!");
static_assert(function_traits<void(int)>::arity == 1, "!");
static_assert(function_traits<void(*)(int, float)>::arity == 2, "!");
auto lambda = [] (int, float, char) {};
static_assert(function_traits<decltype(lambda)>::arity == 3, "!");
auto mutable_lambda = [] (int, float, char, double) mutable {};
static_assert(function_traits<decltype(mutable_lambda)>::arity == 4, "!");
struct Functor
{
void operator()(int) {}
};
static_assert(function_traits<Functor>::arity == 1, "!");
}
Note: Unfortunately none of the above solutions will work for generic lambdas, the related discussion on Arity of generic lambda
source to share