在C ++中编译时获取类型名称

时间:2011-09-19 10:33:57

标签: c++ boost boost-mpl typeinfo

我想获取类型名称并将其打印用于调试目的。我使用以下代码:

#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());
}

它运行良好,但我认为存在不必要的运行时开销。有没有办法获得在编译时计算的类型id的人类可读形式?我在考虑这样的事情:

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

哪个会返回类型名称的字符串常量。

作为一个(不那么严格)相关的问题:是否可以使用boost :: mpl进行字符串处理?

5 个答案:

答案 0 :(得分:5)

我看不到typeid(T).name()会产生运行时开销。 typeid(expr)是的,如果expr属于多态类型。

看起来demangling可能在运行时发生,但是你可以做很多事情。如果这只是用于调试那么我真的不会太担心它,除非你的探查器指示这导致程序运行速度太慢以至于调试其它元素很麻烦。

答案 1 :(得分:2)

我有同样的需求,我已经在我的类的静态方法中使用 __ FUNCTION __ maccro解决了它。 您必须对 __ FUNCTION __ 进行一些常规计算以提取类名。你必须做一些模板技巧,以避免在每个类中粘贴相同的代码。如果有人有意,我可以清理并将我的代码翻译成法语,然后发布。

此方法的主要优点是不需要启用RRTI 。另一方面,类名的提取可能与编译器有关。

答案 2 :(得分:1)

您可以使用std :: type_index来缓存解码的字符串。

答案 3 :(得分:1)

您可以使用std :: map或类似的数据结构(例如splay树)来相对快速地缓存和访问demangled名称。但是在编译时没有完成,我怀疑后者是可能的。

答案 4 :(得分:1)

在 C++ 20 中

你可以使用标准的std::source_location,它的静态方法::currentconsteval,你可以在编译时使用它,然后你可以获得function_name方法.

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 func_name_ = 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
    return {func_name_.begin() + 37, func_name.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>();
}

注意:源位置对象的函数名称可能因编译器而异,如果您的编译器尚不支持源位置库,您可以使用宏 __PRETTY_FUNCTION__ 或任何其他相关宏。

用法:

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}>

type_of中使用此 typeid(...).name() 的优势:

(另外注意:我没有测试其他编译器的能力,所以我只保证GCC)

  • 您可以在编译时检查该值,以便 static_assert(type_of(4.0) == "double&&") 有效。
  • 没有运行时开销。
  • 该操作可以在运行时或编译时完成(取决于给出的参数是否可以在常量表达式中使用)。
  • 它保留了 cv-ref 特征(constvolatile&&&)。
  • 您也可以使用模板参数,以防类型的构造函数被删除并在没有 cv-ref 特征的情况下进行测试。