6

I want to get the type name and print it for debug purposes. I use the following code:

#include <cxxabi.h>

inline const char* demangle(const char *s) {
    abi::__cxa_demangle(s, 0, 0, NULL);
}

template<typename T>
inline const char* type_name() {
    return demangle(typeid(T).name());
}

It works well, but it I suppose there is an unnecessary runtime overhead. Is there any way to get a human-readable form of type ids that is computed at compile time? I am thinking of something that looks like this:

boost::mpl::type_name<MyType>::value

Which would return a string constant of the type name.

jacquev6
  • 620
  • 4
  • 18
petersohn
  • 11,292
  • 13
  • 61
  • 98

5 Answers5

7

I can't see typeid(T).name() incurring a runtime overhead. typeid(expr) yes, if expr is of a polymorphic type.

It looks like the demangling probably happens at runtime, but there's not an awful lot you can do about that. If this is only for debugging then I really wouldn't worry about it too much unless your profiler indicates that this is causing your program to slow down so much that debugging other elements of it is troublesome.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • Yes, I was asking if the demangling can be done at compile time or not. – petersohn Sep 19 '11 at 10:38
  • Demagling cannot be done in compile time. Due to a way in which C++ ABI communicates with an OS (http://en.wikipedia.org/wiki/Name_mangling#Standardised_name_mangling_in_C.2B.2B paragraph "Standardised name mangling in C++") – Marcin Sep 19 '11 at 12:07
  • 1
    @Marcin: Name mangling is well-defined in most ABIs, including the Itanium ABI followed by GCC. No runtime information is required; indeed, how would the compiler produce mangled names in the first place were this the case? – Lightness Races in Orbit Sep 19 '11 at 12:10
  • 1
    @Tomalak Geret'kal I'v double checked what I've wrote -you are right. Sorry for the misleading comment, please ignore it. – Marcin Sep 19 '11 at 12:27
3

I have the same need, I've solved it using the _____FUNCTION_____ maccro in a static method of my class. But you must do some runtine computation on _____FUNCTION_____ to extract the class name. You have to do some template tricks to avoid paste the same code in every class. If someone is interessed I may clean and translate my code from french  to post it.

The main advantage of this method is that you don't need to enable RRTI. On the other hand, the extraction of the class name may be compiler dependant.

template <class MyT>
    class NamedClass
    {
        static std::string ComputeClassName()
        {
            std::string funcMacro=__FUNCTION__;
//compiler dependant
            static const std::string start="scul::NamedClass<class ";
            static const std::string end=">::ComputeClassName";

            return funcMacro.substr(start.size(),funcMacro.size()-end.size()-start.size());;
        }
        static const std::string _ClassName;

    };

    template <class MyT>
    const std::string NamedClass<MyT>::_ClassName=NamedClass<MyT>::ComputeClassName();
Fericelli
  • 320
  • 3
  • 9
2

In C++ 20

You can use the standard std::source_location where its static method ::current is consteval in which you can use it at compile-time and then you can obtain the function_name method.

template <typename T>
consteval auto func_name() {
    const auto& loc = std::source_location::current();
    return loc.function_name();
}

template <typename T>
consteval std::string_view type_of_impl_() {
    constexpr std::string_view functionName = func_name<T>();
    // since func_name_ is 'consteval auto func_name() [with T = ...]'
    // we can simply get the subrange
    // because the position after the equal will never change since 
    // the same function name is used

    // another notice: these magic numbers will not work on MSVC
    return {functionName.begin() + 37, functionName.end() - 1};
}

template <typename T>
constexpr auto type_of(T&& arg) {
    return type_of_impl_<decltype(arg)>();
}

template <typename T>
constexpr auto type_of() {
    return type_of_impl_<T>();
}

Note: The function name from the source location object may vary from compiler-to-compiler and you can use the macro __PRETTY_FUNCTION__ or any other related macros if your compiler doesn't yet support the source location library.

Usage:

int x = 4;
// type_of also preserves value category and const-qualifiers
// note: it returns std::string_view
type_of(3); // int&&
type_of(x); // int&
type_of(std::as_const(x)); // const int&
type_of(std::move(x)); // int&&
type_of(const_cast<const int&&>(x)); // const int&&

struct del { del() = delete; };

type_of<del>(); // main()::del (if inside main function)
// type_of(del{}); -- error
type_of<int>(); // int
type_of<const int&>(); // const int&
type_of<std::string_view>(); // std::basic_string_view<char>
type_of([]{}); // main()::<lambda()>&&
type_of<decltype([]{})>(); // main()::<lambda()>
type_of<std::make_index_sequence<3>>(); // std::integer_sequence<long unsigned int, 0, 1, 2>

// let's assume this class template is defined outside main function:
template <auto X> struct hello {};
type_of<hello<1>>(); // hello<1>
type_of<hello<3.14f>>(); // hello<3.1400001e+0f>

// also this:
struct point { int x, y; };

type_of<hello<point{.x = 1, .y = 2}>>() // hello<point{1, 2}>

Advantage of using this type_of over demangling in typeid(...).name():

(also noted: I didn't test other compiler's ability, so I only guarantee for GCC)

  • You can check the value at compile-time, such that static_assert(type_of(4.0) == "double&&") is valid.
  • There is no runtime overhead.
  • The operation can be done either at runtime or compile-time (depending on the argument given whether it's usable in a constant expression).
  • It preserves cv-ref traits (const, volatile, & and &&).
  • You can alternatively use the template argument just in case the type's constructor is deleted and test without the cv-ref traits.
Desmond Gold
  • 1,517
  • 1
  • 7
  • 19
  • This works with GCC and Clang, but won't with MSVC. MSVC outputs just the function name, as if by __FUNCTION__. I.e. it returns "func_name" instead of "func_name [with T = ...]" – Hedede Oct 11 '21 at 05:40
  • Yes, in short, this is not portable, unless I replace it with proper "cropping" for all compilers. – Desmond Gold Oct 11 '21 at 14:28
1

You could use std::type_index to cache the demangled strings.

spraff
  • 32,570
  • 22
  • 121
  • 229
1

You could use an std::map or similar data structure (splay trees for example) to cache and access the demangled name relatively quickly. It's not done in compile time though, I doubt the latter is possible.

Frigo
  • 1,709
  • 1
  • 14
  • 32