31

I am familiar with C++ RTTI, and find the concept interesting.

Still there exist a lot of more ways to abuse it than to use it correctly (the RTTI-switch dread comes to mind). As a developer, I found (and used) only two viable uses for it (more exactly, one and a half).

Could you share some of the ways RTTI is a viable solution to a problem, with example code/pseudo-code included?

Note: The aim is to have a repository of viable examples a junior developer can consult, criticize and learn from.

Edit: You'll find below code using C++ RTTI

// A has a virtual destructor (i.e. is polymorphic)
// B has a virtual destructor (i.e. is polymorphic)
// B does (or does not ... pick your poison) inherits from A

void doSomething(A * a)
{
   // typeid()::name() returns the "name" of the object (not portable)
   std::cout << "a is [" << typeid(*a).name() << "]"<< std::endl ;

   // the dynamic_cast of a pointer to another will return NULL is
   // the conversion is not possible
   if(B * b = dynamic_cast<B *>(a))
   {
      std::cout << "a is b" << std::endl ;
   }
   else
   {
      std::cout << "a is NOT b" << std::endl ;
   }
}
Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
paercebal
  • 81,378
  • 38
  • 130
  • 159
  • 1
    You could start by posting your 1.5 examples. – Ben Collins Oct 26 '08 at 19:00
  • The problem is that if I post them, and they are the easiest examples (as I guess they are), then most people won't even try sharing their own. Of course, I'll post them if no one thought about them. – paercebal Oct 26 '08 at 19:08
  • :-D ... No, I'm not. Ok, the first is contract programming, where you ask an object if he implements some interface. If yes, you then use the inferface. The second is used when you have complex class hierarchy, and don't want your base Object class to implement the method draw from Shape... – paercebal Oct 26 '08 at 19:41
  • 1 I can agree with. 2 sounds like a broken hierarchy. – fizzer Oct 26 '08 at 19:47
  • The "2" is the .5 part... :-D ... It's not as much broken as it is extended. If you have a large framework of very different objects, the last thing you want is to have all objects have the same 3000 methods. So you set "divergence points". Object could derive into Container and Shape, and [...] – paercebal Oct 26 '08 at 19:50
  • [...] So, Object::toString exists, Container::getSize() exist, too, as well as Shape::draw(). But not Object::draw(), nor Object::getSize(). But every time deriving from Shape would have draw, and every type deriving from Container would have getSize. In C++, because of templates [...] – paercebal Oct 26 '08 at 19:53
  • [...] This case won't appear often, but it could... This example, while similar to Java, I read first in B. Stroustrup's C++ Programming Language. I implemented it on a primitive XML GUI code that was never finished (but was quite cool anyway... :-p ...) – paercebal Oct 26 '08 at 19:55
  • Hey, the "contract" part appeared in the http://stackoverflow.com/questions/238452/c-rtti-viable-examples#238515 from keysersoze! I was right... :-p – paercebal Oct 26 '08 at 20:07

10 Answers10

8

Acyclic Visitor (pdf) is a great use of it.

fizzer
  • 13,551
  • 9
  • 39
  • 61
  • I agree. This again what I called above the "contract" programming. +1 – paercebal Oct 26 '08 at 20:17
  • Link is not longer valid. Wayback Machine to the rescue: https://web.archive.org/web/20060313131550/http://www.objectmentor.com/resources/articles/acv.pdf – Kunal Tyagi Feb 18 '17 at 09:03
8

How about the boost::any object!

This basically uses the RTTI info to store any object and the retrieve that object use boost::any_cast<>.

Martin York
  • 257,169
  • 86
  • 333
  • 562
  • 2
    While boost:any does not use dynamic cast, it uses the typeid operator which works even for non-polymorphic types, to be sure the cast is correct. +1 – paercebal Oct 27 '08 at 10:56
6

You can use RTTI with dynamic_cast to get a pointer to a derived class in order to use it to call a fast, type specialized algorithm. And instead of using the virtual methods through the base class, it will make direct and inlined calls.

This sped things up for me a lot using GCC. Visual Studio didn't seem to do as well, it may have a slower dynamic_cast lookup.

Example:

D* obj = dynamic_cast<D*>(base);
if (obj) {
    for(unsigned i=0; i<1000; ++i)
        f(obj->D::key(i));
    }
} else {
    for(unsigned i=0; i<1000; ++i)
        f(base->key(i));
    }
}
Zan Lynx
  • 53,022
  • 10
  • 79
  • 131
  • Probably D* d = dynamic_cast(base); for (i=0;i!=1000;++i) { d->Foo(); }. Wrong idea, since this doesn't call MoreDerived::Foo – MSalters Oct 27 '08 at 15:07
  • Use the d->D::Foo() syntax. I put it in the example I added. – Zan Lynx Oct 27 '08 at 16:16
  • Interesting micro optimization, I'll have to remember this. – Mark Ransom Oct 27 '08 at 16:28
  • 1
    I would be interested by "why this goes faster on gcc". The work avoided in `obj->D::key` is being done by the dynamic_cast. And will this scale well for, say, multiple types D, E, F, etc., meaning multiple if(D)/else if(E)/else if(F)/else ? – paercebal Jul 11 '10 at 11:03
  • 1
    @paercebal: It worked because the compiler was able to inline the direct call to D::key(i) and over 1000 iterations the savings of not doing indirect pointer calls adds up. – Zan Lynx Jul 11 '10 at 20:09
  • @Zan Lynx: Oops... I was so puzzled by the thing I didn't see the loop. Of course, you're right! – paercebal Jul 12 '10 at 08:25
  • 1
    @Zan Lynx: Now, as MSalters did put it, what if the class is DD, which is derived from D, and overrides `key`? This means that this piece of code needs to know the exact class hierarchy to work. Still, it is an interesting piece of code. +1. – paercebal Jul 12 '10 at 08:29
5

I cant say I've ever found a use for in in real life but RTTI is mentioned in Effective C++ as a possible solution to multi-methods in C++. This is because method dispatch is done on the dynamic type of the this parameter but the static type of the arguments.

class base
{
  void foo(base *b) = 0; // dynamic on the parameter type as well
};

class B : public base {...}
class B1 : public B {...}
class B2 : public B {...}

class A : public base
{
  void foo(base *b)
  {
    if (B1 *b1=dynamic_cast<B1*>(b))
      doFoo(b1);
    else if (B2 *b2=dynamic_cast<B2*>(b))
      doFoo(b2);
  }
};
1800 INFORMATION
  • 131,367
  • 29
  • 160
  • 239
  • As a side-note, there's also a mention of multimethod impl in Alexandrescu's "Modern C++ Design" – GregC Mar 24 '10 at 17:06
3

I worked on an aircraft simulation once, that had what they (somewhat confusingly) referred to as a "Simulation Database". You could register variables like floats or ints or strings in it, and people could search for them by name, and pull out a reference to them. You could also register a model (an object of a class descended from "SimModel"). The way I used RTTI, was to make it so you could search for models that implement a given interface:

SimModel* SimDatabase::FindModel<type*>(char* name="")
{
   foreach(SimModel* mo in ModelList)
   if(name == "" || mo->name eq name)
   {
       if(dynamic_cast<type*>mo != NULL)
       {
           return dynamic_cast<type*>mo;
       }
   }
   return NULL;
}

The SimModel base class:

class public SimModel
{
    public:
        void RunModel()=0;
};

An example interface might be "EngineModel":

class EngineModelInterface : public SimModel
{
    public:
        float RPM()=0;
        float FuelFlow()=0;
        void SetThrottle(float setting)=0; 
};

Now, to make a Lycoming and Continental engine:

class LycomingIO540 : public EngineModelInterface 
{
    public:
        float RPM()
        {
            return rpm;
        }
        float FuelFlow()
        {
            return throttleSetting * 10.0;
        }
        void SetThrottle(float setting) 
        {
            throttleSetting = setting
        }
        void RunModel() // from SimModel base class
        {
            if(throttleSetting > 0.5)
                rpm += 1;
            else
                rpm -= 1;
        }
    private:
        float rpm, throttleSetting;
};
class Continental350: public EngineModelInterface 
{
    public:
        float RPM()
        {
            return rand();
        }
        float FuelFlow()
        {
            return rand;
        }
        void SetThrottle(float setting) 
        {
        }
        void RunModel() // from SimModel base class
        {
        }
};

Now, here's some code where somebody wants an engine:

.
.
EngineModelInterface * eng = simDB.FindModel<EngineModelInterface *>();
.
.
fuel = fuel - deltaTime * eng->FuelFlow();    
.
.
.

Code is pretty pseudo, but I hope it gets the idea across. One developer can write code that depends on having an Engine, but as long as it has something that implements the engine interface, it doesn't care what it is. So the code that updates the amount of fuel in the tanks is completely decoupled from everything except the FindModel<>() function, and the pure virtual EngineModel interface that he's interested in using. Somebody a year later can make a new engine model, register it with the SimulationDatabase, and the guy above who updates fuel will start using it automatically. I actually made it so you could load new models as plugins (DLLs) at runtime, and once they are registered in the SimulationDatabase, they could be found with FindModel<>(), even though the code that was looking for them was compiled and built into a DLL months before the new DLL existed. You could also add new Interfaces that derive from SimModel, with something that implements them in one DLL, something that searches for them in another DLL, and once you load both DLLs, one can do a FindModel<>() to get the model in the other. Even though the Interface itself didn't even exist when the main app was built.

Parenthetically, RTTI doesn't always work across DLL boundaries. Since I was using Qt anyway, I used qobject_cast instead of dynamic_cast. Every class had to inherit from QObject (and get moc'd), but the qobject meta-data was always available. If you don't care about DLLs, or you are using a toolchain where RTTI does work across DLL boundaries (type comparisons based on string comparisons instead of hashes or whatever), then all of the above with dynamic_cast will work just fine.

KeyserSoze
  • 2,501
  • 1
  • 16
  • 17
  • Effectively, I tried, too, the decoupling of interfaces and implementations, and code using defined interfaces worked quite well with implementation types coded time after, without needing recompilation... +1 – paercebal Oct 26 '08 at 20:05
  • After reading the first lines of Your post, I wondered why I could not remember posting some details about my aircraft simulation. :-D This should proove, that there are some examples of useful RTTI. – Black Oct 26 '08 at 20:30
3

I use it in a class tree which serializes to a XML file. On the de-serialization, the parser class returns a pointer to the base class which has a enumeration for the type of the subclass (because you don't know which type it is until you parse it). If the code using the object needs to reference subclass specific elements, it switches on the enum value and dynamic_cast's to the subclass (which was created by the parser). This way the code can check to ensure that the parser didn't have an error and a mismatch between the enum value and the class instance type returned. Virtual functions are also not sufficient because you might have subclass specific data you need to get to.

This is just one example of where RTTI could be useful; it's perhaps not the most elegant way to solve the problem, but using RTTI makes the application more robust when using this pattern.

Nick
  • 6,808
  • 1
  • 22
  • 34
  • You're right: This is an awful switch/RTTI combination, but then, you are using XML, and this is a *good* solution to strong-type again what was a string XML into a full fledged object. +1. – paercebal Oct 27 '08 at 10:32
  • +1 because this is the exact thing I'm looking to do, this was the best idea I could come up with too, and I appreciate the knowledge that I wasn't missing something. – T.E.D. May 29 '14 at 19:43
2

Sometimes static_cast and C-style casts just aren't enough and you need dynamic_cast, an example of this is when you have the dreaded diamond shaped hierarchy (image from Wikipedia).

diamond inheritance

struct top {
};

struct left : top { 
    int i;
    left() : i(42) {}
};

struct right : top {
    std::string name;
    right() : name("plonk") { }
};

struct bottom : left, right {
};

bottom b;
left* p = &b;

//right* r = static_cast<right*>(p); // Compilation error!
//right* r = (right*)p;              // Gives bad pointer silently 
right* r = dynamic_cast<right*>(p);  // OK
Community
  • 1
  • 1
Motti
  • 110,860
  • 49
  • 189
  • 262
1

Use cases I have in my projects (if you know any better solution for specific cases, please comment):

  1. The same thing as 1800 INFORMATION has already mentioned:

    You'll need a dynamic_cast for the operator== or operator< implementation for derived classes. Or at least I don't know any other way.

  2. If you want to implement something like boost::any or some other variant container.

  3. In one game in a Client class which had a std::set<Player*> (possible instances are NetPlayer and LocalPlayer) (which could have at most one LocalPlayer), I needed a function LocalPlayer* Client::localPlayer(). This function is very rarely used so I wanted to avoid to clutter Client with an additional local member variable and all the additional code to handle this.

  4. I have some Variable abstract class with several implementations. All registered Variables are in some std::set<Variable*> vars. And there are several builtin vars of the type BuiltinVar which are saved in a std::vector<BuiltinVar> builtins. In some cases, I have a Variable* and need to check if it is a BuiltinVar* and inside builtins. I could either do this via some memory-range check or via dynamic_cast (I can be sure in any case that all instances of BuiltinVar are in this vector).

  5. I have a gridded collection of GameObjects and I need to check if there is a Player object (a specialized GameObject) inside one grid. I could have a function bool GameObject::isPlayer() which always returns false except for Player or I could use RTTI. There are many more examples like this where people often are implementing functions like Object::isOfTypeXY() and the base class gets very cluttered because of this.

    This is also sometimes the case for other very special functions like Object::checkScore_doThisActionOnlyIfIAmAPlayer(). There is some common sense needed to decide when it actually make sense to have such a function in the base class and when not.

  6. Sometimes I use it for assertions or runtime security checks.

  7. Sometimes I need to store a pointer of some data in some data field of some C library (for example SDL or what not) and I get it somewhere else later on or as a callback. I do a dynamic_cast here just to be sure I get what I expect.

  8. I have some TaskManager class which executes some queue of Tasks. For some Tasks, when I am adding them to the list, I want to delete other Tasks of the same type from the queue.

Community
  • 1
  • 1
Albert
  • 65,406
  • 61
  • 242
  • 386
0

I used RTTI when doing some canvas-based work with Qt several years ago. It was darn convenient when doing hit-tests on objects to employ RTTI to determine what I was going to do with the shape I'd 'hit'. But I haven't used it otherwise in production code.

itsmatt
  • 31,265
  • 10
  • 100
  • 164
0

I'm using it with Dynamic Double Dispatch and Templates. Basically, it gives the ability to observe/listen to only the interesting parts of an object.

Johann Gerell
  • 24,991
  • 10
  • 72
  • 122