This will not compile function_type_deducer([](){}).describe_me();
It would work if function_type_deducer not a template. :) Lacking lambdas (empty [] ) are implicitly converted to function pointers. Unfortunately, implicit conversions are not taken into account when outputting some template arguments. See this question for more information (note that my answer is not entirely correct, as the comments indicate).
He does not notice that there is a slight difference between x and y, since y takes the string &, where x takes the string.
This is not a function problem, a typeid , as this simple test code shows:
template<class T> void x(void(T)){ T v; (void)v; } void f1(int){} void f2(int&){} int main(){ x(f1); x(f2); }
Live example on Ideone . Exit:
error: 'v declared as reference but not initialized
A simple fix would be to use tag dispatch:
#include <type_traits> // is_reference #include <iostream> #include <typeinfo> template<class T> void print_name(std::true_type){ std::cout << "reference to " << typeid(T).name(); } template<class T> void print_name(std::false_type){ std::cout << typeid(T).name(); } template<class T> void print_name(){ print_name(typename std::is_reference<T>::type()); }
And call print_name<NextArg>() instead of typeid(NextArg).name() .
Did I reinvent the wheel?
Yes, it seems and no, you didn’t. Boost.Function provides a typedef for all arguments ( argN_type style), as well as a static constant arity for its number. However, you cannot easily access these typedefs in general. You will need a workaround in order not to accidentally gain access to nonexistent ones. The idea of tuple works best, but you can write it beautifully. Here's a modified version of what I once wrote:
#include <tuple> #include <type_traits> #include <iostream> #include <typeinfo> namespace detail{ template<class T> std::ostream& print_name(std::ostream& os); template<class T> std::ostream& print_pointer(std::ostream& os, std::true_type){ typedef typename std::remove_pointer<T>:: type np_type; os << "pointer to "; return print_name<np_type>(os); } template<class T> std::ostream& print_pointer(std::ostream& os, std::false_type){ return os << typeid(T).name(); } template<class T> std::ostream& print_name(std::ostream& os, std::true_type){ return os << "reference to " << typeid(T).name(); } template<class T> std::ostream& print_name(std::ostream& os, std::false_type){ return print_pointer<T>(os, typename std::is_pointer<T>::type()); } template<class T> std::ostream& print_name(std::ostream& os){ return print_name<T>(os, typename std::is_reference<T>::type()); } // to workaround partial function specialization template<unsigned> struct int2type{}; template<class Tuple, unsigned I> std::ostream& print_types(std::ostream& os, int2type<I>){ typedef typename std::tuple_element<I,Tuple>::type type; print_types<Tuple>(os, int2type<I-1>()); // left-folding os << ", "; return print_name<type>(os); } template<class Tuple> std::ostream& print_types(std::ostream& os, int2type<0>){ typedef typename std::tuple_element<0,Tuple>::type type; return print_name<type>(os); } } // detail:: template<class R, class... Args> struct function_info{ typedef R result_type; typedef std::tuple<Args...> argument_tuple; static unsigned const arity = sizeof...(Args); void describe_me(std::ostream& os = std::cout) const{ using namespace detail; os << "I return '"; print_name<result_type>(os); os << "' and I take '" << arity << "' arguments. They are: \n\t'"; print_types<argument_tuple>(os, int2type<arity-1>()) << "'\n"; } };
Live example on Ideone . Exit:
main: I return 'i' and I take '2' arguments. They are: 'i, pointer to pointer to c' x: I return 'Ss' and I take '3' arguments. They are: 'i, Ss, c' y: I return 'Ss' and I take '3' arguments. They are: 'i, reference to Ss, c'