Advertisement

Any pattern for runtime type check?

Started by January 10, 2001 08:08 AM
12 comments, last by haigu 24 years ago
say if 2 classes are inherited from 1 base class, class base; class child1 : public base; class child2 : public base; I want to identify them at runtime, given a base pointer,base *ptr. One way is to add a virtual function, virtual int base::gettype(), the return number can be defined as an enum. but if we add any inherited new classes, we have to modify the header file and add an enum value, and we have to recompile base class and all its children. this is ugly is there any one can give a suggestion?
Ò»½«¹¦³ÉÍò¹Ç¿Ý
This is what dynamic cast is for.

For example:

  MyBase *ptr = new Derived;// is it a MyOtherDerivedMyOtherDerived *odPtr = dynamic_cast<MyOtherDerived*>( ptr );// odPtr will be set to null because it is not a MyOtherDerived  


You can also do this with references, but a failed cast on a reference type throws a bad_cast exception

However, doing this too much amounts to case analysis on class type. Which is better handled by putting such behavior in virtual functions.
Advertisement
Someone please correct me if I am worng, but I think if you have a good OOD you would not ever need to do that. (That's not to say that its never desirable,)

Ideally, you want to treat sub-classes transparently, and a RTTC is not transparent

What are you trying to accomplish with the RTTC?

Edited by - Magmai Kai Holmlor on January 10, 2001 12:21:20 PM
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
The dynamic_cast<> operator stuffs in a bunch of code for run-time typecasting, and I suppose it can be used for run-time type checking, but it is true that this can be avoided by good OOD. Reflection, on the other hand, can be very useful for writing scripting engines, etc. Java has built-in reflection. Check the java.lang.reflect docs for information on this.

p
p
It sounds like the typeid operator is what you're looking for. It's used like this:

const typeinfo &ti = typeid(someDerivedObject);
if(strcmp(ti.name(), "Derived1") == 0) { /* do something */ }
else if(strcmp(ti.name(), "Derived2") == 0) { /* something else */}

David

Edited by - fugue88 on January 10, 2001 5:44:47 PM
quote:
Someone please correct me if I am worng, but I think if you have a good OOD you would not ever need to do that. (That''s not to say that its never desirable,)


I think that it breaks oo-principles if base class needs to know type of derived class, but in case of checking the type of sibling class, it''s sometimes needed.

For instance if you have declared abstract classes which present some interaction in between, in implementation there is sometimes need to check that the type of passed interface belongs to the same implementation by using dynamic_cast.

I faced this problem when I declared abstract class hierarchy for graphics surface handling (relative to surfaces in DirectX) and specified some simple graphics operations for them (for instance virtual blit-method which copied content of a surface to another). Naturally if I make both OpenGL and DX implementations for the surface class hierarchy, I could give OpenGL surface as argument to blit method of DX surface whatfor I need dynamic_cast to check that the type of passed surface is correct.

Cheers, Altair


"Only two things are infinite, the universe and human stupidity, and I''''m not sure about the former." - Albert Einstein
"Only two things are infinite, the universe and human stupidity, and I'm not sure about the former." - Albert Einstein
Advertisement
Here''s a situation:

DoAction(int action, GameObject subject){  switch(action)  {  case KILL:    if (subject instanceof Monster)      ((Monster)subject).kill(player);    else ReportError();  case OPEN:    if (subject instanceof Door)      ((Door)subject).open(player);    if (subject instanceof Chest)      ((Chest)subject).loot(player);  // ...} 


This is Java code and it illustrates how knowing the exact subclass can be usefull. The instanceof operator is built into Java. It is interresting because (new Door() instanceof GameObject) is also true. To get exact class type, you can try this.GetClass().GetName().equals("GameObject"); which is true for a GameObject but not for a subclass of GameObject.

p
p
Epiphany: Try this in C++:
#define DECLARE_CLASS(classname) class classname { \  public: static virtual const char *type() { return #classname; }#define END_CLASS } 

Now your class:
class MyClass {...} 

will look like this:
DECLARE_CLASS(MyClass)...END_CLASS 

and this class has a static operator type() that returns a string. The catch is, if you don''t use this method for all subclasses of MyClass, some of them will return the value of the closest superclass that does use these macros.

Here''s a macro to make the code more intuitive:
#define IsClass(object, classname) (_stricmp(object.type(), #classname) == 0) 


p
p
Regarding this DECLARE_CLASS macro etc, you can get this kind of functionality via macro tricks in a form which doesn''t require using strings for identification, if you use some kind of "class object" mechanism (analagous to C++ RTTI''s "type_info" mechanism, but something more full featured and closer to an actual reflection API).

Coincidentally, this is exactly the kind of thing that I wrote up in the "Reflective Factory" pattern.

You can find Reflective Factory in the patterns database here on gamedev.net.

Ok ... listen up ... this is the definative answer. I know that sounds arogant .. but I just want to make it clear that this is an answer I know ... not one I''m thinking about (I often get into general discussions that are not about knowledge but instead ideas) ... this one is pure knowledge.

C++ has two distinct apects to it''s RTTI (run time type identification) system. They are dynamic_cast and typeid. Both are operators built into the language (keywords).

dynamic_cast will do what you want to do, but it is not the right way ... since .. as pointed out .. it generates extra code to actually perform a cast (dynamic casting requires looking up an offset for the derived class ... since c++ supports multiple inheiritance ... even Java''s interfaces add this overhead). dynamic_cast is supposed to be used when you have a pointer to a base class which you think is actually a member of a particular derived class, and want to use it''s special (derived) features if it is. For detecting the class of an object, see below.

typeid returns a "const type_info&" ... which, in thes cases where the compiler can solve the type at compile time, generates no real overhead. In the cases where it needs run time information to resolve the type, it has slightly better performance (one function call less) than using a virtual function. Two people already sugested using this operator ... so why am I posting? Because they used it correctly and then used the resulting type_info wrong ... they threw away all the performance gain in a terribly inefficient call two two functions (name) and then a third (strcmp). The proper way to compare type_info''s is with the == operator just like anything else ... and it''s great because internally it uses a simple int or pointer comparison (no the good compilers).

here''s a sample of the absolute faster / best way to test if the type of an objects is Child2.

  // assuming we have this:  Base *objectif(typeid(object) == typeid(Child2))    {    // then it is a member of Child2    }  


if you need to do many tests in a row ... make sure and call the base typeid only once .. and store it in a local ... as follows.

  type_info objType = typeid(object);if(objType == typeid(Child1))    {    // do Child1 stuff    }else if(objType == typeid(Child2))    {    // do Child2 stuff    }else    {    // unknown type ... do generic stuff here    }  


Please note ... these systems work great .. and ARE efficient ... but there is one situation when you have to use dynamic_cast instead .... and that is one you have a multilevel heirarchy and need to find out if the derived type is ANY of a set of derived classes ... like if you have base class Object, derived class Character, and further derivatives Player, and NPC. If you need to find out if an Object* points to a Player ... then you have to do

  if(dynamic_cast<Player*>(myObjectPointer) != NULL)    {    // then the object DOES point to a Player or derived class    }  


Good Luck.

This topic is closed to new replies.

Advertisement