58

I was wondering if it is possible in C++ to retrieve the name of a class in string form without having to hardcode it into a variable or a getter. I'm aware that none of that information is actually used at runtime, therefor it is unavailable, but are there any macros that can be made to create this functionality?

Edit: May be helpful to note that I'm actually trying to retrieve the name of a derived class, and I'm using Visual C++ 2008 Express Edition.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Morgan
  • 1,765
  • 2
  • 19
  • 26

5 Answers5

92

You can use typeid:

#include <typeinfo>

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

However, the type name isn't standardided and may differ between different compilers (or even different versions of the same compiler), and it is generally not human readable because it is mangled.

On GCC and clang (with libstdc++ and libc++), you can demangle names using the __cxa_demangle function (on MSVC demangling does not seem necessary):

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

This will still not necessarily be a readable name — for instance, std::string is a type name for the actual type, and its complete type name in the current libstdc++ is std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >; by contrast, in the current libc++ it’s std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >. “Prettifying” type aliases is unfortunately not trivial.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 1
    Interesting, I didn't know about this. It seems to work pretty well. It gives me a bit more text than I wanted in the response, but it seems to work pretty well. Thanks! – Morgan Jun 21 '09 at 20:24
  • Will this work of there are no virtual methods in the class? I though RTTI doesn't work in this case. I guess as long as you have a virtual destructor you will be okay. – LeopardSkinPillBoxHat May 12 '10 at 01:10
  • 7
    @LeopardSkinPillBoxHat: Yes, it will work (see §5.2.8/3 and 4). It’s a common misconception that `typeid` will only work with polymorphic types, probably stemming from the similarity to RTTI features. – In fact, using `typeid` on static types does not need, and does not use, RTTI. The operator is evaluated at compile time and the result is compiled in (strictly speaking, that’s an implementation detail but it’s the only sane implementation). – Konrad Rudolph May 12 '10 at 08:44
35

If you just want to check if it's certain class, then

typeid(obj) == typeid(CSubClass)

will always work regardless of the implementations.

Otherwise, a convenient way is to declare:

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

and implement per subclass.

fchen
  • 781
  • 7
  • 15
  • 8
    Easy, simple, compiles. Wrong, unfortuantely. `typeid()` returns a `typeinfo*`, a pointer. If two `typeinfo*` pointers are equal, they refer to the same type, but if they're unequal they still may refer to the same type. That's why there's a `std::type_index` class with proper semantics. `std::type_index(typeid(obj)) == std::type_index(typeid(CSubClass))` will be true **if and only if** the two types are equal. – MSalters May 24 '17 at 09:22
11

The typeid(obj).name() thing always gives the type of the variable as it was declared, not the actual type (class) of the object. If the variable obj is assigned to an instance of a subclass of the class that obj was declared as, typeid doesn't reveal that, unfortunately.

Andre Holzner
  • 18,333
  • 6
  • 54
  • 63
jbillfinger
  • 182
  • 1
  • 3
  • 7
    Using GCC 4.7.3, using typeid(*somePtr).name() gives me the concrete classes' name. – notlesh Jun 05 '13 at 19:18
  • 4
    I ran into the same problem, but @stephelton's comment made me realize I was calling it on a pointer instead of the actual object or reference and it was returning the type of the pointer! Just adding the `*` fixed everything. – Mark Ransom Jul 08 '16 at 19:55
  • Confirming both of the above comments for VS. After `BaseClass* ptr = new SubClass;` I find `typeid(ptr).name()` yields `class BaseClass *`, and `typeid(*ptr).name()` gives `class SubClass`. – Jonathan Lidbeck Oct 19 '17 at 00:49
  • @JonathanLidbeck Did able to find correct class name after removing prefixed "class". – Pabitra Dash Dec 25 '18 at 01:33
1

What about this,

Tested on Windows 10 using Visual Studio 2019 (v142).

#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"

In 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"
blongho
  • 1,151
  • 9
  • 12
0

With C++17, and a third-party library, you can now obtain the name of a class like

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

This uses only compile-time information, so it can be constexpr. Note that it’s not portable; for example Intel’s compiler isn’t supported.

Franklin Yu
  • 8,920
  • 6
  • 43
  • 57