Monday, December 6, 2010

RTTI examples

#include <iostream>
#include <typeinfo> // Header for typeid operator
using namespace std;

// Base class
class MyBase {
  public:
    virtual void Print() {
        cout << "Base class" << endl;
    };
};

// Derived class
class MyDerived : public MyBase {
  public:
    void Print() {
        cout << "Derived class" << endl;
    };
};

int main()
{
    // Using typeid on built-in types types for RTTI
    cout << typeid(100).name() << endl;    
    cout << typeid(100.1).name() << endl;

    // Using typeid on custom types for RTTI
    MyBase* b1 = new MyBase();
    MyBase* d1 = new MyDerived();

    MyBase* ptr1;
    ptr1 = d1;

    cout << typeid(*b1).name() << endl;
    cout << typeid(*d1).name() << endl;
    cout << typeid(*ptr1).name() << endl;

    if ( typeid(*ptr1) == typeid(MyDerived) ) {
    cout << "Ptr has MyDerived object" << endl;
    }

    // Using dynamic_cast for RTTI
    MyDerived* ptr2 = dynamic_cast<MyDerived*> ( d1 );
    if ( ptr2 ) {
    cout << "Ptr has MyDerived object" << endl;
    }
}

OUTPUT:
i
d
6MyBase
9MyDerived
9MyDerived
Ptr has MyDerived object
Ptr has MyDerived object

RTTI -Notes - dynamic_cast

Runtime Type Information (RTTI) is the concept of determining the type of any variable during execution (runtime.) The RTTI mechanism contains:
  • The operator dynamic_cast
  • The operator typeid
  • The struct type_info 

Dynamic_cast

The dynamic_cast can only be used with pointers and references to objects. It makes sure that the result of the type conversion is valid and complete object of the requested class. This is way a dynamic_cast will always be successful if we use it to cast a class to one of its base classes. Take a look at the example:

 class Base_Class { };
 class Derived_Class: public Base_Class { };

 Base_Class a; Base_Class * ptr_a;
 Derived_Class b; Derived_Class * ptr_b;

 ptr_a = dynamic_cast<Base_Class *>(&b);
 ptr_b = dynamic_cast<Derived_Class *>(&a);
The first dynamic_cast statement will work because we cast from derived to base. The second dynamic_cast statement will produce a compilation error because base to derived conversion is not allowed with dynamic_cast unless the base class is polymorphic.
If a class is polymorphic then dynamic_cast will perform a special check during execution. This check ensures that the expression is a valid and complete object of the requested class.
Take a look at the example:

// dynamic_cast
#include <iostream>
#include <exception>
using namespace std;

class Base_Class { virtual void dummy() {} };
class Derived_Class: public Base_Class { int a; };

int main () {
  try {
  Base_Class * ptr_a = new Derived_Class;
  Base_Class * ptr_b = new Base_Class;
  Derived_Class * ptr_c;

     ptr_c = dynamic_cast< Derived_Class *>(ptr_a);
     if (ptr_c ==0) cout << "Null pointer on first type-cast" << endl;

     ptr_c = dynamic_cast< Derived_Class *>(ptr_b);
     if (ptr_c ==0) cout << "Null pointer on second type-cast" << endl;

   } catch (exception& my_ex) {cout << "Exception: " << my_ex.what();}
  return 0;
}
In the example we perform two dynamic_casts from pointer objects of type Base_Class* (namely ptr_a and ptr_b) to a pointer object of type Derived_Class*.
If everything goes well then the first one should be successful and the second one will fail. The pointers ptr_a and ptr_b are both of the type Base_Class. The pointer ptr_a points to an object of the type Derived_Class. The pointer ptr_b points to an object of the type Base_Class. So when the dynamic type cast is performed then ptr_a is pointing to a full object of class Derived_Class, but the pointer ptr_b points to an object of class Base_Class. This object is an incomplete object of class Derived_Class; thus this cast will fail!
Because this dynamic_cast fails a null pointer is returned to indicate a failure. When a reference type is converted with dynamic_cast and the conversion fails then there will be an exception thrown out instead of the null pointer. The exception will be of the type bad_cast.
With dynamic_cast it is also possible to cast null pointers even between the pointers of unrelated classes.
Dynamic_cast can cast pointers of any type to void pointer(void*).

RTTI - Notes - typeid

type_id:
In C++, the typeid keyword is used to determine the class of an object at runtime.
#include <iostream>
#include <typeinfo>  //for 'typeid' to work
 
class Person {
public:
   // ... Person members ...
   virtual ~Person() {}
};
 
class Employee : public Person {
   // ... Employee members ...
};
 
int main () {
   Person person;
   Employee employee;
   Person *ptr = &employee;
   // The string returned by typeid::name is implementation-defined
   std::cout << typeid(person).name() << std::endl;   // Person (statically known at compile-time)
   std::cout << typeid(employee).name() << std::endl; // Employee (statically known at compile-time)
   std::cout << typeid(ptr).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)
}