Is it possible to print a variable's type in standard C++? -
for example:
int = 12; cout << typeof(a) << endl;
expected output:
int
c++11 update old question: print variable type in c++.
the accepted (and good) answer use typeid(a).name()
, a
variable name.
now in c++11 have decltype(x)
, can turn expression type. , decltype()
comes own set of interesting rules. example decltype(a)
, decltype((a))
different types (and , understandable reasons once reasons exposed).
will our trusty typeid(a).name()
explore brave new world?
no.
but tool not complicated. , tool using answer question. compare , contrast new tool typeid(a).name()
. , new tool built on top of typeid(a).name()
.
the fundamental issue:
typeid(a).name()
throws away cv-qualifiers, references, , lvalue/rvalue-ness. 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. const
gone. not qoi (quality of implementation) issue. standard mandates behavior.
what i'm recommending below is:
template <typename t> std::string type_name();
which used this:
const int ci = 0; std::cout << type_name<decltype(ci)>() << '\n';
and me outputs:
int const
<disclaimer>
have not tested on msvc. </disclaimer>
welcome feedback do.
the c++11 solution
i using __cxa_demangle
non-msvc platforms recommend ipapadop in answer demangle types. on msvc i'm trusting typeid
demangle names (untested). , core wrapped around simple testing detects, restores , reports cv-qualifiers , references input type.
#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; }
the results
with solution can this:
int& foo_lref(); int&& foo_rref(); int foo_value(); int main() { int = 0; const int ci = 0; std::cout << "decltype(i) " << type_name<decltype(i)>() << '\n'; std::cout << "decltype((i)) " << type_name<decltype((i))>() << '\n'; std::cout << "decltype(ci) " << type_name<decltype(ci)>() << '\n'; std::cout << "decltype((ci)) " << type_name<decltype((ci))>() << '\n'; std::cout << "decltype(static_cast<int&>(i)) " << type_name<decltype(static_cast<int&>(i))>() << '\n'; std::cout << "decltype(static_cast<int&&>(i)) " << type_name<decltype(static_cast<int&&>(i))>() << '\n'; std::cout << "decltype(static_cast<int>(i)) " << type_name<decltype(static_cast<int>(i))>() << '\n'; std::cout << "decltype(foo_lref()) " << type_name<decltype(foo_lref())>() << '\n'; std::cout << "decltype(foo_rref()) " << type_name<decltype(foo_rref())>() << '\n'; std::cout << "decltype(foo_value()) " << type_name<decltype(foo_value())>() << '\n'; }
and output is:
decltype(i) int decltype((i)) int& decltype(ci) int const decltype((ci)) int const& decltype(static_cast<int&>(i)) int& decltype(static_cast<int&&>(i)) int&& decltype(static_cast<int>(i)) int decltype(foo_lref()) int& decltype(foo_rref()) int&& decltype(foo_value()) int
note (for example) difference between decltype(i)
, decltype((i))
. former type of declaration of i
. latter "type" of expression i
. (expressions never have reference type, convention decltype
represents lvalue expressions lvalue references).
thus tool excellent vehicle learn decltype
, in addition exploring , debugging own code.
in contrast, if build on typeid(a).name()
, without adding lost cv-qualifiers or references, output be:
decltype(i) int decltype((i)) int decltype(ci) int decltype((ci)) int decltype(static_cast<int&>(i)) int decltype(static_cast<int&&>(i)) int decltype(static_cast<int>(i)) int decltype(foo_lref()) int decltype(foo_rref()) int decltype(foo_value()) int
i.e. every reference , cv-qualifier stripped off.
c++14 update
just when think you've got solution problem nailed, comes out of , shows better way. :-)
this answer jamboree shows how type name in c++14 @ compile time. brilliant solution couple reasons:
- it's @ compile time!
- you compiler job instead of library (even std::lib). means more accurate results latest language features (like lambdas).
jamboree's answer doesn't quite lay out vs, , i'm tweaking code little bit. since answer gets lot of views, take time go on there , upvote answer, without which, update never have happened.
#include <cstddef> #include <stdexcept> #include <cstring> #include <ostream> #ifndef _msc_ver # if __cplusplus < 201103 # define constexpr11_tn # define constexpr14_tn # define noexcept_tn # elif __cplusplus < 201402 # define constexpr11_tn constexpr # define constexpr14_tn # define noexcept_tn noexcept # else # define constexpr11_tn constexpr # define constexpr14_tn constexpr # define noexcept_tn noexcept # endif #else // _msc_ver # if _msc_ver < 1900 # define constexpr11_tn # define constexpr14_tn # define noexcept_tn # elif _msc_ver < 2000 # define constexpr11_tn constexpr # define constexpr14_tn # define noexcept_tn noexcept # else # define constexpr11_tn constexpr # define constexpr14_tn constexpr # define noexcept_tn noexcept # endif #endif // _msc_ver class static_string { const char* const p_; const std::size_t sz_; public: typedef const char* const_iterator; template <std::size_t n> constexpr11_tn static_string(const char(&a)[n]) noexcept_tn : p_(a) , sz_(n-1) {} constexpr11_tn static_string(const char* p, std::size_t n) noexcept_tn : p_(p) , sz_(n) {} constexpr11_tn const char* data() const noexcept_tn {return p_;} constexpr11_tn std::size_t size() const noexcept_tn {return sz_;} constexpr11_tn const_iterator begin() const noexcept_tn {return p_;} constexpr11_tn const_iterator end() const noexcept_tn {return p_ + sz_;} constexpr11_tn char operator[](std::size_t n) const { return n < sz_ ? p_[n] : throw std::out_of_range("static_string"); } }; inline std::ostream& operator<<(std::ostream& os, static_string const& s) { return os.write(s.data(), s.size()); } template <class t> constexpr14_tn static_string type_name() { #ifdef __clang__ static_string p = __pretty_function__; return static_string(p.data() + 31, p.size() - 31 - 1); #elif defined(__gnuc__) static_string p = __pretty_function__; # if __cplusplus < 201402 return static_string(p.data() + 36, p.size() - 36 - 1); # else return static_string(p.data() + 46, p.size() - 46 - 1); # endif #elif defined(_msc_ver) static_string p = __funcsig__; return static_string(p.data() + 38, p.size() - 38 - 7); #endif }
this code auto-backoff on constexpr
if you're still stuck in ancient c++11. , if you're painting on cave wall c++98/03, noexcept
sacrificed well.
c++17 update
in comments below lyberta points out new std::string_view
can replace static_string
:
template <class t> constexpr std::string_view type_name() { using namespace std; #ifdef __clang__ string_view p = __pretty_function__; return string_view(p.data() + 34, p.size() - 34 - 1); #elif defined(__gnuc__) string_view p = __pretty_function__; # if __cplusplus < 201402 return string_view(p.data() + 36, p.size() - 36 - 1); # else return string_view(p.data() + 49, p.find(';', 49) - 49); # endif #elif defined(_msc_ver) string_view p = __funcsig__; return string_view(p.data() + 84, p.size() - 84 - 7); #endif }
i've updated constants vs nice detective work jive dadson in comments below.
Comments
Post a Comment