Getting type names at compile time in C++ [duplicate]
Asked Answered
B

5

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.

Boo answered 19/9, 2011 at 10:33 Comment(0)
V
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.

Vicegerent answered 19/9, 2011 at 10:35 Comment(4)
Yes, I was asking if the demangling can be done at compile time or not.Boo
Demagling cannot be done in compile time. Due to a way in which C++ ABI communicates with an OS (en.wikipedia.org/wiki/… paragraph "Standardised name mangling in C++")Acropolis
@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?Vicegerent
@Tomalak Geret'kal I'v double checked what I've wrote -you are right. Sorry for the misleading comment, please ignore it.Acropolis
S
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();
Softener answered 19/11, 2012 at 15:36 Comment(1)
I'd be interested to see how this works if you have time to share a snippet.Manganin
L
3

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.
Lempira answered 26/7, 2021 at 2:47 Comment(2)
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 = ...]"Richie
Yes, in short, this is not portable, unless I replace it with proper "cropping" for all compilers.Lempira
H
1

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

Hartfield answered 19/9, 2011 at 11:48 Comment(0)
L
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.

Longtin answered 19/9, 2011 at 16:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.