#include <string>
#include <typeinfo>

#ifdef __GNUG__

    #include <cxxabi.h>
    #include <cstdlib>
    #include <memory>

    template< typename T > std::string type_name()
    {
        int status ;
        std::unique_ptr< char[], decltype(&std::free) > buffer(
            __cxxabiv1::__cxa_demangle( typeid(T).name(), nullptr, 0, &status ), &std::free ) ;
        return status==0 ? buffer.get() : "__cxa_demangle error" ;
    }

#else // !defined __GNUG__

    template< typename T > std::string type_name() { return typeid(T).name() ; }

#endif //__GNUG__

template< typename T > std::string type_name( const T& ) { return type_name<T>() ; }


#define print_type_name(var) ( std::cout << #var << " is of type " << type_name(var) << "\n\n" )

template<class T> class App{
    public:
        T var;
        App(T arg) :var(arg){}

};

class Test{};

#include <iostream>
#include <vector>
#include <cstring>

int main(){
    print_type_name("string");
    print_type_name('b');
    print_type_name(1.1);
    print_type_name(1);

    App<std::string> obj("obj_str");
    print_type_name(obj.var);

    App<int> obj2(1);
    print_type_name(obj2.var);

    Test o;
    App<Test> obj3(o);
    print_type_name(obj3.var);
    print_type_name(obj3);

    std::vector<int> vector ;
    print_type_name(vector);
    print_type_name(vector.rbegin());

    print_type_name(std::strcat);
    print_type_name( &std::strcat );
}
