#include <iostream>

#define MAKE_TYPE_NAME(type)                        \
    template <>                                     \
    struct typename_of<type> {                      \
        constexpr static const char* value = #type; \
    }

//
template < typename >
struct typename_of;

MAKE_TYPE_NAME( int );
MAKE_TYPE_NAME( double );
MAKE_TYPE_NAME( int(double) );
MAKE_TYPE_NAME( int(int,double) );
MAKE_TYPE_NAME( int(*)(double) );
MAKE_TYPE_NAME( int[10] );
MAKE_TYPE_NAME( int(*)[10] );
MAKE_TYPE_NAME( int(*[10])(double) );


//
template < typename T >
void foo()
{
    std::cout << typename_of<T>::value << std::endl;
}


int main() { 
    
    foo<int>();
    foo<double>();
    
    foo<int(double)>();
    foo<int(int,double)>();
    foo<int(*)(double)>();
    
    foo<int[10]>();
    foo<int(*)[10]>();
    
    foo<int(*[10])(double)>();
}
