HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppCritical

Is it possible to print (the name of) a variable's type in standard C++?

Submitted by: @import:stackoverflow-api··
0
Viewed 0 times
possiblenamestandardtheprintvariabletype

Problem

For example:
int a = 12;
cout

Expected output:
int
`

Solution

C++11 update to a very old question: Print variable type in C++.

There are C++14, 17 below as well.

The accepted (and good) answer is to use typeid(a).name(), where a is a variable name.

Now in C++11 we have decltype(x), which can turn an expression into a type. And decltype() comes with its own set of very interesting rules. For example decltype(a) and decltype((a)) will generally be different types (and for good and understandable reasons once those reasons are exposed).

Will our trusty typeid(a).name() help us explore this brave new world?

No.

But the tool that will is not that complicated. And it is that tool which I am using as an answer to this question. I will compare and contrast this new tool to typeid(a).name(). And this new tool is actually built on top of typeid(a).name().

The fundamental issue:

typeid(a).name()


throws away cv-qualifiers, references, and lvalue/rvalue-ness. For example:

const int ci = 0;
std::cout << typeid(ci).name() << '\n';


For me outputs:

i


and I'm guessing on MSVC outputs:

int


I.e. the const is gone. This is not a QOI (Quality Of Implementation) issue. The standard mandates this behavior.

What I'm recommending below is:

template  std::string type_name();


which would be used like this:

const int ci = 0;
std::cout () << '\n';


and for me outputs:

int const


` I have not tested this on MSVC. But I welcome feedback from those who do.

The C++11 Solution

I am using
__cxa_demangle for non-MSVC platforms as recommend by ipapadop in his answer to demangle types. But on MSVC I'm trusting typeid to demangle names (untested). And this core is wrapped around some simple testing that detects, restores and reports cv-qualifiers and references to the input type.

#include 
#include 
#ifndef _MSC_VER
#   include 
#endif
#include 
#include 
#include 

template 
std::string
type_name()
{
    typedef typename std::remove_reference::type TR;
    std::unique_ptr own
           (
#ifndef _MSC_VER
                abi::__cxa_demangle(typeid(TR).name(), nullptr,
                                           nullptr, nullptr),
#else
                nullptr,
#endif
                std::free
           );
    std::string r = own != nullptr ? own.get() : typeid(TR).name();
    if (std::is_const::value)
        r += " const";
    if (std::is_volatile::value)
        r += " volatile";
    if (std::is_lvalue_reference::value)
        r += "&";
    else if (std::is_rvalue_reference::value)
        r += "&&";
    return r;
}


The Results

With this solution I can do this:

int& foo_lref();
int&& foo_rref();
int foo_value();

int
main()
{
    int i = 0;
    const int ci = 0;
    std::cout () () () () (i)) is " (i))>() (i)) is " (i))>() (i)) is " (i))>() () () () << '\n';
}


and the output is:

decltype(i) is int
decltype((i)) is int&
decltype(ci) is int const
decltype((ci)) is int const&
decltype(static_cast(i)) is int&
decltype(static_cast(i)) is int&&
decltype(static_cast(i)) is int
decltype(foo_lref()) is int&
decltype(foo_rref()) is int&&
decltype(foo_value()) is int


Note (for example) the difference between
decltype(i) and decltype((i)). The former is the type of the declaration of i. The latter is the "type" of the expression i. (expressions never have reference type, but as a convention decltype represents lvalue expressions with lvalue references).

Thus this tool is an excellent vehicle just to learn about
decltype, in addition to exploring and debugging your own code.

In contrast, if I were to build this just on
typeid(a).name(), without adding back lost cv-qualifiers or references, the output would be:

decltype(i) is int
decltype((i)) is int
decltype(ci) is int
decltype((ci)) is int
decltype(static_cast(i)) is int
decltype(static_cast(i)) is int
decltype(static_cast(i)) is int
decltype(foo_lref()) is int
decltype(foo_rref()) is int
decltype(foo_value()) is int


I.e. Every reference and cv-qualifier is stripped off.

C++14 Update

Just when you think you've got a solution to a problem nailed, someone always comes out of nowhere and shows you a much better way. :-)

This answer from Jamboree shows how to get the type name in C++14 at compile time. It is a brilliant solution for a couple reasons:

  • It's at compile time!



  • You get the compiler itself to do the job instead of a library (even a std::lib). This means more accurate results for the latest language features (like lambdas).



Jamboree's answer doesn't quite lay everything out for VS, and I'm tweaking his code a little bit. But since this answer gets a lot of views, take some time to go over there and upvote his answer, without which, this update would never have happened.

``
#include
#include
#include
#include

#ifndef _MSC_VER
# if __cplusplus
CONSTEXPR11_TN static_string(const char(&a)[N]) NOEXCEPT_TN
: p_(a)
, sz_(N-1)
{}

CONSTEXPR11

Code Snippets

typeid(a).name()
const int ci = 0;
std::cout << typeid(ci).name() << '\n';
template <typename T> std::string type_name();
const int ci = 0;
std::cout << type_name<decltype(ci)>() << '\n';
#include <type_traits>
#include <typeinfo>
#ifndef _MSC_VER
#   include <cxxabi.h>
#endif
#include <memory>
#include <string>
#include <cstdlib>

template <class T>
std::string
type_name()
{
    typedef typename std::remove_reference<T>::type TR;
    std::unique_ptr<char, void(*)(void*)> own
           (
#ifndef _MSC_VER
                abi::__cxa_demangle(typeid(TR).name(), nullptr,
                                           nullptr, nullptr),
#else
                nullptr,
#endif
                std::free
           );
    std::string r = own != nullptr ? own.get() : typeid(TR).name();
    if (std::is_const<TR>::value)
        r += " const";
    if (std::is_volatile<TR>::value)
        r += " volatile";
    if (std::is_lvalue_reference<T>::value)
        r += "&";
    else if (std::is_rvalue_reference<T>::value)
        r += "&&";
    return r;
}

Context

Stack Overflow Q#81870, score: 843

Revisions (0)

No revisions yet.