Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way to convert decltype to string in a macro?

Is there any way I can evaluate decltype in a C++ macro? My main motivation is to create a macro that is able to determine the type of this and convert it to a string.

If it's not possible to use decltype is there any other way a macro used inside a class declaration can get the type of the class as a string?

like image 838
Tim MB Avatar asked Jul 08 '13 10:07

Tim MB


2 Answers

Is there any way I can evaluate decltype in a C++ macro?

No, since macros are strictly evaluated before decltype.

As far as I know there is no way to get the name of the class as a macro, full stop. Any such way would have to be supported by a compiler-generated macro.

However, you can use typeid to get the mangled name (strictly speaking, an implementation-defined representation), and then use compiler-specific tools to retrieve the demangled name from that.

For instance, GCC offers the demangling library to do this.

Here’s a minimal example online demo:

#define THIS_CLASS_NAME() demangled(typeid(*this).name())

std::string demangled(char const* tname) {
    std::unique_ptr<char, void(*)(void*)>
        name{abi::__cxa_demangle(tname, 0, 0, nullptr), std::free};
    return {name.get()};
}

Usage:

namespace foo {
    template <typename T>
    struct bar {
        bar() { std::cout << THIS_CLASS_NAME() << '\n'; }
    };
}

int main() {
    foo::bar<int> b;
}

Yields:

foo::bar<int>
like image 51
Konrad Rudolph Avatar answered Sep 27 '22 21:09

Konrad Rudolph


Macros are expanded before any code is compiled, so unfortunately they don't have any concept of types.

Depending on your needs, you might be able to use a traits class instead though. It's theoretically a bit more efficient and portable than RTTI and typeid, but it only works for types you've explicitly told it about. For example:

template <typename T>
struct Traits
{
    static const char * TYPE_NAME;
};
// Generic definition as a fall-back:
template <typename T> const char * Traits<T>::TYPE_NAME = "unknown";

// Explicit definitions
template < > const char * Traits< int >::TYPE_NAME = "int";
template < > const char * Traits< float >::TYPE_NAME = "float";
template < > const char * Traits< ExampleClass >::TYPE_NAME = "ExampleClass";

The explicit definitions are a bit cumbersome, so you could create a macro to make them more readable:

#define DECLARE_TYPE_TRAIT(name) template < > const char * Traits<name>::TYPE_NAME = #name;

DECLARE_TYPE_TRAITS(int)
DECLARE_TYPE_TRAITS(float)
DECLARE_TYPE_TRAITS(ExampleClass)

Using the traits class in your code is really easy. You just instantiate the traits template on whatever type you want to look up, and access the TYPE_NAME member:

int foo;
ExampleClass blah;

cout << Traits<decltype(foo)>::TYPE_NAME << endl;
cout << Traits<decltype(blah)>::TYPE_NAME << endl;
like image 20
Peter Bloomfield Avatar answered Sep 27 '22 21:09

Peter Bloomfield