Skip to main content
GameDev.net gamedev.net
🔒 Locked

[C++] Rtti. How do I find out type of a templated derived?

Started by bhavanvaishnav Dec 12, 2008 at 12:55 AM 13 replies 4.9k views
Original Post
bhavanvaishnav
bhavanvaishnav
Hi, I have been trying to create a messaging system. Here is the intro code: typedef std::type_info const *TypeIdPtr; class MsgContainer { public: template MsgContainer(msgDataT &msgData):mType(&typeid(msgDataT)){} TypeIdPtr mType; }; template class Msg: public MsgContainer { public: msgDataT mData; Msg() : MsgContainer(mData) {} }; So as you can see. My Msg can hold anything. For e.g: typedef struct MyMsg { int data1; int data2; } When I send the message, i pass a pointer to the Msg class that holds MyMsg. On the receiving end, I wish to typecast this pointer to the MsgContainer class, and identify the type of the contained Msg. I have just started reading about the std::type_info class but I do not think there is a way to actually get the type. i.e : Msg here. I want to ust the type_info to create an object of Msg, but obviously this cant be done. Is there a way around this. I have no way of knowing the actual type through std::type_info? Please let me know if I am not very clear. Also, I was wondering what are the issues with using RTTI in terms of performance? Thanks, Bhavan
Zipster
Zipster
The type_info can tell you what the type is, but only for the purpose of comparison to other type_info's or for display. You can't actually instantiate a type based on its type_info - there is no native support for reflection in C++.

I would go for a much simpler messaging system. Have a base class for message data like Msg, and then have each of your specific data classes derive from this class and give them a unique ID (from an enumeration for instance). All your message handling code should deal with Msg type until you reach the code that processes the messages. It can downcast from Msg to the specific message data type depending on the ID of the message received. This is essentially what you'd have to do with type_info, however instead of comparing against a type name you can switch on a user-defined integer, which is independent of how any particular compiler handles RTTI. Alternatively, you can give the Msg class a virtual Process function which is implemented in each data class, and call that from within the message processing code (also known as double dispatch). This has it's own advantages (no switch and downcast) and disadvantages (you're now in the data class context instead of the message handler context and potentially have much less information to work with when handling the message).

As for your second question, RTTI introduces a slight performance hit because you have to check to make sure two polymorphic types are compatible before you cast between them. Plus, each type with RTTI needs to store extra static information about its class hierarchy so that this check can be performed. Ideally you want to avoid situations where you absolutely need RTTI for your code to function. A good balance is to use RTTI in your Debug build so you can catch places where the wrong types somehow get through, but turn it off in Release builds once you've eliminated any such bugs.
bhavanvaishnav
bhavanvaishnav
Hi Zipster,

Yeah,

I considered that approach but that would mean I would have to have an enum consisting of all messages in my system (which is going to be a lot).

And also on the receiving end, I will have to have a huge switch (based on the enum) statement for all kinds of messages. Uh, not very friendly.

I found exactly what I was looking for here on gamedev. Seems to be exactly what I need.

Great article.
http://www.gamedev.net/reference/programming/features/effeventcpp/

Let me see if I can use this.

Thanks,
Bhavan
Zipster
Zipster
A registration-based system also works, it just turns the external switch into an internal look-up. But it also shifts the user's responsibility from that of a switch statement to that of registration functions, which still need to be called out one-to-one somewhere. Not that I'm arguing against it, just saying that there's a similar amount of maintenance to be done in both systems. Also note that the article only allows you to register one handler for each event type. You'd want to use a std::multimap/stdext::hash_multimap if you want to register more than one handler for each event type.
bhavanvaishnav
bhavanvaishnav
Hi,

Yeah I agree with the registration part.

But the advantage here would be abstracting everything through classes.

I declare n class with n receiver functions. And all I need to do is create a delegate with which holds the registered function. In the map, I would have it mapped to the delegate vector for that particular message. Hmmm. There would be enums again. I have not yet figured out completely how I am going to do this. But I just want to move away from a centralized switch statement. Have receiver functions defined anywhere in code. Have them registered without any centralized mechanism. This would help in reuse of the messaging system as well as everything is decentralized.

Uh, I dont think I can explain it better than that. But if you have any suggestions or would like to know in more detail what I want, I'd be glad to explain it in detail.

Thanks,
Bhavan
Zipster
Zipster
Ironically enough, I had to implement an event handling system today for the project I'm working on and I decided to go with a registration-based approach, similar to the one presented in the article. However like I mentioned before there were a number of practical problems the article didn't address that I ended up having to solve myself.

The first issue is that it depends on RTTI being enabled. This might not be a problem if you're already using RTTI for other things, but RTTI certainly isn't something you want to turn on for a single case like this. What I did instead was give each event type a unique enum value called TypeID and use it to replace the formal typeid. It's basically a "poor man's RTTI" that can be resolved statically. Given that I have a small, known, and fixed number of events I'm listening for, it worked out perfectly for me.

The second issue was registering multiple handlers for the same event type. Since the map is keyed by the type (be in type_info or integer ID), if you try to register a second handler for an event type already in the map, the first handler gets forgotten, and leaks memory no less. The simple solution is to replace the map with a multimap, however that has it's own problem.

What happens if I register the same instance and event type pair more than once? With a multimap it would just get added twice (since we create a new handler object each time), and you'd end up calling the same handling code multiple times whenever that particular event was raised. Regardless of whether or not the calling code is correct, this certainly isn't the desired behavior from an error-tolerance perspective. Plus it's a huge pain having to dig into the handler objects and check if a particular instance already exists. And finally...

How do you un-register handlers? If at any time an instance in your event handler gets deleted, or goes out of scope, or otherwise enters an invalid state, the event handler becomes a ticking time-bomb, ready to explode the next time that pointer is accessed. It's possible to ensure that some objects don't go out of scope before the event handler (perhaps major subsystems), but there are tons of objects that might want to listen for events that are constantly being created and destroyed. Plus, scope issues aside, you might just want to transfer event processing responsibilities from one instance of a class to another instance of another class. If you have a different class instance for every "state" of your game, then only one state should be listening for events and they need to be registering and un-registering their handlers as the game state changes. These are all things that need to be addressed, otherwise the event handler has very limited usage.

What I ended up going with was a two-layered map. The first layer maps event types to a second map. This second map maps pointer values to the function handlers. Not only does this allow me to register multiple handlers for the same event, but I can also make sure that if the same instance/event pair is added multiple times, it's only ever in the map once. Finally, since I'm storing pointer values, I can easily un-register instances by removing them from the second map, which is keyed by the particular even type I'm removing. Executing the handlers on an incoming event is also easy since they're already bucketed by event type. The only minor drawback is that a single instance can't register two functions that handle the same event type, but this usually never happens and it's easy enough to re-factor such a class so that it only has to register one function that can in turn call out to any number of other functions that it needs to.
CodeStorm
CodeStorm
There’s a pretty good chapter in Noel Llopis' book 'C++ for Game Programmers' on a custom made RTTI system, if that’s what you're looking for.

It's VERY simple, VERY streamlined and VERY efficient for determining base class types (basically a comparison of pointers is involved), but determining types for derived classes is done in O(N) (linear) time.

Anyway, I'm not currently on my home computer at the moment and so cannot submit the code I'm using, based on Noel's ideas.

But if you can get your hands on a copy of this book (or find a link that can download extracts) then give the RTTI chapter a read.
bhavanvaishnav
bhavanvaishnav
Hi,

@Zipster:
Thanks for the elaborated reply.

Hmmm, sounds good. But how are you registering functions? delegates? like the one in the article? The problem here is I will be registering the functions in code(Uh, runtime). Is there a way to register them while declaring. Uh, that is asking for a lot, I know, just a thought.

And about the issues:
I really wont have a situation where an instance that is listening to events is destroyed. I am looking at multiple threads that run through out as event listeners. And any class that I create in one threads context, listens to events that are meant for that particular thread. All these instances as well run through out (as they are used to switch game state etc).

About the RTTI enabling. I have major concerns over that as well. All my projects will have to turn on RTTI if they depend on the messaging system. Thats bad. But you said you created an RTTI system based on enums. Now there comes an issue that the enums are going to be centralized. Isn't it? Hmm. But as per the article. There is actually not much of an overhead using RTTI here, as there are no virtual functions, no dynamic casts. So do you think that is fine? I will be storing the typeids at registration, thats offline. So that should not be a major cause for concern.

@CodeStorm: Thanks for the reply. Will definitely have a look. Any example would be appreciated. Cheers.

CodeStorm
CodeStorm
No probs....

I knock off from work in a few hours. I'll submit my code then. It's not much... A small class in a header file, along with a macro to define 2 statics in an implementation file.
CodeStorm
CodeStorm
Quote:
Original post by CodeStorm
No probs....

I knock off from work in a few hours. I'll submit my code then. It's not much... A small class in a header file, along with a macro to define 2 statics in an implementation file.


LOL!

Seems I've bitten of more than I could chew here. I had to go back and hack out my RTTI code from my library, simplify it a little since my RTTI system also uses strings to help determine runtime types in a more readable form, but since strings are not efficient (at least for release builds), I've removed all string based code in my upcoming code samples.

And finally, I've littered the code samples with (I hope!) useful self-explanatory comments. But forgive me if they seem over the top... Comments are NOT my strong point since usually I'm the only one reading my own code. I've also included a little test program to demonstrate how my RTTI system work.

To begin with, my RTTI system defines 3 macros which simply helps in speeding up the process of integrating my RTTI system into other classes on the fly.

The first macro, defined in header file RTTI.h say, is as follows:

// RTTI.h//-----------------------------------------------------------------------------------------------------------------------------------// IMP_RTTI//-----------------------------------------------------------------------------------------------------------------------------------// Notes   : Macro which implements RTTI within ANY class definition, by declaring a private static member of type CRTTI//           and defining two public accessors; GetStaticRTTI and GetRTTI, which returns a constant reference to this member.//// Remarks : Once this macro concludes, memeber access within the class is set to public.//-----------------------------------------------------------------------------------------------------------------------------------#define IMP_RTTIprivate: static const CRTTI RTTI;               /* Private declaration of the static constant member RTTI,   */                                        /* which is of type CRTTI.                                   */public: static const CRTTI & GetStaticRTTI ()  /* Public definition of the static method GetStaticRTTI.     */ {  return RTTI;                          /* Returns a constant reference to the member RTTI.          */ } virtual const CRTTI & GetRTTI () const /* Public definition of the virtual constant method GetRTTI. */ {  return RTTI;                          /* Returns a constant reference to the member RTTI.          */ }


I tend to define my macros and constants in caps and the weird /**/ comments are needed since multi-line macro definitions are composed using the '\' character at end of line. (NOTE: You have to include the '\' yourself. Seems to expand it all on one line when I link the code in this post with '\' chars)

An example of how you integrate this macro into your classes is provided later on.

BTW: You DON'T have to use ANY of these macros since you can merely type in the code by hand... But I just find macros useful in speeding up development anytime bog standard code is needed. But sure... They look horrible (especially multi-line ones!), they can be an arse to work with and debugging them is HELL.

The next two macros define bog standard code for defining static RTTI class members in implementation files. (But again...these macros are NOT vital!)

//-----------------------------------------------------------------------------------------------------------------------------------// BASE_RTTI_STATIC (BaseClass)//-----------------------------------------------------------------------------------------------------------------------------------// Notes : Macro which defines the RTTI member of the class BaseClass in an implementation file.//         This member is constructed with a null pointer to a constant CRTTI type.//// Remarks : BaseClass must be previously defined using the IMP_RTTI macro.//-----------------------------------------------------------------------------------------------------------------------------------#define BASE_RTTI_STATIC(BaseClass) const CRTTI BaseClass::RTTI (NULL);


And last macro definition:

//-----------------------------------------------------------------------------------------------------------------------------------// DERIVED_RTTI_STATIC (DerivedClass, BaseClass)//-----------------------------------------------------------------------------------------------------------------------------------// Notes : Macro which defines the RTTI member of the class DerivedClass in an implementation file.//         This member is constructed with a pointer to a constant CRTTI reference,//         provided by BaseClass's GetStaticRTTI method.//// Remarks : Both DerivedClass and BaseClass must have been previously defined using the IMP_RTTI macro and BaseClass's static//           member must also have been previously defined using either the BASE_RTTI_STATIC or DERIVED_RTTI_STATIC macros.//-----------------------------------------------------------------------------------------------------------------------------------#define DERIVED_RTTI_STATIC(DerivedClass, BaseClass) const CRTTI DerivedClass::RTTI (&BaseClass::GetStaticRTTI ());


Since macros can be pretty unsafe, I've made sure to put in comments about the order in which they must be used. BASE_RTTI_STATIC and DERIVED_RTTI_STATIC can only be used with classes that use the IMP_RTTI somewhere within their definitions. And DERIVED_RTTI_STATIC can only be used with classes who have had their statics initialized with either BASE_RTTI_STATIC or DERIVED_RTTI_STATIC in an implementation file.

But as I said you can come up with your own strategy in implementation.

KK the final and most important piece of the puzzle, is the RTTI class itself defined:

//-----------------------------------------------------------------------------------------------------------------------------------// CRTTI class definition : //-----------------------------------------------------------------------------------------------------------------------------------class CRTTI{//-----------------------------------------------------------------------------------------------------------------------------------// Private members and methods : //----------------------------------------------------------------------------------------------------------------------------------- const CRTTI * ITS_BASE_RTTI;           // Private definition of the member ITS_BASE_RTTI, which is a pointer to type const CRTTI. CRTTI           (const CRTTI &);       // Private copy constructor. void operator = (const CRTTI &) const; // Private assignment operator.public://-----------------------------------------------------------------------------------------------------------------------------------// Constructor//-----------------------------------------------------------------------------------------------------------------------------------// Notes : Constructs an instance of type CRTTI, using a pointer to type const CRTTI.//// Remarks : The pointer argument is set to null if the default constructor is invoked.//----------------------------------------------------------------------------------------------------------------------------------- CRTTI         (const CRTTI * BASE_RTTI = 0): ITS_BASE_RTTI (BASE_RTTI) { }//-----------------------------------------------------------------------------------------------------------------------------------// operator ==//-----------------------------------------------------------------------------------------------------------------------------------// Notes : Tests if two CRTTI instances are equivalent.//// Returns a bool values of true if they are. False otherwise.//// Remarks : Performs in O(1) constant time.//----------------------------------------------------------------------------------------------------------------------------------- bool operator == (const CRTTI & RTTI) const {  return this == &RTTI // Equivalence test based on the this pointer. }//-----------------------------------------------------------------------------------------------------------------------------------// operator >=//-----------------------------------------------------------------------------------------------------------------------------------// Notes : Tests if two CRTTI instances are equivalent, even if the rhs (right hand side) expression is a base CRTTI.//// Returns a bool values of true if they are. False otherwise.//// Remarks: Performs in O(N) linear time.//----------------------------------------------------------------------------------------------------------------------------------- bool operator >= (const CRTTI & RTTI) const {  const CRTTI * BASE_RTTI (this); // Initializes BASE_RTTI pointer to this.  do                              // Begins the loop.  {   if (BASE_RTTI == &RTTI) return true; // Equivalence test returns true if expression BASE_RTTI == &RTTI is true.  }  while (BASE_RTTI = BASE_RTTI->ITS_BASE_RTTI); // Assigns BASE_RTTI to point to the next base CRTTI.  return false;                                 // Returns false if BASE_RTTI points to NULL. }};


I've implemented this class so that copying is strictly prohibited and I've overloaded the >= operator to test RTTI equivalence in class hierarchies, but again, this is just an implementation detail.

To test the above code and demonstrate how it all works, the following header defines two classes; a base class and a derived one, which implements RTTI:

// TestRTTI.h#include "RTTI.h" // Includes RTTI macros and the CRTTI class.class CBase{ IMP_RTTI // Macro which implements RTTI in this class.          // Other CBase specific members and methods go here.          // Remember the macro IMP_RTTI has set member access within this class to public!};class CDerived : public CBase // CDerived class derives from CBase publicly.{ IMP_RTTI // Macro which implements RTTI in this class.          // Other CDerived specific members and methods go here.          // Remember the macro IMP_RTTI has set member access within this class to public!};


As you can see using macros just makes including bog standard RTTI implementation code into any class that needs it a little simpler.

Finally the following implementation code for a console application project, displays basic test results on screen:

// TestRTTI.cpp#include "TestRTTI.h" // Includes definitions of the CBase and CDerived classes, RTTI macros and the CRTTI class.#include <iostream>   // Includes standard input / output routines for the console.BASE_RTTI_STATIC (CBase)              // Macro which defines CBase's static CRTTI member.DERIVED_RTTI_STATIC (CDerived, CBase) // Macro which defines CDerived's static CRTTI member, whose CRTTI pointer points to                                      // CBase's previously defined static CRTTI member.int main (){ // Initialize some pointers to point to CBase and CDerived instance on the heap: CBase * BaseA  (new CBase); CBase * BaseB  (new CBase); CBase * DerivedA (new CDerived); CBase * DerivedB (new CDerived); // Cout out some basic RTTI test results: std::cout << "BaseA == BaseA    : " << ((BaseA->GetRTTI ()  == BaseA->GetRTTI ()) ? "true\n" : "false\n"); std::cout << "BaseA == BaseB    : " << ((BaseA->GetRTTI ()  == BaseB->GetRTTI ()) ? "true\n" : "false\n"); std::cout << "DerivedA == BaseA : " << ((DerivedA->GetRTTI () == BaseA->GetRTTI ()) ? "true\n" : "false\n"); std::cout << "DerivedA >= BaseA : " << ((DerivedA->GetRTTI () >= BaseA->GetRTTI ()) ? "true\n" : "false\n"); // Cleanup: delete BaseA; delete BaseB; delete DerivedA; delete DerivedB; return 0;}


Again macros just makes it a little easier to define class statics on the fly and if you run the project you'll find that the above tests all display true, except test;

DerivedA == BaseA

which displays false: DerivedA points to a heap instance that derives from the heap instance BaseA points to, but that instance is NOT of type CDerived . The >= operator must be deployed to test for type equality within class hierarchies.

One final point is that this RTTI system is fairly cheap in terms of memory overhead. Each class simply has a static RTTI object whose size is only 4 bytes (on my system). However, using the >= operator on deeply nested class hierarchies can result in some overhead if such tests are performed in critical loops.

But at least you don't get the overhead when the compilers RTTI system is used, in which ALL classes, whether they need RTTI or not, contains such info.

Hope this helps a little!
bhavanvaishnav
bhavanvaishnav
Hi,

Hmm, Very interesting, and quite simple.

Thats a great solution, and the comments were great.

Just a few things that went over the top :)

1>
#define DERIVED_RTTI_STATIC(DerivedClass, BaseClass) const CRTTI DerivedClass::RTTI (&BaseClass::GetStaticRTTI ());

Why is the derived RTTI set to the base class RTTI? would that not be Null? according to this macro:

#define BASE_RTTI_STATIC(BaseClass) const CRTTI BaseClass::RTTI (NULL);

2>
Just wondering, thats going to work with a templated class as well?

i.e:
this== &RTTI
myTemplatedClass == myTemplatedClass is going to return false.

Uh I haven't yet checked out the implementation yet so probably my understanding is not a 100%, hence the trivial questions :).
Just stuck up with something else. Will try it out as soon as possible.

3>
If I am using this system, How am I going to map it to the functions I need to call when I get an event of some type. I guess that would be a map consisting of the CRTTI class to a vector of delegates?

As you can see I need some clarity and I will get that as soon as I start looking deeper into the implementation of the whole system as such. Will get back to you as soon as I have some time off.

Thanks for the post again.

Cheers,
Bhavan











CodeStorm
CodeStorm
Hi again.

No probs... I'll try and look into how I would implement my RTTI system into your specific requirements. I.e. templates and linking specific types to functions.

It should be easy enough to sort out, but I'm at work at the moment and would rather play about with test code on my home computer.

With regards to the macros defining RTTI class statics, the macro:

#define BASE_RTTI_STATIC(BaseClass) const CRTTI BaseClass::RTTI (NULL);

does define a static instance of a CRTTI class, but this instances' internal base CRTTI pointer is what is set to NULL.

Because BaseClass is, well... A base, its static CRTTI instance need not point to any other CRTTI instance.

The line;

#define DERIVED_RTTI_STATIC(DerivedClass, BaseClass) const CRTTI DerivedClass::RTTI (&BaseClass::GetStaticRTTI ());

now sets the static CRTTI instance in DerivedClass, to point to the static CRTTI instance in BaseClass.

This is how the RTTI system works in class hierarchies. Each class, including those in a hierarchical tree, have their own CRTTI static object. If one class derives from another, then in order to test if one type is equivalent to another in terms of being in the same hierarchy, we simply set the base CRTTI pointers in each static instance of CRTTI in a derived class, to a static instance of CRTTI in a base class.

A diagram will serve to illustrate this more clearly and I'll submit one, along with a possible strategy to implement my RTTI system to your needs, in my next post.

By the way, in terms of equivalence of types due to hierarchies, the following results;

Derived >= Base : true
Base >= Derived : false

stand. Remember, although Base and Derived live in the same tree, Base does NOT derive from Derived. If you wish to test if various run time types live in the same tree, then always test against the BASE type of that tree and not the other way around.
bhavanvaishnav
bhavanvaishnav
Hi,

Thanks for the post.

Yeah, I got the part where you set the derived type to base.

Now this is good if you want to find out if one class(derived) belongs to a particular hierarchy.

But in my situation I just have one base.

Ok, I will try to explain the scenario in short.

class Base: // This acts like a message container.
{
TYPE mType;

};

And a templated derived.

template
class Derived:Base // This is my actual message
{
Derived(){t = ;} // Want to store the derived type to mType
anyStructure mData;
};

Now as you can see, when I create the message after my messaging system is in place, it would follow this pattern:

typedef struct someMsg
{
int data1;
float data2;
};

Now when I send this struct as the message I would have something like:

template
void sendMsg(someMsgType msg)
{
Derived msg; // Here automatically the type is stored in mType.
send(msg);
}

Now where I receive, due to my messaging system architecture I receive a unsigned int as the data, which I will typecast to the Base *.

From here I can take the Base->mType to determine the type of the message and then get from my map of (This would be similar to the map in the article I pointed to earlier in this post), the functions to execute.

So in short my situation would be:

DerivedA == DerivedB should say false.

I think by changing the derived macro to store the type of the derived would do.

Hmmm. I still would have the problem of registering functions.
One more concern is since just a Base * pointer, Will the function call:

ReceiverFunction(Derived *) automatically typecast the Base * pointer? I haven't tried this out so uh, again sorry for the trivial questions. If this happens, I dont think there will be any more issues with the system except for the fact of course, I cant unregister items, duplicates etc as pointed out by Zipster. But my architecture does not come across such situations (as of now :), and relying on intuition).

Thanks,
Bhavan




bhavanvaishnav
bhavanvaishnav
Hello people. Merry Christmas.

I finally got some time to get back to the messaging system I had started off with.

Here is where I am:

I am good with the registering of overloaded functions as event listeners as the HandlerFunctionBase and MemberFunctionHandler here:

http://www.gamedev.net/reference/programming/features/effeventcpp/

Work with templates as events.(As in my situation explained above)

I am also good with the converting of the base MsgContainer to the specific Msg type from just its pointer. (As the MemberFunctionHandler's call method does that.

Now about the rtti:

I thought of using a void * in my MsgContainer class which is filled by the Msg class to static 'this' every time a new msg is created. I thought this would do as the address for each different type would be different.

But then you cant use that in a map.

Thats the last part I need to sort out.

I could use Enums in a centralized file for each type of message I create. But then I will have to update that centralized file every time a new message comes in. Also, I would need message registration. (To actually assign a message to a type)

So here is what I need:
Can I have a macro, that updates an enum in another file?.. :)

Or else:
Can you suggest another way to have the rtti happen for me (which I could use in my map of (rtti, HandlerFunctionBase *)? This is the final piece of the puzzle.

Thanks for all the support.

Cheers.
Bhavan.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.