Is it possible to print a variable's type in standard C++?

前端 未结 21 1568
南笙
南笙 2020-11-22 01:41

For example:

int a = 12;
cout << typeof(a) << endl;

Expected output:

int
相关标签:
21条回答
  • 2020-11-22 02:12

    I like Nick's method, A complete form might be this (for all basic data types):

    template <typename T> const char* typeof(T&) { return "unknown"; }    // default
    template<> const char* typeof(int&) { return "int"; }
    template<> const char* typeof(short&) { return "short"; }
    template<> const char* typeof(long&) { return "long"; }
    template<> const char* typeof(unsigned&) { return "unsigned"; }
    template<> const char* typeof(unsigned short&) { return "unsigned short"; }
    template<> const char* typeof(unsigned long&) { return "unsigned long"; }
    template<> const char* typeof(float&) { return "float"; }
    template<> const char* typeof(double&) { return "double"; }
    template<> const char* typeof(long double&) { return "long double"; }
    template<> const char* typeof(std::string&) { return "String"; }
    template<> const char* typeof(char&) { return "char"; }
    template<> const char* typeof(signed char&) { return "signed char"; }
    template<> const char* typeof(unsigned char&) { return "unsigned char"; }
    template<> const char* typeof(char*&) { return "char*"; }
    template<> const char* typeof(signed char*&) { return "signed char*"; }
    template<> const char* typeof(unsigned char*&) { return "unsigned char*"; }
    
    0 讨论(0)
  • 2020-11-22 02:13

    Another take on @康桓瑋's answer (originally ), making less assumptions about the prefix and suffix specifics, and inspired by @Val's answer - but without polluting the global namespace; without any conditions; and hopefully easier to read.

    The popular compilers provide a macro with the current function's signature. Now, functions are templatable; so the signature contains the template arguments. So, the basic approach is: Given a type, be in a function with that type as a template argument.

    Unfortunately, the type name is wrapped in text describing the function, which is different between compilers. For example, with GCC, the signature of template <typename T> int foo() with type double is: int foo() [T = double].

    So, how do you get rid of the wrapper text? @HowardHinnant's solution is the shortest and most "direct": Just use per-compiler magic numbers to remove a prefix and a suffix. But obviously, that's very brittle; and nobody likes magic numbers in their code. Instead, you get the macro value for a type with a known name, you can determine what prefix and suffix constitute the wrapping.

    #include <string_view>
    
    template <typename T> constexpr std::string_view type_name();
    
    template <>
    constexpr std::string_view type_name<void>()
    { return "void"; }
    
    namespace detail {
    
    using type_name_prober = void;
    
    template <typename T>
    constexpr std::string_view wrapped_type_name() 
    {
    #ifdef __clang__
        return __PRETTY_FUNCTION__;
    #elif defined(__GNUC__)
        return __PRETTY_FUNCTION__;
    #elif defined(_MSC_VER)
        return __FUNCSIG__;
    #else
    #error "Unsupported compiler"
    #endif
    }
    
    constexpr std::size_t wrapped_type_name_prefix_length() { 
        return wrapped_type_name<type_name_prober>().find(type_name<type_name_prober>()); 
    }
    
    constexpr std::size_t wrapped_type_name_suffix_length() { 
        return wrapped_type_name<type_name_prober>().length() 
            - wrapped_type_name_prefix_length() 
            - type_name<type_name_prober>().length();
    }
    
    } // namespace detail
    
    template <typename T>
    constexpr std::string_view type_name() {
        constexpr auto wrapped_name = detail::wrapped_type_name<T>();
        constexpr auto prefix_length = detail::wrapped_type_name_prefix_length();
        constexpr auto suffix_length = detail::wrapped_type_name_suffix_length();
        constexpr auto type_name_length = wrapped_name.length() - prefix_length - suffix_length;
        return wrapped_name.substr(prefix_length, type_name_length);
    }
    

    See it on GodBolt. This should be working with MSVC as well.

    0 讨论(0)
  • 2020-11-22 02:14

    You could use a traits class for this. Something like:

    #include <iostream>
    using namespace std;
    
    template <typename T> class type_name {
    public:
        static const char *name;
    };
    
    #define DECLARE_TYPE_NAME(x) template<> const char *type_name<x>::name = #x;
    #define GET_TYPE_NAME(x) (type_name<typeof(x)>::name)
    
    DECLARE_TYPE_NAME(int);
    
    int main()
    {
        int a = 12;
        cout << GET_TYPE_NAME(a) << endl;
    }
    

    The DECLARE_TYPE_NAME define exists to make your life easier in declaring this traits class for all the types you expect to need.

    This might be more useful than the solutions involving typeid because you get to control the output. For example, using typeid for long long on my compiler gives "x".

    0 讨论(0)
  • 2020-11-22 02:16

    Try:

    #include <typeinfo>
    
    // …
    std::cout << typeid(a).name() << '\n';
    

    You might have to activate RTTI in your compiler options for this to work. Additionally, the output of this depends on the compiler. It might be a raw type name or a name mangling symbol or anything in between.

    0 讨论(0)
  • 2020-11-22 02:16

    The other answers involving RTTI (typeid) are probably what you want, as long as:

    • you can afford the memory overhead (which can be considerable with some compilers)
    • the class names your compiler returns are useful

    The alternative, (similar to Greg Hewgill's answer), is to build a compile-time table of traits.

    template <typename T> struct type_as_string;
    
    // declare your Wibble type (probably with definition of Wibble)
    template <>
    struct type_as_string<Wibble>
    {
        static const char* const value = "Wibble";
    };
    

    Be aware that if you wrap the declarations in a macro, you'll have trouble declaring names for template types taking more than one parameter (e.g. std::map), due to the comma.

    To access the name of the type of a variable, all you need is

    template <typename T>
    const char* get_type_as_string(const T&)
    {
        return type_as_string<T>::value;
    }
    
    0 讨论(0)
  • 2020-11-22 02:18

    Very ugly but does the trick if you only want compile time info (e.g. for debugging):

    auto testVar = std::make_tuple(1, 1.0, "abc");
    decltype(testVar)::foo= 1;
    

    Returns:

    Compilation finished with errors:
    source.cpp: In function 'int main()':
    source.cpp:5:19: error: 'foo' is not a member of 'std::tuple<int, double, const char*>'
    
    0 讨论(0)
提交回复
热议问题