Search code examples
c++dynamicrtti

Get object's type from pointer to base class at runtime


I'm working with a class library where all classes are, directly or indirectly, derived from a base class Base and have a name. The library provides a facility to search for objects by a name, which will return a Base*.

Is there any way to find the type of the returned object without checking all possibilities using dynamic_casts as I did in the following example? I'd like to avoid that, if at all possible, since the derived classes have template parameters, which makes for quite a few possibilities.

It would also be fine if I were at least able to find out the class type (T1 or T2, in the example below) without knowing the template type, ie. do something like a dynamic_cast<T1<i_dont_care>*>.

#include <iostream>

using namespace std;

class Base {
public:
    virtual ~Base() {}
};

template <typename T> class T1 : public Base {};
template <typename T> class T2 : public Base {};

Base *find_by_name() {
    return new T2<int>();
}

int main() {
    Base *b = find_by_name();

    if (T1<int> *p = dynamic_cast<T1<int>*>(b)) {
        cout << "T1<int>" << endl;
        // do something meaningful with p
    } else if (T1<double> *p = dynamic_cast<T1<double>*>(b))
        cout << "T1<double>" << endl;
    else if (T2<int> *p = dynamic_cast<T2<int>*>(b))
        cout << "T2<int>" << endl;
    else if (T2<double> *p = dynamic_cast<T2<double>*>(b))
        cout << "T2<double>" << endl;
    else
        cout << "unknown" << endl;

    delete b;

    return 0;
}

Note that the above example is simplified, ie. in each if I'd do something meaningful with p.

I do realize that this is bad design from the very start, however I'm stuck with this library and there's also no way for me to change its implementation.


Solution

  • There is a typeid operator, which returns an instance of std::type_info, with which you can get the name of the type.

    Not sure if that will help you though. First, the returned name is not guaranteed to be the same across implementations. Second - what would you do once you have the name? You'd compare it with your pre-defined names probably, but that is probably slower than a bunch of dynamic_cast's.

    Without type support built into your Base class or a new intermediate layer of hierarchy, dynamic_cast is your best choice. In reality it will be very fast (usually just a single compare instruction).

    By intermediate layer I mean:

    class Base {
    public:
        virtual ~Base() {}
    };
    
    class T1Base : public Base {};
    class T2Base : public Base {};
    
    template <typename T> class T1 : public T1Base {};
    template <typename T> class T2 : public T2Base {};
    
    int main() {
        Base *b = find_by_name();
    
        if (dynamic_cast<T1Base*>(b))
            cout << "T1" << endl;
        else if (dynamic_cast<T2Base*>(b))
            cout << "T2" << endl;
        else
            cout << "unknown" << endl;
    
        delete b;
    
        return 0;
    }