Programmatically getting the name of a derived class
Asked Answered
H

4

23

I am attempting to do something like:

class Base {
public:
   Base() {
      cout << typeid(*this).name() << endl;
   }
   ...
};

class Derived : public Base { ... }
class MoreDerived : public Derived { ... }

Derived d;
MoreDerived m;

Problem is, I always get Base printed to the screen, when I need to see Derived and MoreDerived. Is there a way to get typeid to work this way with derived classes? Or is there another approach besides typeid?

Note: I am adding functionality to an already coded suite, so I don't want to have to add a virtual method to the base class where the derived classes return this value themselves. Also, not worried about runtime overhead, this will be part of a debug compile switch.

Haler answered 19/7, 2011 at 12:27 Comment(0)
J
17

In the constructor Base(), the object is still a "Base" instance. It will become a Derived instance after the Base() constructor. Try to do it after the construction and it will work.

See for example :

Jaquelynjaquenetta answered 19/7, 2011 at 12:30 Comment(4)
Sadly, I have done this before. On the positive, I was really doing the print in the destructor along with stats, so I was able to save the name off. Thanks for the help.Haler
@ysdx: it only works when typeid(*this).name() is put in a virtual function after the ctor. If typeid(*this).name() is put in a non-virtual function the printed out class is still BaseObstruent
@StevenLee, AFAIU as long as the Base class is polymorphic (you should really declare the Base::~Base virtual) this should return the dynamic type and not the static type.Jaquelynjaquenetta
@ysdx: yes, typeid seems to be designed for polymorphic objects (en.cppreference.com/w/cpp/language/typeid)Obstruent
E
13

You can't do that from within a constructor (or destructor) - neither with typeid nor with a virtual method. The reason is while you're in a constructor the vtable pointer is set to the base class being constructed, so the object is of base class and no amount of polymorphism will help at that point.

You have to execute that code after the most derived class has been constructed. One option would be to use a factory function:

template<class T>
T* CreateInstance()
{
    T* object = new T();
    cout << typeid(*object).name() << endl;
    return object;
}
Elfrieda answered 19/7, 2011 at 12:29 Comment(1)
Of course, the vtable pointer setting is just a behind-the-scenes implemention of the C++ rule: An object under construction has the type of the ctor that's running.Collateral
F
0

Another option is to provide a virtual toName() function

struct Object{
  virtual std::string toName() const = 0;
}
struct Base: Object{
  std::string toName()const{ return "Base"; }
}
struct Derived: Base, Object{
  std::string toName()const{ return "Derived"; }

This might get tedious since you need to manually create each toName function. But the advantage it gives you is to provide your own custom name.

Foreworn answered 19/7, 2011 at 14:24 Comment(0)
P
0

Since the type is not polymorphic, the base class will never see the derived class, irrespective of any initialization order taking place in the constructor, it simply hasn't got a vtable with which to access it.

You cannot get around the virtual method call, all typeid is doing on a dynamic type is following the vtable. Use compile-time templates if you want to avoid this.

We can exemplify this by outputting in a method instead of the constructor. Contrast the following code with and without virtual.

#include <iostream>

class Base
{
public:
    Base() {
        std::cout << "In ctor: " << typeid(*this).name() << std::endl;
    }

    void Foo() {
        std::cout << "In method: " << typeid(*this).name() << std::endl;
    }

    /*virtual*/ ~Base() = default;
};

class Derived : public Base {};
class MoreDerived : public Derived {};

int main() {
    Base b;
    Derived d;
    MoreDerived md;
    
    b.Foo();
    d.Foo();
    md.Foo();
}

When not polymorphic:

In ctor: Base
In ctor: Base
In ctor: Base
In method: Base
In method: Base
In method: Base

Polymorphic:

In ctor: Base
In ctor: Base
In ctor: Base
In method: Base
In method: Derived
In method: MoreDerived
Peru answered 19/9, 2023 at 13:31 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.