24 RTTI (Run Time Type Information)

Bhushan Trivedi

Introduction

 

RTTI or Run Time Type Information is another feature that allows flexibility to the C++ programmer. In this and the next module, we will be throwing some light on how RTTI functions and how a programmer can take advantage of it.

 

RTTI is used to find out the type of an object at runtime. This is a very handy tool for making sure the object type is exactly learned before any assignment is made. We have already seen the consequences of assigning a base class object to derived and a derived class object to the base. This system, being a runtime component, impacts the performance of the system to such a notable extent that it is usually kept disabled by default. If we want our solution to use RTTI, we ought to enable the RTTI. Some applications can sustain performance penalty but demand better decision-making process at runtime. For example, applications which talk to other applications over the web. The performance of the system does not matter as the delay is largely decided by the latency of the client server operation. However, if the application is about fetching data from the local database server or communicating with the local protocol stack etc., performance cannot be ignored. We assume that the programs that we are describing in these two modules are of the type where performance is a secondary1. If you are anyway planning to use virtual functions, the overhead needed by the RTTI is not much and one can use it without being concerned much about performance.

 

To manage RTTI, ANSI C++ standard provided two keywords, typeid which basically indicates the type of the object, and dynamic_cast, a casting operator to cast only when the casting is safe. We will introduce typeid in this module and dynamic_cast in the next module.

 

Virtual functions and polymorphic objects

 

In the previous module, we have seen a few examples of a base class pointer pointing to multiple types of derived class objects and manipulate them without explicitly mentioning the object types. Such objects can be manipulated using virtual functions and thus are known as polymorphic objects. They are obtained by dereferencing base class pointers like using *pShape; That means any object that belongs to a class that contains at least one virtual function, is a polymorphic object. All other objects belong to any class that is derived from

 

1 You probably now able to guess why C++ is primarily used for database, OS, and compiler related programming and Java are preferred for web-based programming. that base class, are also possible to be manipulated using the base class pointer or reference assume that there is a function Print available for printing. Where can we place thatfunction? If we place that function in the News class, we will have Print () function available to all the classes of type Text and nonText. What we want is to do is not to have print option for nonText so we may decide to place it in the Text class, so, it is only available to Text class and the other derived classes from it and thus they are also polymorphic objects

 

//Program 26.1 //block 1
//NewsHierarchy.cpp
#include <iostream>
#include <typeinfo>
#include <string>
using namespace std;

One can easily solve problems like having a draw () function having a hierarchy of virtual functions. However, some problems demand more. Let us take an example to understand when the virtual functions cannot solve the problem completely and we need an additional service to manage.

 

We have hinted about a case of having news items coming online from various reporters to the system. The system will have to decide what to display based on what user’s preferences are. We also assume that all important news, as per user’s own preferences, are being flashed on the screen in the form of headlines. When user double clicks on the item, it is displayed in detail. The user may also right click on it to have various operations, for example, sending it to some other user, store it for later viewing, copy it, move it to a specific folder on the device or even print it. When there are many different types of news items possible, we may decide to divide them into a hierarchy and provide virtual, pure virtual and non-virtual functions for all these operations, based on the need. For example, sending a mail to somebody, with this news as an attachment works the same for all types of news, so we will only define a non-virtual function for it. Another critical operation is printing. Not all news items are printed the same fashion and we need specific treatment for specific types of news, so we need a virtual function for printing. We may even go for pure virtual implementation when the base class printing is not sufficient for other news items to inherit and use. A typical requirement from the user is, he would like to print only items without images. So any news with images should not have printing option when the user presses the right click.

 

How can that be achieved? Assume that we have a base class News. The incoming news is divided into two broad groups, Text and nonText. Thus the News class inherits into Text and nonText. The nonText class indicates the news with non-text items including images. Also,

 

assume that there is a function Print available for printing. Where can we place that
//Program 26.1 //block 1 function? If we place that function in the News class, we will have
//NewsHierarchy.cpp Print () function available to all the classes of type Text and
#include <iostream> nonText. What we want is to do is not to have print option for
#include <typeinfo>
#include <string> nonText so we may decide to place it in the Text class, so, it is only
using namespace std;
available to Text class and the other derived classes from it and

Another critical operation is printing. Not all news items are printed the same fashion and we need specific treatment for specific types of news, so we need a virtual function for printing. We may even go for pure virtual implementation when the base class printing is not sufficient for other news items to inherit and use. A typical requirement from the user is, he would like to print only items without images. So any news with images should not have printing option when the user presses the right click.

 

How can that be achieved? Assume that we have a base class News. The incoming news is divided into two broad groups, Text and nonText. Thus the News class inherits into Text and nonText. The nonText class indicates the news with non-text items including images. Also,

 

assume that there is a function Print available for printing. Where can we place that
//Program 26.1 //block 1 function? If we place that function in the News class, we will have
//NewsHierarchy.cpp Print () function available to all the classes of type Text and
#include <iostream> nonText. What we want is to do is not to have print option for
#include <typeinfo>
#include <string> nonText so we may decide to place it in the Text class, so, it is only
using namespace std;
available to Text class and the other derived classes from it and

One can easily solve problems like having a draw () function having a hierarchy of virtual functions. However, some problems demand more. Let us take an example to understand when the virtual functions cannot solve the problem completely and we need an additional service to manage.

 

We have hinted about a case of having news items coming online from various reporters to the system. The system will have to decide what to display based on what user’s preferences are. We also assume that all important news, as per user’s own preferences, are being flashed on the screen in the form of headlines. When user double clicks on the item, it is displayed in detail. The user may also right click on it to have various operations, for example, sending it to some other user, store it for later viewing, copy it, move it to a specific folder on the device or even print it. When there are many different types of news items possible, we may decide to divide them into a hierarchy and provide virtual, pure virtual and non-virtual functions for all these operations, based on the need. For example, sending a mail to somebody, with this news as an attachment works the same for all types of news, so we will only define a non-virtual function for it.

 

Another critical operation is printing. Not all news items are printed the same fashion and we need specific treatment for specific types of news, so we need a virtual function for printing. We may even go for pure virtual implementation when the base class printing is not sufficient for other news items to inherit and use. A typical requirement from the user is, he would like to print only items without images. So any news with images should not have printing option when the user presses the right click. How can that be achieved? Assume that we have a base class News. The incoming news is divided into two broad groups, Text and nonText. Thus the News class inherits into Text and nonText. The nonText class indicates the news with non-text items including images. Also,  not to any class that inherits from nonText.

#define SEND 1

#define STORE 2

#define PRINT 3

So we may decide to place the Print () function in the Text class and now the News class. However, this is not going to work for us when we use the pointer to News class to access the news items. The Print () function is just not available. Let us look at the example to illustrate the point. This program is a skeleton of the actual code. We have omitted most of the content to only highlight the part we wanted to address. Look at the first block. It defines three constant values which we are going to use, in the switch case structure for specific operations.

The block 2 defines the News hierarchy that we have discussed above. It defines the base class News with three virtual functions, Send, Store and ~News(), the destructor. The Text class inherits all those functions plus an additional function for printing.

 

Similarly, the nonText class also inherits from the News class but does not define a print function.

//Program 26.1 block 2

//NewsHierarchy.cpp

class News

{

public:

virtual void Send(string mAddress){};

virtual void Store(string Location = “/home/C++”){}; virtual ~News() = 0;

};

News::~News()

{ }

class Text : public News

{

public:

void Send(string Address) { /*code for sending mail */ } void Store() { /* code for storing news */ }

virtual void Print() { /* code for printing*/ }; virtual ~Text () {};

};

class nonText : public News

{

public:

 

void Send(string Address) {/* code for sending */}; // other functions but no print

};

 

//…..other code

Finally, we have a code for a right click () operation that   works on this hierarchy of news. When it is executed, we have some options to execute. Unfortunately, the argument passed to the rClick() function is a pointer to base class News as well as the choice. The pointer to a base class cannot access print() as it is not defined in the base class. Thus, we cannot provide an option to print for a base class pointer. How can we solve this problem? We would like to have preferably the code shown in rClick() function which provides an option to print but we cannot do that.

 

One of the solutions is called downcasting (casting a pointer to base class object into a pointer to a derived class). That means if we can cast the News pointer to Text pointer we can enable and execute the print function. However, this casting is meaningful and correct only if the base class pointer is actually pointing to a derived class object or any other object inherited from the derived class object.

Downcasting demands us to know the type of the object the pointer is pointing to at runtime. Virtual functions cannot decide that and thus virtual or pure virtual functions are of little use

in this case. For other functions like Send and Store, there is no such problem, we can define them in the base class and continue defining them in the subsequent classes if need be or use continue using the same option provided in the base class, it is just fine. The RTTI comes to the rescue here. What we can do is, we can test the incoming news being an object of a typical class as it comes along in the right click function itself. If the object belongs to the category Text, we will enable it otherwise disable it. RTTI helps this type of checking using typeinfo object and typeid parameter.

 

//Program 26.1 block 3

void rClick(News & r_News, int Choice)

{

string mAddress;

switch(Choice)

{

case SEND:

cout << “Mail address please “;

cin >> mAddress;

r_News.Send(mAddress);

break;

case STORE:

r_News.Store();

break;

//following is not acceptable

 

/*               case PRINT:

r_News.Print();

break;

*/

default:

cout << “in default “;

break;

}

if (typeid(r_News) == typeid(Text))

{

// enabling print now

 

Text & PrintableNewsItem = (Text &)r_News; // this is safe now!

PrintableNewsItem.Print();

}

}

int main()

{

Text t_News;

rClick(t_News, 1);

Let us try to see how RTTI can help us. When RTTI is enabled, every class has a typical typeinfo object associated with it, containing the information about the type. RTTI provides a typeid operator which

returns the typeinfo object associated with the object under consideration and let us know the type of that object. The next section describes these two items in more detail, and also how they can help us solve the problem.

 

The typeinfo object and typeid operator

 

Before we begin talking about RTTI, there are a few things you must notice.

  1. RTTI must be enabled for doing exercises based on this module.
  2. You must have a hierarchy which is designed properly and there is a base class pointer designed to access the content of the derived class objects at runtime
  3. RTTI has to deal with polymorphic objects; i.e. dereferencing our base class pointer to operate on
  4. including the <typeinfo> header file in the program
  5. link the typeinfo library. This is done automatically once the RTTI is enabled. Once it is included, the compiler automatically creates a typeinfo object. The compiler also creates a vtble (even when no virtual function is defined), and the first entry is made to point to this typeinfo object.
  6.    The typeid operator is used to get this typeinfo object associated with any object.

The typeinfo object contains three functions which we can use in our program. Here they are.

  1. The first function is called name (), it returns the name of the object.
  2. The second function is the overloaded ==, this is used to compare one object with another, to see if they are of the same type
  3. The third function is exactly opposite, overloaded operator !=, which checks the inequality of two objects that we are comparing.

Interestingly, one can use typeinfo for non-polymorphic objects as well. Let us see how that can be done. Look at the program 26.2 block 1. There is three type of objects that we are planning to see. First is int, second is char and third are t_Class. The i_1 and i_2 are ints, c_1 is char while t_SC is of t_Class. Look closely at the statement which uses the name() attribute of the typeinfo object returned by typeid operator applied to these objects.

 

//Program 26.2 Block 1

//typeinfo.cpp

#include <iostream>

#include <typeinfo>

#include <string>

using namespace std;

class t_Class

{

int i_1;

public:

t_Class(int t_i_2)

{

i_1 = t_i_2;

}

};

int main()

{

int i_1, i_2;

char c_1 = ‘a’;

t_Class t_SC(5);

cout << “i_1 type is “;

cout << typeid(i_1).name(); cout << “\n”;

if (typeid(i_1) == typeid(i_2))

{

cout << “i_1 and i_2  have same type\n”;

}

if (typeid(i_1) != typeid(c_1))

{

cout << “i_1 and c_1 aren’t of same type\n”;

}

cout << “object t_SC is of type “; cout << typeid(t_SC).name()<< “\n”;

}

Interestingly, one can use typeinfo for non-polymorphic objects as well. Let us see how that can be done. Look at the program 26.2 block 1. There is three type of objects that we are planning to see. First is int, second is char and third are t_Class. The i_1 and i_2 are ints, c_1 is char while t_SC is of t_Class. Look closely at the statement which uses the name() attribute of the typeinfo object returned by typeid operator applied to these objects.

 

cout << typeid(i_1).name();

 

you can see that the output is int which is the type for i_1.

 

Another is about == operator overloaded for the typeinfo object

 

if (typeid(i_1) == typeid(i_2))

 

You can see that the types are same and thus this should yield true, which it does.

The third attribute is overloaded != operator which is used in the following statement

 

if (typeid(i_1) != typeid(c_1))

 

You can see that the type of int and char are not similar so the output should result in a message accordingly.

// Program 26.2 block 2
Finally, we apply the typeid to an object, i_1 type is int
i_1 and i_2 have the same type
however, this object is not polymorphic so it i_1 and c_1 aren’t of the same type
object t_SC is of type t_Class

is not much of a use except for the fact that it shows how one can use typeid for an object.

 

cout << typeid(t_SC).name()<< “\n”;

 

Above statement displays that it is of type t_Class2. Now it is the time for using the typeid for the purpose it is designed with, using it for polymorphic objects.

 

Using typeid for polymorphic objects

 

We can use typeid for polymorphic objects and that is really unique and useful operation. Let us try to understand how that can help us. Look at the following code. There is a macro defined for demangling the names typeid returns when used with Apple’s Xcode IDE which in turn uses gcc compiler. For windows OS and few other compilers, there is no need to demangle and original code with typeid (Object).name () just works.

 

Here is the code described in Program 26.3. We have defined the same figures now but with Parameterized constructors for a change. The code is described in a single block and it contains the hierarchy and four line of main () code.

 

The output does not need much explanation. The typeid is now used for the polymorphic pShape pointer. You can see that the typeid returns correct name of the object where pShape points to.

 

//Program 26.3 Block 1

 

//PolymorphicTypeid.cpp

 

#include <iostream>

#include <typeinfo>

#include <string>

#include <cxxabi.h>

using namespace std;

# define type_name(X) __cxxabiv1::__cxa_demangle(typeid(X).name(),0,0,0)

#include <iostream>

using namespace std;

class Shape

 

{   int l_Style;

int fill;

 

public:

virtual void draw() { cout << “It is Shape \n”; }

 

class Circle:public Shape

{    int R, X, Y;

 

public:

 

Circle (int t_R, int t_X, int t_Y): R(t_R),X(t_X),Y(t_Y) {};

 

};

void draw() { cout << “It is Circle \n”; }

class Square: public Shape

 

{   int Len, X, Y;

public:

 

};

  • 2 Sometimes the output is displayed in mangled form and int is displayed as i, char is displayed as c and so on. Every specific compiler has some means of converting it to demangled form. For gcc, one must write a.out | c++filt to get the demangled output.

Square (int t_Len, int t_X, int t_Y):Len(t_Len),X(t_X),Y(t_Y) {}          };

 

int main()

 

{

Shape t_Shape, *pShape = nullptr;

Circle t_Circle (5,2,2);

Square t_Square (5,5,5);

pShape = &t_Shape;

cout << “*pShape’s type is ” << type_name(*pShape) << “\n”;

pShape = &t_Circle;

cout << “*pShape’s type is ” << type_name(*pShape)<< “\n”;

pShape = &t_Square;

cout << “*pShape’s type is ” << type_name(*pShape) << “\n”;

 

}

output: –

*pShape’s type is Shape

*pShape’s type is Circle

 

*pShape’s type is Square

 

Program ended with exit code: 0

 

Looking at the complete code, you can clearly see that the code like typeid(pShape).name() yield the name of the object the pShape is pointing to. Every time pShape points to a different item, the typeinfo object it holds contains the information about the object which it points. This is done at run time.

 

Solving the news problem

 

How can we use typeid to solve our News problem? Here is an answer. The code casts the

 

if (typeid(r_News) == typeid(Text))

 

{

// print now

Text & PrintableNewsItem = (Text &)r_News; // this is safe now!

PrintableNewsItem.Print();

}

 

r_News item into a Text & so now we have the print function possible to be used. If the typeid of the news matches with the typeid of Text, the printing is enabled, otherwise not. Problem solved! You can see that the code is quite simplified with the use of typeid. Only if the type of the object is Text, the reference to News is cast into a reference of Text. Once we have the reference to Text, as the print function is defined in the class, the print is enabled and can also be executed if the user chooses so.

 

The typeid can be used to check the type of polymorphic objects and can safely cast the base class pointer into a derived class pointer (or reference) when the base class pointer is actually pointing to the derived class object. When the typeid is not used, this process is quite risky. A casting of a base class pointer to a derived class pointer enhances the scope of the pointer and if the base class pointer is not pointing to a derived class object or pointing to some other derived class object, the consequences are disastrous. When we use typeid, we can

check at run time if the base class pointer is pointing to the object we want our pointer to cast to, and only then we do so. We have seen earlier this that process is known as Downcasting and is safe when we use that after ensuring the compatible types.

 

Is the problem really solved? It seems the problem is solved for the time being but there is a catch which we need to address. We will soon see it but let us have a little more detailed introduction to how typeid functions coming next.

 

Applying typeid

 

If you have seen closely, we are applying typeid to two different types of things, a class or an object. For example, when we write code as follows

 

(typeid(r_News) == typeid(Text)

 

The LHS of this statement applies the typeid to a reference to an object while the RHS is applied to a class name. The LHS returns the typeinfo object associated with the r_News object. The RHS returns a typeinfo object associated with a class Text. In fact, there is only one typeinfo object associated with the class, whenever the typeid operator is applied to an object, it returns the typeinfo object associated with the class the object belongs to. Thus, when a r_News object is passed, the typeid returns the typeinfo object associated with the class associated with the r_News item at that point in time.

 

Both the operators == and != are overloaded and thus we can compare typeinfo objects.

 

Usefulness of RTTI

 

We mentioned about design in the 23rd and 24th module. The issue continues here. Whenever a problem is to be solved, the user has both the options open to him, when he decides to use OO programming.

 

Every system design has to honor the library creator and library user roles and dilemmas associated with that. Let us try to understand. When a system with a graphics library is to be designed, it contains classes like Shape, circle, and rectangle etc. are designed, the library designer who designs hierarchy or at least base classes, provide that as a platform to the library user. The library user uses and extends that hierarchy for his own use. For example, when a library designer provider provides these basic shapes and related functions, library user might design a video game based on it or design a system for showing the prototype of the building to the potential customer. Some other user might use the same hierarchy to design 3D figures based on these two dimension figures, for example, extend the two-dimensional point to a 3D point with an additional dimension and extending the draw () and other functions like fillColor () or calcArea () or duplicate () etc. When the designers design their hierarchy and set of functions, they make sure that their design is general enough to be

applied in all such diversified cases. The problem is that they do not have a clear idea of how their system will be put to use.

 

When the programmer codes for a specific system, he has a clearer idea about his own system requirements and also how his own hierarchy should be designed. He expects the designer’s hierarchy to be available in the most general form and he sometimes also wants to extend it in all possible manner that he can.

 

However, when the programmer who is a library user, starts working with the hierarchy, they need to check specific types and take appropriate actions based on the type, for example, our square class might need two more functions, grow and shrink. If there are few classes derived from the square class, they might also inherit them.

 

The user needs to add operations on base class as well, for example, requiresMemory() operation returns the memory needed. Only if the library designer provided such facility, he might be able to add. Otherwise, he might inherit Shape class into MyShpae and add that function additionally. However, how to provide other members of the hierarchy is a tricky business. There are a few ways to do that, one of them involves using multiple-inheritance. However, we again refrain from veering off the focus of our course and do not discuss it further.

 

An excellent example of extending the functionality involves an operation known as a tween in the multimedia parlance. One of them is known as motion tween. When an image is at one place on the left side of the screen and it has to move to some other place on the screen, on the right side. A user specifies both positions, beginning and ending positions, and the system generates all intermediary images, so much so that when somebody looks at those images in real time, they feel that the object is moving from left to right. Another tween operation involves shape twin. There are two images, let us call them image1 and image2, designed and placed on the screen and some points of importance are also noted down. The shape tween generates intermediary images for converting image-1 to image-2. Sometimes when the programmer wants to show a man’s face turns into tiger’s, he may use shape tween with images of man’s face and tiger’s. The programmer should be able to add such functions with any images of their classes of the hierarchy. You can see that having virtual functions might not just work, the programmer needs to find out exactly where the pointer is pointing to and invite motion or shape tween based on the requirement.

 

One more interesting use of RTTI is in working with templates. We know that when we write templatized code, we need to write generic code which can work with any object. However, sometimes some decisions are to be made based on the type of the object we are dealing with. The typeid can be used with templates and thus can be useful in such a case as well. We will see an example of the same.

Sometimes the programmers need to patch the system for some ad hoc operation. For example, consider an Intrusion Detection System checking for a typical set of attacks and working successfully. An admin realizes that some strange activity going on. He would like to do an additional operation on the packets, he might augment the IDS with a typical patch code, with checking the type of the packet and taking an action based on the type it belongs to. He does not need to do anything with the actual system code, this code can work independently and run independently.

 

One of the biggest advantages of RTTI is that it allows using an operator called dynamic_cast to check something for an entire hierarchy, unlike virtual functions and typeid operator. The power of dynamic_cast will be shown using an example in the next module.

 

References
1.Programming with ANSI C++, Bhushan Trivedi, Oxford University Press
2.www.stroustrup.com, homepage of Bjarne Stroustrup, the creator of C+