Skip to main content
added 1 character in body
Source Link

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

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 common, 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 - 42];
    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__);
}
added 2337 characters in body
Source Link
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);
}
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);
}
double pi = 3.14;
const char* str = "test";

Serial.println(type_name(pi));
Serial.println(type_name(str));
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 common, 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 - 42];
    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.

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);
}
double pi = 3.14;
const char* str = "test";

Serial.println(type_name(pi));
Serial.println(type_name(str));
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);
}
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 common, 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 - 42];
    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.

added 4 characters in body
Source Link

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.indexOflastIndexOf(']', start);

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

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.indexOf(']', start);

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

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));
deleted 238 characters in body
Source Link
Loading
added 150 characters in body
Source Link
Loading
Source Link
Loading