【问题标题】:Retrieving a c++ class name programmatically以编程方式检索 C++ 类名
【发布时间】:2010-11-04 17:01:53
【问题描述】:

我想知道是否可以在 C++ 中以字符串形式检索类的名称,而无需将其硬编码到变量或 getter 中。我知道在运行时实际上并没有使用这些信息,因此它不可用,但是是否可以创建任何宏来创建此功能?

编辑:请注意,我实际上是在尝试检索派生类的名称,并且我使用的是 Visual C++ 2008 Express Edition。

【问题讨论】:

  • 因为这取决于编译器,你使用什么编译器?
  • 我正在使用visual c++ 2008 express,我想注意我实际上是在尝试检索派生类的名称会有所帮助

标签: c++ class macros


【解决方案1】:

你可以使用typeid:

#include <typeinfo>

std::cout << typeid(obj).name() << "\n";

但是,类型名称不是标准化的,并且在不同的编译器(甚至同一编译器的不同版本)之间可能会有所不同,并且通常不可读,因为它是 mangled

在 GCC 和 clang(使用 libstdc++ 和 libc++)上,您可以使用 __cxa_demangle function 对名称进行解组(在 MSVC 上解组似乎没有必要):

#include <cxxabi.h>
#include <cstdlib>
#include <memory>
#include <string>

std::string demangle(char const* mangled) {
    auto ptr = std::unique_ptr<char, decltype(& std::free)>{
        abi::__cxa_demangle(mangled, nullptr, nullptr, nullptr),
        std::free
    };
    return {ptr.get()};
}

这将仍然不一定是可读的名称——例如,std::string 是实际类型的类型名称,它在当前 libstdc++ 中的完整类型名称是 std::__cxx11::basic_string&lt;char, std::char_traits&lt;char&gt;, std::allocator&lt;char&gt; &gt;;相比之下,在当前的 libc++ 中,它是std::__1::basic_string&lt;char, std::__1::char_traits&lt;char&gt;, std::__1::allocator&lt;char&gt; &gt;。不幸的是,“美化”类型别名并非易事。

【讨论】:

  • 有趣,我不知道这个。它似乎工作得很好。它在回复中给了我比我想要的更多的文字,但它似乎工作得很好。谢谢!
  • 类中没有虚方法的工作会不会?我虽然 RTTI 在这种情况下不起作用。我想只要你有一个虚拟析构函数就可以了。
  • @LeopardSkinPillBoxHat:是的,它会起作用(参见§5.2.8/3 和 4)。一个常见的误解是 typeid 仅适用于多态类型,这可能源于与 RTTI 功能的相似性。 – 事实上,在静态类型上使用typeid 不需要也不会使用 RTTI。运算符在编译时被评估,结果被编译进去(严格来说,这是一个实现细节,但它是唯一合理的实现)。
【解决方案2】:

如果你只是想检查它是否是某个类,那么

typeid(obj) == typeid(CSubClass)

无论实施如何,都将始终有效。

否则,一种方便的方法是声明:

virtual const char* classname() { return "CMyClass";}

并实现每个子类。

【讨论】:

  • 容易,简单,编译。错了,可惜了。 typeid() 返回一个 typeinfo*,一个指针。如果两个typeinfo* 指针相等,则它们引用相同的类型,但如果它们不相等,它们仍然可能引用相同的类型。这就是为什么有一个具有适当语义的std::type_index 类。 std::type_index(typeid(obj)) == std::type_index(typeid(CSubClass)) 为真当且仅当这两种类型相等。
【解决方案3】:

typeid(obj).name() 总是给出声明变量的类型,而不是对象的实际类型(类)。如果变量 obj 被分配给 obj 被声明为的类的子类的实例,不幸的是 typeid 不会显示这一点。

【讨论】:

  • 使用 GCC 4.7.3,使用 typeid(*somePtr).name() 给我具体类的名称。
  • 我遇到了同样的问题,但@stephelton 的评论让我意识到我是在指针而不是实际的对象或引用上调用它,并且它正在返回指针的类型!只需添加 * 即可解决所有问题。
  • 确认 VS 的上述两个 cmets。在BaseClass* ptr = new SubClass; 之后,我发现typeid(ptr).name() 产生class BaseClass *,而typeid(*ptr).name() 产生class SubClass
  • @JonathanLidbeck 在删除前缀“类”后确实能够找到正确的类名。
【解决方案4】:

这个呢,

使用 Visual Studio 2019 (v142) 在 Windows 10 上测试。

#include <iostream>
#include <typeinfo>
#include <string>

/**
 @author    blongho
 @fn        template<typename Object> std::string classNameOf()

 @brief     Determine the class name of an object

 @tparam    Object  Type of the object.

 @returns   A name of the class
 @date      2019-09-06
 */

template<typename Object>
std::string classNameOf() {
    std::string name = typeid(Object).name(); //* user defined types gives "class Type"*\ 
    size_t spacePosition = name.find_first_of(" ");
    if (spacePosition != std::string::npos) {
        return name.substr(spacePosition + 1, name.length());
    }
    return name; // mostly primitive types
}


class Person {
private:
    /* data */
public:
    Person() {};
    ~Person() {};

};

class Data
{
private:
    /* data */
public:
    Data() {};
    ~Data() {};

};

struct Type {};

int main() {
    std::cout << "Class name of Person() is \"" << classNameOf<Person>() << "\"\n";
    std::cout << "Class name of Data() is \"" << classNameOf<Data>() << "\"\n";
    std::cout << "Class name of Type() is \"" << classNameOf<Type>() << "\"\n";
    std::cout << "Class name of double is \"" << classNameOf<double>() << "\"\n";
    std::cout << "Class name of std::string is \"" << classNameOf<std::string>() << "\"\n";
    std::cout << "Class name of int is \"" << classNameOf<int>() << "\"\n";
    std::cout << "Class name of float is \"" << classNameOf<float>() << "\"\n";
    std::cout << "Class name of char is \"" << classNameOf<char>() << "\"\n";
    return 0;
}

Output

Class name of Person() is "Person"
Class name of Data() is "Data"
Class name of Type() is "Type"
Class name of double is "double"
Class name of std::string is "std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >"
Class name of int is "int"
Class name of float is "float"
Class name of char is "char"

在 Ubuntu 18.04 中,

g++ -o test src/main.cpp
./test
Class name of Person() is "6Person"
Class name of Data() is "4Data"
Class name of Type() is "4Type"
Class name of double is "d"
Class name of std::string is "NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"
Class name of int is "i"
Class name of float is "f"
Class name of char is "c"

【讨论】:

    【解决方案5】:

    使用 C++17 和 third-party library,您现在可以获得类的名称,如

    #include <iostream>
    #include "nameof.hpp"
    
    namespace test {
        class Object {};
    }
    
    int main() {
        constexpr auto obj_name = nameof::nameof_type<test::Object>();
        std::cout << obj_name << std::endl;
        // this prints "test::Object"
    }
    

    这仅使用编译时信息,因此它可以是constexpr。请注意,它不是便携式的;例如不支持英特尔的编译器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-11-04
      • 1970-01-01
      • 1970-01-01
      • 2012-01-30
      • 2014-02-07
      • 2010-10-25
      • 2011-10-24
      • 2018-07-21
      相关资源
      最近更新 更多