Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

dynamic

rtti

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.

like image 346
rainer Avatar asked Aug 25 '16 07:08

rainer


People also ask

Can a derived class pointer point to a base class object?

Derived class pointer cannot point to base class.

What can hold the address of a pointer to the base class A base class object by derived class object?

Explanation: A base class pointer can point to a derived class object, but we can only access base class member or virtual functions using the base class pointer because object slicing happens when a derived class object is assigned to a base class object.

How do you check if an object belongs to a certain class in C++?

C++ has no direct method to check one object is an instance of some class type or not. In Java, we can get this kind of facility. In C++11, we can find one item called is_base_of<Base, T>. This will check if the given class is a base of the given object or not.

Can pointer to derived class be created?

A derived class is a class which takes some properties from its base class. It is true that a pointer of one class can point to other class, but classes must be a base and derived class, then it is possible.

How to get the type of an object at runtime?

At runtime, you need to interrogate dynamically the type of particular class. Use runtime type identification (commonly referred to as RTTI) to query the address of the object for the type of object it points to. Example 8-6 shows how. Example 8-6. Using runtime type identification

What is base class pointer in C++?

In this tutorial, we will discuss the concept of the base class pointer in C++ and its implementation in the program. What is a pointer? A pointer is a type of variable or a memory location that contain the address of other variables or object.

What is a pointer to a derived class?

A pointer to derived class is a pointer of base class pointing to derived class, but it will hold its aspect. This pointer of base class will be able to temper functions and variables of its own class and can still point to derived class object. Writing code in comment?

How to access the variable of base class in C++?

To access the variable of the base class, base class pointer will be used. So, a pointer is type of base class, and it can access all, public function and variables of base class since pointer is of base class, this is known as binding pointer. In this pointer base class is owned by base class but points to derived class object.


2 Answers

There's something like typeid http://en.cppreference.com/w/cpp/language/typeid, which applied to polymorphic expression will evaluate in a runtime to its type representation.

Following wiki example: https://en.wikipedia.org/wiki/Run-time_type_information#dynamic_cast

#include <iostream> 
#include <typeinfo>    // for 'typeid'

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

class Employee : public Person {
};

int main() 
{
    Person person;
    Employee employee;
    Person* ptr = &employee;
    Person& ref = employee;
    // The string returned by typeid::name is implementation-defined

    // Person (statically known at compile-time)
    std::cout << typeid(person).name() << std::endl;   

    // Employee (statically known at compile-time)
    std::cout << typeid(employee).name() << std::endl; 

    // Person* (statically known at compile-time)
    std::cout << typeid(ptr).name() << std::endl;      

    /* Employee (looked up dynamically at run-time
     * because it is the dereference of a
     * pointer to a polymorphic class) */
    std::cout << typeid(*ptr).name() << std::endl;     

    // Employee (references can also be polymorphic)        
    std::cout << typeid(ref).name() << std::endl;      
}
like image 152
SzymonPajzert Avatar answered Oct 18 '22 20:10

SzymonPajzert


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;
}
like image 26
rustyx Avatar answered Oct 18 '22 21:10

rustyx