101

Is it possible to get the object name too?

#include<cstdio>

class one {
public:
    int no_of_students;
    one() { no_of_students = 0; }
    void new_admission() { no_of_students++; }
};

int main() {
    one A;
    for(int i = 0; i < 99; i++) {
        A.new_admission();
    }
    cout<<"class"<<[classname]<<" "<<[objectname]<<"has "
        <<A.no_of_students<<" students";
}

where I can fetch the names, something like

[classname] = A.classname() = one
[objectname] = A.objectname() = A

Does C++ provide any mechanism to achieve this?

Lazer
  • 90,700
  • 113
  • 281
  • 364
  • I'm confused. Are you relating C++ `class` with a "class of students"? If you have a `class` representing a class, and the class has a name like "Mrs Gutentag's Kindergarten," it should have a data member to store that as a `std::string`. – Potatoswatter Sep 06 '10 at 06:00
  • @Potatoswatter: Now, I am confused. What are you asking? – Lazer Sep 06 '10 at 06:07
  • 2
    There are two meanings to the word "class," and the kind of class that has students is not what the `class` keyword refers to. – Potatoswatter Sep 06 '10 at 06:10
  • @Potatoswatter: Oh, I get it. No, I just want to retrieve the `class` name i.e. `one` here. – Lazer Sep 06 '10 at 06:13
  • http://stackoverflow.com/questions/81870/is-it-possible-to-print-a-variables-type-in-standard-c – Ciro Santilli OurBigBook.com Jan 01 '17 at 20:51

10 Answers10

131

You can display the name of a variable by using the preprocessor. For instance

#include <iostream>
#define quote(x) #x
class one {};
int main(){
    one A;
    std::cout<<typeid(A).name()<<"\t"<< quote(A) <<"\n";
    return 0;
}

outputs

3one    A

on my machine. The # changes a token into a string, after preprocessing the line is

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

Of course if you do something like

void foo(one B){
    std::cout<<typeid(B).name()<<"\t"<< quote(B) <<"\n";
}
int main(){
    one A;
    foo(A);
    return 0;
}

you will get

3one B

as the compiler doesn't keep track of all of the variable's names.

As it happens in gcc the result of typeid().name() is the mangled class name, to get the demangled version use

#include <iostream>
#include <cxxabi.h>
#define quote(x) #x
template <typename foo,typename bar> class one{ };
int main(){
    one<int,one<double, int> > A;
    int status;
    char * demangled = abi::__cxa_demangle(typeid(A).name(),0,0,&status);
    std::cout<<demangled<<"\t"<< quote(A) <<"\n";
    free(demangled);
    return 0;
}

which gives me

one<int, one<double, int> > A

Other compilers may use different naming schemes.

Scott Wales
  • 11,336
  • 5
  • 33
  • 30
  • 23
    Why do we get `3one`? What is `3`? – Lazer Sep 06 '10 at 06:12
  • 5
    @Lazer No idea, as people say the result of `typeid` is platform specific. It's probably something to do with C++'s name mangling scheme. – Scott Wales Sep 06 '10 at 06:15
  • 3
    If I try `template class one{};` and use a `one` I get `3oneIiE` as the class name, so it is indeed to do with name mangling (The stuff probably means class with a `3` character name `one` with `I` one template argument `i` (int) `E` end of arguments) – Scott Wales Sep 06 '10 at 06:19
  • +1 I was not aware that we even have such functionality in C++ – mukeshkumar Sep 06 '10 at 07:50
  • I get 6... My guess is that it could be the major number of g++ version? – dom_beau Apr 29 '18 at 14:41
  • 7
    "3" is probably the length of the name "one". Name mangling prepends lengths to names. – DrChandra Oct 31 '19 at 15:40
  • When I use `typeid(A)` on a variable that's declared as a supertype of the object, and filled with a subclass instance, it prints the superclass type instead of the object type. \[edit] Correction, it works when I dereference the (pointer) variable. – Mark Jeronimus Sep 27 '22 at 11:26
  • gcc uses numbers before classes. msvc doesn't. – Macke Feb 10 '23 at 10:15
22

use typeid(class).name

// illustratory code assuming all includes/namespaces etc

#include <iostream>
#include <typeinfo>
using namespace std;

struct A{};
int main(){
   cout << typeid(A).name();
}

It is important to remember that this gives an implementation defined names.

As far as I know, there is no way to get the name of the object at run time reliably e.g. 'A' in your code.

EDIT 2:

#include <typeinfo>
#include <iostream>
#include <map>
using namespace std; 

struct A{
};
struct B{
};

map<const type_info*, string> m;

int main(){
    m[&typeid(A)] = "A";         // Registration here
    m[&typeid(B)] = "B";         // Registration here

    A a;
    cout << m[&typeid(a)];
}
Chubsdad
  • 24,777
  • 4
  • 73
  • 129
  • @chubsdad: What is the output that you expect? I get `1A`, but the class name is `one`. – Lazer Sep 06 '10 at 06:11
  • @Lazer: As pointed out, this prints an implementation defined string. Refer my edit if you want a more fancy way – Chubsdad Sep 06 '10 at 06:29
  • 1
    The site [cppreference.com says](http://en.cppreference.com/w/cpp/language/typeid#Notes) "There is no guarantee that the same std::type_info instance will be returned by all evaluations of the typeid expression on the same type." This mean `&typeid(A) == &typeid(A)` could be `false`. With C++11 you could use [`std::type_index`](http://en.cppreference.com/w/cpp/types/type_index). – JojOatXGME Oct 25 '15 at 17:13
  • probably is better hash values to be used, e.g. typeid(A).hash_code() – Nick Feb 16 '16 at 07:57
21

To get class name without mangling stuff you can use func macro in constructor:

class MyClass {
    const char* name;
    MyClass() {
        name = __func__;
    }
}
grungegurunge
  • 841
  • 7
  • 13
  • 6
    this si compiler specific. One will give you `MyClass::MyClass` the other `MyClass`. When usid in regular method it will produce `MyClass::SomeMethod` or `SomeMethod`. – Marek R Jul 31 '17 at 12:01
  • Thanks. Simple and elegant. Will do for debugging and tracking the source of issues. – Stefan Jul 25 '23 at 16:12
9

Just write simple template:

template<typename T>
const char* getClassName(T) {
  return typeid(T).name();
}

struct A {} a;

void main() {
   std::cout << getClassName(a);
}
sea-kg
  • 317
  • 2
  • 5
9

Do you want [classname] to be 'one' and [objectname] to be 'A'?

If so, this is not possible. These names are only abstractions for the programmer, and aren't actually used in the binary code that is generated. You could give the class a static variable classname, which you set to 'one' and a normal variable objectname which you would assign either directly, through a method or the constructor. You can then query these methods for the class and object names.

Alexander Rafferty
  • 6,134
  • 4
  • 33
  • 55
  • @Lazer: the example provides absolutely no reason to want to retrieve the name `one`. That's what I'm asking about on the question discussion. Wanting the name of a C++ class is associated with ugly dynamic type hacks, and debugging. Wanting the name of a class in school which the program represents is not a job for runtime type information. – Potatoswatter Sep 06 '10 at 06:13
  • @Potatoswatter: It has nothing to do with classes in particular as you suggest. For example, if I have a variable `int a;`, is there a construct in C++ that allows me to do something similar to `if(a.type() == int) {}`. That is what I wanted to know. – Lazer Sep 06 '10 at 06:18
  • 2
    @Lazer: Yes, for that operation, use `if ( typeid(a) == typeid(int) )`; no need to mess around with strings. However, there properly isn't a need for that operation in the first place, since the static types are already known. If the static types are known by the compiler but flexible, as in templates, you should use `` template operations such as `is_same< type_of_a, int >` instead of runtime comparison. `typeid` only serves a practical purpose on classes with virtual methods, or for debugging templates. – Potatoswatter Sep 06 '10 at 06:26
7

You could try using "typeid".

This doesn't work for "object" name but YOU know the object name so you'll just have to store it somewhere. The Compiler doesn't care what you namned an object.

Its worth bearing in mind, though, that the output of typeid is a compiler specific thing so even if it produces what you are after on the current platform it may not on another. This may or may not be a problem for you.

The other solution is to create some kind of template wrapper that you store the class name in. Then you need to use partial specialisation to get it to return the correct class name for you. This has the advantage of working compile time but is significantly more complex.

Edit: Being more explicit

template< typename Type > class ClassName
{
public:
    static std::string name()
    {
        return "Unknown";
    }
};

Then for each class somethign liek the following:

template<> class ClassName<MyClass>
{
public:
    static std::string name()
    {
        return "MyClass";
    }
};

Which could even be macro'd as follows:

#define DefineClassName( className ) \
\
template<> class ClassName<className> \
{ \
public: \
    static std::string name() \
    { \
        return #className; \
    } \
}; \

Allowing you to, simply, do

DefineClassName( MyClass );

Finally to Get the class name you'd do the following:

ClassName< MyClass >::name();

Edit2: Elaborating further you'd then need to put this "DefineClassName" macro in each class you make and define a "classname" function that would call the static template function.

Edit3: And thinking about it ... Its obviously bad posting first thing in the morning as you may as well just define a member function "classname()" as follows:

std::string classname()
{
     return "MyClass";
}

which can be macro'd as follows:

DefineClassName( className ) \
std::string classname()  \
{ \
     return #className; \
}

Then you can simply just drop

DefineClassName( MyClass );

into the class as you define it ...

Goz
  • 61,365
  • 24
  • 124
  • 204
  • i would recommend not to allow default class name as it defeat the purpose. if we want class to be named, then enforce it on every class. – YeenFei Sep 06 '10 at 06:05
  • Yeah fair enough. Some people may prefer it to work with any class even other classes. – Goz Sep 06 '10 at 06:09
  • I guess you meant #className instead of ##className ? – usta Sep 06 '10 at 08:37
  • Aye ... sorry .... I didn't have a machine i could test compile that on at the time. – Goz Sep 06 '10 at 09:46
4

You can try this:

template<typename T>
inline const char* getTypeName() {
  return typeid(T).name();
}

#define DEFINE_TYPE_NAME(type, type_name)  \
  template<>                               \
  inline const char* getTypeName<type>() { \
    return type_name;                      \
  }

DEFINE_TYPE_NAME(int, "int")
DEFINE_TYPE_NAME(float, "float")
DEFINE_TYPE_NAME(double, "double")
DEFINE_TYPE_NAME(std::string, "string")
DEFINE_TYPE_NAME(bool, "bool")
DEFINE_TYPE_NAME(uint32_t, "uint")
DEFINE_TYPE_NAME(uint64_t, "uint")
// add your custom types' definitions

And call it like that:

void main() {
 std::cout << getTypeName<int>();
}
voltento
  • 833
  • 10
  • 26
3

An improvement for @Chubsdad answer,

//main.cpp

using namespace std;

int main(){
A a;
a.run();
}

//A.h
class A{
public:
 A(){};
 void run();
}

//A.cpp
#include <iostream>
#include <typeinfo>
void A::run(){
   cout << (string)typeid(this).name();
}

Which will print:

class A*
OhadM
  • 4,687
  • 1
  • 47
  • 57
  • 3
    No, it doesn't print A* with my g++ 6.3 compiler, but "P1A", because the typeid name is implementation dependent. – Frank Buss Jan 19 '19 at 04:27
0

Here is a trick for getting the name of a class you create:

struct NameTest {
    NameTest() : mName {std::source_location::current().function_name()} {
    }
    void operator()() {
        auto src_loc = std::source_location::current();

        std::cout << "Class name:\t" << mName  //
                  << "\nFunc:\t\t" << src_loc.function_name() //
                  << "\nLine:\t\t" << src_loc.line() << '\n';
    }
    const std::string mName;
};

int main() {
    NameTest name_test;
    name_test();
    return 0;
}

output:

Class name: NameTest::NameTest()

Func:       void NameTest::operator()()

Line:       81

A little string manipulation will strip the unneeded parts

Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
rm1948
  • 431
  • 2
  • 11
0

Using MACRO

#define ADD_CLASS_NAME(class_name)         \
public:                                \
const char *getName()      \
{                                  \
    return #class_name ;\
}
class Test{
  ADD_CLASS_NAME(Test);
};
int main(){
 Test t;
 std::cout<<t.getName();
}
abhishek
  • 26
  • 3