There also a trick using compiler predefined macro. The advantage is it can print any type.
template <class T>
String type_name(const T&)
{
String s = __PRETTY_FUNCTION__;
int start = s.indexOf("[with T = ") + 10;
int stop = s.lastIndexOf(']');
return s.substring(start, stop);
}
Use it like this
double pi = 3.14;
const char* str = "test";
Serial.println(type_name(pi));
Serial.println(type_name(str));
EDIT:
Some improvements based on tim's analyze.
First I have a comment on variation that has lower memory requirements. __PRETTY_FUNCTION__ is a literal string (a constant) that should not be changed. So my const version that do not use heap is following.
template <size_t N>
const char* extract_type(const char (&signature)[N])
{
const char* beg = signature;
while (*beg++ != '=');
++beg;
const char* end = signature + N;
for (; *end != ']'; --end);
static char buf[N];
char* it = buf;
for (; beg != end; ++beg, ++it)
*it = *beg;
*it = 0;
return buf;
}
template <class T>
const char* type_name(const T&)
{
return extract_type(PRETTY_FUNCTION);
}
A less safe but more efficient version using the fact that offset to type we want extract is a constant (always the same). If function signature changes (like put it in a namespace) the offset will change too. But come on, set it once, write a comment and it will work forever :).
template <size_t N>
const char* extract_type(const char (&signature)[N])
{
const char* beg = signature + 42;
const char* end = signature + N - 2;
static char buf[N - 43];
char* it = buf;
for (; beg != end; ++beg, ++it)
*it = *beg;
*it = 0;
return buf;
}
template <class T>
const char* type_name(const T&)
{
return extract_type(PRETTY_FUNCTION);
}
While above is ok ofcourse it can be improved further. Here is a my version of constexpr function mentioned by @tim. It uses constant offset as above to keep it short. This code generates only extracted string of type, the proof is here.
#include <utility>
template <class T, std::size_t... I>
const char* type_name(std::index_sequence<I...>)
{
static constexpr char name[] = { PRETTY_FUNCTION[I + 60]..., 0 };
return name;
}
template <class T>
const char* type_name(const T&)
{
return type_name<T>(
std::make_index_sequence<sizeof(PRETTY_FUNCTION) - 44>());
}
The minimum requirement for this is C++14 because of sequence traits. These can be re-implemented (see definition here) to adapt it to C++11.