Jump to content
  • Advertisement
Sign in to follow this  
kosmon_x

RTTI: Good or bad?

This topic is 4844 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey all, I've recently come across a need to use RTTI in my game. I'm concerned about potential performance hits for using it. Are my concerns valid or is the performance hit for a inheritance graph traversal (every object in every frame) negligable? For further info, here are some details on my situation: I have two abstract base classes that an object can compose itself from: IController and IState. IController has the virtual methods: Frame(), HandleInput(), and Render(). IState has the virtual method GetState(). All game object inherit from IController, but only some will inherit from IState (the ones that have a physical state in the world, ie a position, velocity, mass, etc). So, a sample object would be class Player : public IController, public IState {}; I'm keeping a master list of all objects in my game, a vector<IController*> m_Objects. Something like this: IController* c = new Player(). m_Objects.push_back(c); etc for every different kind of object I have. Every frame, I cycle through the list if IControllers and call their Frame(), HandleInput(), and Render() methods. It looks something like this:
void Frame()
{
  std::vector<IController*>::iterator It;
  for( It = m_Objects.begin(); It != m_Objects.end(); It++ )
  {
    (*It)->Frame();
    (*It)->HandleInput(); // etc
  }
}






However, if the object inheriting from IController* has state (also inherits from IState), I would like to call additional methods on it. So now, my Frame function would look like this:
void Frame()
{
  std::vector<IController*>::iterator It;
  for( It = m_Objects.begin(); It != m_Objects.end(); It++ )
  {
    (*It)->Frame();
    (*It)->HandleInput();

    // RTTI
    IState* state = dynamic_cast<IState*>(*It);
    if( state )
      state->GetState()->DoStuffWithState();
  }
}






So, is this bad or is it acceptable?

Share this post


Link to post
Share on other sites
Advertisement
That looks really nasty to me. Not from a performance standpoint, (though depending on how many objects you're talking about and how deep your inheritance trees are, it could be pretty bad there too) but from a good software engineering standpoint. Type inspections are rarely needed.

Why not just make an extra vector for your IState objects? Or just write an update function that will call Frame(), HandleInput(), and if the object happens to be an IState, calls the state functions inside the class itself?

Share this post


Link to post
Share on other sites
Thanks for the quick reply :)

I want to be able to access the state externally from the objects to do stuff like have the game force all objects with state to position XYZ, for instance. The Player class itself wouldn't be able to do this from within its own Frame() method. The easiest (and most logical) way is to just query the class for its state and do whatever special-case-circumstances I want with it. But, since not all IControllers have state and they are all lumped into one vector, I lose track of which ones derive from IState.

I was thinking about creating a new vector to hold pointers to all IState* objects, but I'm not convinced that it's the best solution...What if I want to add a Renderable abstract base class in the future, so now my player will be composed of IController, IState, and IRenderable. Now I'll have to have 3 vectors, each to store pointers to objects of its respective type. 2-3 vectors isn't bad, but when does this get out of hand and become unacceptable engineering-wise?

Is there an alternative solution that's better than all of the above? :)

Share this post


Link to post
Share on other sites
Use of RTTI usually (but not necessarily) indicates design problems. As SiCrane suggested, encapsulating the special case IState code inside the class is an easy and better alternative in this case. It might look like this:
    for( It = m_Objects.begin(); It != m_Objects.end(); It++ )
{
(*It)->Update();
}

virtual void IController::Update()
{
Frame();
HandleInput();
}

virtual void Player::Update()
{
IController::Update();
GetState()->DoStuffWithState();
}


Performance-wise, using RTTI the way you do can cause severe cache problems since each iteration might call a different function (blowing the instruction cache), and operate on data located in a different place (blowing the data cache). If you put objects of the same type together and operate on all of them together, then you would have much better cache performance. However, now we are talking about optimization and that should be left for last.

Share this post


Link to post
Share on other sites
Also, something I've done [and am curious as to others' opinions of] is always provide something like an virtual IController->GetState() which defaults to a blank, or otherwise unspecial state. Stateful objects can then provide their own GetState().

Share this post


Link to post
Share on other sites
I've used my own custom RTTI in my game engine and it just so happens it turned out to be indispensable when it came time to do serialization and scripting. I would have the class name, all the available functions and their return values and parameters and furthermore I would know how to load things directly off of disk just by using the RTTI ID that was generated for each class. This doesn't make sense when you are working in underlying systems such as rendering subsystem or the sound subsystem, but when you are dealing with objects that will need to be serialized or manipulated via script then RTTI could be used as a great tool.

Share this post


Link to post
Share on other sites
RTTI using the compiler is nasty. If you do it yourself it's actually not that bad and can be very helpful. However, the overhead and complexity of it sort of demands a certain project size and need in order to be worth the trouble.

The Doom3 engine uses this extensively, for example, as does the Tiki engine.

Doom3 defines a class called idTypeInfo. That class has a pointer to a function of type "void idClass::Spawn()". This enables you to say, in script or when loading map data, "spawn BadGuy" (to spawn a monster) or "give BigGun" (to give youself a big gun) or "removeclass idWeapon" (to remove all weapons from the game). It also makes it possible to iterate the base classes when doing load/save. And it comes in very hand when combined with a messaging system - you can declare a std::map in the typeinfo that says "when you recieve a message called this, here is the function you call for this type".

Share this post


Link to post
Share on other sites
Quote:
Original post by Telastyn
Also, something I've done [and am curious as to others' opinions of] is always provide something like an virtual IController->GetState() which defaults to a blank, or otherwise unspecial state. Stateful objects can then provide their own GetState().


I was considering doing this, however my IState returns a reference to the state.. ie:

class IState
{
ObjState& GetState() = 0;
};

It would be easy if it returned a pointer -- the default implementation could just return NULL...? Otherwise, it would have to return a static state which would be bad because I want to perform operations only on the objects that have a valid state, not the ones with a static placeholder state. I guess I could have an IsValid() member function on the state that would only be set by those with valid states, but that seems kind of sloppy/unnecessary...

And anyway, the whole point of breaking IController up into two classes was so that I could differentiate between those with and without state based on whether or not they inherit from IState.. but it seems like this isn't such a good solution...?

Share this post


Link to post
Share on other sites
Here's an interesting link from the Mozilla foundation on how to write portable C++ code, and the 4th thing on that list is don't use RTTI (followed by an explanation of why not to).

linkage

Share this post


Link to post
Share on other sites
You realize that portability guide was written in 1998, right? Compilers are much more well behaved now then they were back then.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!