I tested the template form and, the long-hand form, and the __PRETTY_FUNCTION__ macro and got different results. See the last two rows of the table for the differences.
Template Form 1
MAKE_TYPE_INFO( bool )
MAKE_TYPE_INFO( bool* )
MAKE_TYPE_INFO( char )
MAKE_TYPE_INFO( char* )
MAKE_TYPE_INFO( double )
MAKE_TYPE_INFO( double* )
MAKE_TYPE_INFO( float )
MAKE_TYPE_INFO( float* )
MAKE_TYPE_INFO( int )
MAKE_TYPE_INFO( int* )
MAKE_TYPE_INFO( short )
MAKE_TYPE_INFO( short* )
MAKE_TYPE_INFO( String )
MAKE_TYPE_INFO( String* )
MAKE_TYPE_INFO( char[5] )
MAKE_TYPE_INFO( char*[5]char(*)[5] )
const char* TypeOf(const bool&) { static const char type[] = "bool"; return type; }
const char* TypeOf(const bool*) { static const char type[] = "bool*"; return type; }
const char* TypeOf(const char&) { static const char type[] = "char"; return type; }
const char* TypeOf(const char*) { static const char type[] = "char*"; return type; }
const char* TypeOf(const double&) { static const char type[] = "double"; return type; }
const char* TypeOf(const double*) { static const char type[] = "double*"; return type; }
const char* TypeOf(const float&) { static const char type[] = "float"; return type; }
const char* TypeOf(const float*) { static const char type[] = "float*"; return type; }
const char* TypeOf(const int&) { static const char type[] = "int"; return type; }
const char* TypeOf(const int*) { static const char type[] = "int*"; return type; }
const char* TypeOf(const String&) { static const char type[] = "String"; return type; }
const char* TypeOf(const String*) { static const char type[] = "String*"; return type; }
//const char* const TypeOf(const char[5]) { static const char* type = "char[5]"; return type; } // Decays to char* so generates compiler redefinition warning.
const char* const TypeOf(const char(*)[5]) { static const char* type = "char(*)[5]"; return type; }
Template Form 2: __PRETTY_FUNCTION__ Macro
Using String
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);
}
Using char array
This variation has lower memory requirements.
template <typename T>
char* type_name(const T&)
{
char* pf = __PRETTY_FUNCTION__;
char* begin = strstr(pf, "[with T = ") + 10;
char* end = strrchr(pf, ']');
*end = 0;
return begin;
}
Test Code
bool b = false;
char c = 'a';
double d = 1.2;
float f = 3.4;
int i = 5;
String s = "6789";
char chars[] = "1234";
Serial.println(F("TypeOf() Test"));
Serial.println(TypeOf(b));
Serial.println(TypeOf(&b));
Serial.println(TypeOf(c));
Serial.println(TypeOf(&c));
Serial.println(TypeOf(d));
Serial.println(TypeOf(&d));
Serial.println(TypeOf(f));
Serial.println(TypeOf(&f));
Serial.println(TypeOf(i));
Serial.println(TypeOf(&i));
Serial.println(TypeOf(s));
Serial.println(TypeOf(&s));
Serial.println(TypeOf(chars));
Serial.println(TypeOf(&chars));
Serial.println(F("TYPE_NAME() Test"));
Serial.println(TYPE_NAME(b));
Serial.println(TYPE_NAME(&b));
Serial.println(TYPE_NAME(c));
Serial.println(TYPE_NAME(&c));
Serial.println(TYPE_NAME(d));
Serial.println(TYPE_NAME(&d));
Serial.println(TYPE_NAME(f));
Serial.println(TYPE_NAME(&f));
Serial.println(TYPE_NAME(i));
Serial.println(TYPE_NAME(&i));
Serial.println(TYPE_NAME(s));
Serial.println(TYPE_NAME(&s));
Serial.println(TYPE_NAME(chars));
Serial.println(TYPE_NAME(&chars));
Serial.println(F("type_name() Test"));
Serial.println(type_name(b));
Serial.println(type_name(&b));
Serial.println(type_name(c));
Serial.println(type_name(&c));
Serial.println(type_name(d));
Serial.println(type_name(&d));
Serial.println(type_name(f));
Serial.println(type_name(&f));
Serial.println(type_name(i));
Serial.println(type_name(&i));
Serial.println(type_name(s));
Serial.println(type_name(&s));
Serial.println(type_name(chars));
Serial.println(type_name(&chars));
| TypeOf() |
TYPE_NAME() |
type_name() |
| bool |
bool |
bool |
| bool* |
bool* |
bool* |
| char |
char |
char |
| char* |
char* |
char* |
| double |
double |
double |
| double* |
double* |
double* |
| float |
float |
float |
| float* |
float* |
float* |
| int |
int |
int |
| int* |
int* |
int* |
| String |
String |
String |
| String* |
String* |
String* |
| char* |
char[5] |
char [5] |
| boolchar(*)[5] |
unknownchar(*)[5] |
char (*)[5] |
Some Conclusions
TypeOf() has an issue with a decaying pointer.
TypeOf() and TYPE_NAME() have to have individual definitions with long-hand form or MAKE_TYPE_INFO.
type_name() uses the __PRETTY_FUNCTION__ macro to automatically get the type name within the template function, therefore there is no need to individually define the different types.
- Template Form 1 has the lowest memory requirements.
Questions
- Since Template Form 2 has higher memory requirements than Template Form 1, how could you convert Form 2 to Form 1?
- How would you initialise a
static const char * from a substring of a char array obtained from the __PRETTY_FUNCTION__ macro and passed as a template argument at compile-time?
Something like this pseudo code?
static const char * name = substr(__PRETTY_FUNCTION__, begin, end);
Perhaps this constant expression which can be assigned to a template struct member at compile-time? (I used a for loop because strstr and strrchr are not constexpr.)
template <typename T>
constexpr char* type_name(const T&)
{
char* pf = __PRETTY_FUNCTION__;
char* begin;
char* end;
begin = end = pf;
bool found_open_square_bracket = false;
for (; *pf != 0; pf++)
{
if (!found_open_square_bracket && *pf == '[')
{
begin = pf + 10;
found_open_square_bracket = true;
}
if (*pf == ']')
{
end = pf;
}
}
*end = 0;
return begin;
}