Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using typeid to get name of derived class

Tags:

c++

I'm creating a resource manager that takes in classes derived from the class Resource. My problem is that typeid(..).name() returns the wrong name of the class.

I want to add resources like Texture to a map like this:

typedef std::unordered_map<std::string, Resource*> ResourceMap;

And I want the name of the 'class + counter' to be the string/name of the Resource. Unfortunately everything just ends up being called P8Resource because these classes derive from 'Resource'. An untimely predicament indeed, but typeid must know somehow that texture is a Texture, not Resource.

class Resource {}; // abstract

class Texture : public Resource {};

Resource *texture = new Texture();
Texture *texture2 = new Texture();

typeid(texture).name(); // yields 'Resource'
typeid(texture2).name(); // yields 'Texture'

How can I make the first typeid statement yield 'Texture' without using type conversions?

like image 987
Poriferous Avatar asked Aug 14 '15 18:08

Poriferous


4 Answers

typeid of a pointer will always be the declared type, because that's the real type of the pointer itself. To know the real type of the object the pointer points to, you need to dereference the pointer to get the actual type: http://ideone.com/FYmp79

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

struct Resource {
    virtual ~Resource() = default;
};

struct Texture : Resource {};

int main() {
    Resource *resource = new Resource;
    Resource *texture = new Texture;

    cout << typeid(*resource).name() << endl; // yields 'Resource'
    cout << typeid(*texture).name() << endl; // yields 'Texture'
    return 0;
}

EDIT: as other people said, you need to make the class polymorphic to get the runtime type information.

like image 60
Kan Li Avatar answered Nov 13 '22 06:11

Kan Li


My problem is that typeid(..).name() returns the wrong name of the class.

typeid(...).name() is only useful for debugging or logging purposes. As http://en.cppreference.com/w/cpp/types/type_info/name says:

No guarantees are given, in particular, the returned string can be identical for several types and change between invocations of the same program.

As for your problem,

How can I make the first typeid statement yield 'Texture' without using type conversions?

The safest, easiest and most correct way to do this would be to add a virtual name function of your own to Resource:

virtual std::string name() const = 0;

Then override it in every subclass to return the name of the class.

like image 35
Christian Hackl Avatar answered Nov 13 '22 06:11

Christian Hackl


Firstly, typeid can provide dynamic run-time type identification only for values of polymorphic class types. Your classes are not polymorphic. You need at least one virtual method in the base class in order to "activate" the dynamic behavior of typeid.

Secondly, in order to use typeid for determining the dynamic type of polymorphic object, you have to apply it to the object itself, not to a pointer to the object (as in your code).

Thirdly, the value returned by name() does not mean much and cannot be used for any practical purposes. Formally, name() can simply return an empty string every time. You have to use (and compare) the type_info objects themselves for run-time type identification.

like image 7
AnT Avatar answered Nov 13 '22 06:11

AnT


The typeid operator can only return the dynamic type of an expression, if that expression is a glvalue to an object of a polymorphic class.

A class is polymorphic if it defines at least one virtual member function, directly or in one of its bases.

Since your Resource class and the derived classes don't satisfy this requirement, the typeid operator can only access the static type.

To solve that, and issues that you will run into as soon as you try to delete a pointer to such a resource, make the destructor virtual.

like image 3
Daniel Jour Avatar answered Nov 13 '22 04:11

Daniel Jour