Skip to main content
added 14 characters in body
Source Link
tim
  • 699
  • 6
  • 15

Attribution:

Attribution:

Added attribution for algorithms.
Source Link
tim
  • 699
  • 6
  • 15

Attribution: Peter Bloomfield

Attribution:

  1. SnakeNET
  2. brewmanz
  3. Tweaked by me to be more generic.
  1. Original String version by Vladimir Talybin
  2. Tweaked char array version by me.
  1. Since Template Form 2 has higher memory requirements than Template Form 1, how could you convertit be converted from Form 2 to Form 1?
  2. How would you initialise a static const char * be initialised from a substring of a char array obtained from the __PRETTY_FUNCTION__ macro and passed as a template argument at compile-time?
  1. Since Template Form 2 has higher memory requirements than Template Form 1, how could you convert Form 2 to Form 1?
  2. 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?

Attribution: Peter Bloomfield

Attribution:

  1. SnakeNET
  2. brewmanz
  3. Tweaked by me to be more generic.
  1. Original String version by Vladimir Talybin
  2. Tweaked char array version by me.
  1. Since Template Form 2 has higher memory requirements than Template Form 1, how could it be converted from Form 2 to Form 1?
  2. How would a static const char * be initialised from a substring of a char array obtained from the __PRETTY_FUNCTION__ macro and passed as a template argument at compile-time?
Added comparison with `__PRETTY_FUNCTION__` macro with some conclusions and follow-up questions.
Source Link
tim
  • 699
  • 6
  • 15

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

  1. TypeOf() has an issue with a decaying pointer.
  2. TypeOf() and TYPE_NAME() have to have individual definitions with long-hand form or MAKE_TYPE_INFO.
  3. 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.
  4. Template Form 1 has the lowest memory requirements.

Questions

  1. Since Template Form 2 has higher memory requirements than Template Form 1, how could you convert Form 2 to Form 1?
  2. 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;
}

I tested the template form and the long-hand form and got different results. See the last two rows of the table for the differences.

Template Form

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] )
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; }

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));
TypeOf() TYPE_NAME()
bool bool
bool* bool*
char char
char* char*
double double
double* double*
float float
float* float*
int int
int* int*
String String
String* String*
char* char[5]
bool unknown

I tested the template form, 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] )
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]
char(*)[5] char(*)[5] char (*)[5]

Some Conclusions

  1. TypeOf() has an issue with a decaying pointer.
  2. TypeOf() and TYPE_NAME() have to have individual definitions with long-hand form or MAKE_TYPE_INFO.
  3. 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.
  4. Template Form 1 has the lowest memory requirements.

Questions

  1. Since Template Form 2 has higher memory requirements than Template Form 1, how could you convert Form 2 to Form 1?
  2. 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;
}
deleted 24 characters in body
Source Link
tim
  • 699
  • 6
  • 15
Loading
Source Link
tim
  • 699
  • 6
  • 15
Loading