Slightly improved original part:
#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...)> {};
New part starts here (traits' class partial specialization 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...)> {};
Crucial part is here (determining the presence of 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, "!");
}
DEMO 1
Or even simpler, with a single trait class:
#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, "!");
}
DEMO 2
Note: Unfortunately none of the above solutions will work for generic lambdas, related discussion is Arity of a generic lambda