5

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

#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

这将返回类型名称的字符串常量。

4

5 回答 5

7

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

看起来拆解可能发生在运行时,但您对此无能为力。如果这只是为了调试,那么我真的不会太担心它,除非你的分析器表明这会导致你的程序变慢,以至于调试它的其他元素很麻烦。

于 2011-09-19T10:35:58.437 回答
3

我有同样的需求,我已经在我的班级的静态方法中使用_____FUNCTION_____ 宏解决了它。但是您必须对 _____FUNCTION_____ 进行一些运行计算以提取类名。你必须做一些模板技巧来避免在每个类中粘贴相同的代码。如果有人感兴趣,我可能会清理我的代码并将其从法语翻译以发布。

这种方法的主要优点是您不需要启用 RRTI。另一方面,类名的提取可能依赖于编译器。

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();
于 2012-11-19T15:36:26.120 回答
1

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

于 2011-09-19T11:48:23.403 回答
1

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

于 2011-09-19T16:00:59.403 回答
1

在 C++ 20 中

您可以使用std::source_location其静态方法所在::current的标准consteval,您可以在编译时使用它,然后您可以获得该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

    // another notice: these magic numbers will not work on MSVC
    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 特征的情况下进行测试。
于 2021-07-26T02:47:45.097 回答