2

How can I obtain dynamic type name?

class O {
public:
std::string typename(){ return typeid(*this).name(); }
}

class C  : public O { /* ... */ }

O* varb = new C();
cout << O->typename(); // <--- return class O instead of class C

How can I resolve this?

johnsyweb
  • 136,902
  • 23
  • 188
  • 247

5 Answers5

3

Declare your function returning typename as virtual. Otherwise *this will refer only to the part of CO subobject. You can also google for polymorphism in C++.

Edit. As pointed out by @"Cheers and hth. - Alf", it's enough to declare some function as virtual to make a class polymorphic. And declaring a destructor virtual is a must for polymorphic base classes. But you still need to declare all the functions you will possibly reimplement as virtual.

Dmitry Markin
  • 1,145
  • 8
  • 8
  • A better approach is to make the destructor virtual. It's enough that the class is polymorphic (has *some* virtual member function). And the destructor is nice for that -- avoiding introducing an unintended customization point. – Cheers and hth. - Alf Jan 11 '14 at 10:47
3

On my Linux/Debian/Sid/x86-64 system the file

// file raffa.cc
#include <iostream>
#include <fstream>
#include <typeinfo>
#include <string>

class O {
public:
    virtual std::string type_name() {
        return typeid(*this).name();
    }
    virtual ~O() {};
};

class C  : public O {
    int f;
public:
    C(int k) : O(), f(k) {};
    virtual ~C() {};
    /* ... */
};

using namespace std;

int main() {
    O* varb = new C(__LINE__);
    cout << varb->type_name() << endl; 
    delete varb;
    return 0;
}   

compiled with

g++-4.8 -std=c++11 -Wall -O raffa.cc -o raffa

displays when running ./raffa:

1C

The prefix 1 is probably because of name mangling. See carefully answers to this question to unmangle it.

PS: in real life avoid raw pointers and be scared of memory leaks, so don't copy my code verbatim without understanding these issues! Use valgrind ...

Community
  • 1
  • 1
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
2

typename is a reserved keyword in C++!

Make O::type_name() virtual to enable C::type_name to be invoked via an O*:

#include <iostream>
#include <memory>

class O {
public:
    virtual std::string type_name() { return typeid(*this).name(); }
    virtual ~O() {}
};

class C: public O {};

int main() {
    std::unique_ptr<O> varb { new C() };
    std::cout << varb->type_name();
}

See it run!

johnsyweb
  • 136,902
  • 23
  • 188
  • 247
  • 2
    and give O a virtual destructor. – juanchopanza Jan 11 '14 at 10:34
  • 2
    A better approach is to make the destructor virtual. It's enough that the class is polymorphic (has some virtual member function). And the destructor is nice for that -- avoiding introducing an unintended customization point. – Cheers and hth. - Alf Jan 11 '14 at 10:47
  • Good point, @Cheersandhth.-Alf. Making `type_name()` `virtual` looks more explicit to me, but you're right a `virtual` dtor would suffice. – johnsyweb Jan 11 '14 at 10:49
1

Some errors in your code:

  • First of all you cannot use typename as a custom name, it's a C++ keyword.
  • Second, you cannot use

    O* varb = new C(); 
    cout << O->typename();
    

    since you're trying to dereference a class name and that makes no sense. You probably meant varb->typename() anyway.

To the typeid usage.. if you're trying to use typeid to return dynamically the name of the class a pointer is referring to (runtime), you should use something like

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

class O {
public:
    virtual void vfunction() // Just one virtual function in the base to make the derived polymorphic
    {
        cout << "hello";
    }
};

class C  : public O 
{
    public:
    C() {};
};



int main() 
{
    // your code goes here

    O* varb = new C(); // Declare an O* pointer to C

    cout << typeid(*varb).name(); // This will print out "C", runtime info

    cout << typeid(varb).name(); // This will print out "O*"

    return 0;
}

http://ideone.com/K2RGd5

And keep in mind that a class needs to be polymorphic (that is, to inherit from a base class with virtual functions) in order for typeid to return the runtime class it is pointing to when dereferencing the pointer.

Some more information here: https://stackoverflow.com/a/11484105/1938163


Notice: in the code above, if you're using gcc, you might see different class names than the original you used.. that's custom-defined by gcc due to name mangling and if you want real code names to show up you should use something like

#include <iostream>
#include <typeinfo>
#include <cxxabi.h> // Needed to demangle in gcc
using namespace std;

class O {
public:
    virtual void vfunction()
    {
        cout << "hello";
    }
};

class C  : public O 
{
    public:
    C() {};
};



int main() {
    // your code goes here

    O* varb = new C();

    int status;
    // Demangle symbols
    cout << __cxxabiv1::__cxa_demangle( typeid(*varb).name(), nullptr, 0, &status ); << endl;
    cout << __cxxabiv1::__cxa_demangle( typeid(varb).name(), nullptr, 0, &status );

    return 0;
}
Community
  • 1
  • 1
Marco A.
  • 43,032
  • 26
  • 132
  • 246
1

You can't obtain a nice human-readable type name in a portable way, without specifying each type name yourself.

However, with Visual C++ the typeid::name names are human readable, and with g++ they're not too bad.

So, simply change the current code

class O {
public:
std::string typename(){ return typeid(*this).name(); }
}

class C  : public O { /* ... */ }

O* varb = new C();
cout << O->typename(); // <--- return class O instead of class C

to

class O {
public:
    std::string type_name() const { return typeid(*this).name(); }
    virtual ~O() {}
};

class C  : public O { /* ... */ };

O* varb = new C();
cout << varb->type_name(); // <--- return class O instead of class C

where

  • The function name typename, which is C++ keyword, was changed to type_name.
  • The function was made const, so that it can be called on a const object.
  • A virtual destructor was added to make the class polymorphic (a requirement for typeid to work in polymorphic way).
  • Semicolon was added at the end of both class definitions.
  • O-> was changed to varb->: you can't dereference a class.

Disclaimer: code not touched by compiler's hands (but when you post a question you should better put the code to a compiler first!).

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331