This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

 

  Using Static Members For RTTI Without RTTI
  Submitted by



I recently stumbled upon an interesting dilemma: I am working on some code that can benefit from RTTI, but the code base does not use RTTI, to enforce type safety at runtime. Because I had been recently playing around how to fake template member explicit specializations in Visual C++ 6.0 utilizing a static member pointer to the class type, somehow it ocurred to me the idea that I could use the address of said pointer, as an implicit type id for the class.

In other words, because all static members have to go somewhere in memory, it ocurred to me that static members of different classes have to have different addresses. We can take advantage of that information to "automatically generate" an implicit class type id.

So here is the code. Please refer to the comments in the code to continue the "tip of the day" text and afterwards...

// rhmRtti.cpp : Defines the entry point for the console application.
//
#include <stdafx.h
#include <iostream
#include <string

#define NULL 0

////////////////////////////////////////// // BASE CLASS ////////////////////////////////////////// class I_TypeInfoClass { public: // public stuff here // This template accepts a reference, of a const pointer of type const T // and the template itself is a const function // Note: Visual C++ 6.0 requires the template member definition inside // the class declaration. I.e. no separate definition outside of the // class { }; declaration is allowed. Visual C++ 7.0 (.NET) seems to // handle it fine. template <typename T bool IsOfExactType(const T * const & refClassType) const { return GetClassTypeAddress() == reinterpret_cast<unsigned long(&refClassType); }

// This template may be unnecessary under another implementation // but I decided to use it because it elimates code in the subclasses // It's all a matter of taste though, imho. protected: // enforce that the subclasses have to implement this method virtual unsigned long GetClassTypeAddress() const = 0; };

///////////////////////////////////////// // SUB CLASS A ///////////////////////////////////////// class SubClassA: public I_TypeInfoClass { public: // We make this guy a const * that points to a const SubClass A // because we do not want the user to change it and call on // potentially anything. Since it points to NULL it would crash though static const SubClassA * const mClassType; protected:

// The keyword virtual in this statement is optional, // because once a base class defines a virtual function // the derived classes that override it will always be virtual // also, but I want to be descriptive :-) virtual unsigned long GetClassTypeAddress () const { return reinterpret_cast<unsigned long(&mClassType); }

// private code & stuff here };

// It really does not matter, to what this pointer points to // but let's make it point to NULL since it is true we point // to no valid object :-) const SubClassA * const SubClassA::mClassType = NULL;

/////////////////////////////////////////// // SUB-SUB CLASS C /////////////////////////////////////////// class SubSubClassC: public SubClassA { public:

// We make this guy a const * that points to a const SubClass A // because we do not want the user to change it and call on // potentially anything. Since it points to NULL it would crash though static const SubSubClassC * const mClassType;

protected:

virtual unsigned long GetClassTypeAddress () const { return reinterpret_cast<unsigned long(&mClassType); }

// private code & stuff here };

const SubSubClassC * const SubSubClassC::mClassType = NULL;

/////////////////////////////////////////////////////// // Simple utility function to print status type checks for this // example. /////////////////////////////////////////////////////// void PrintTypeCheck(I_TypeInfoClass *pI_TypeInfoClass, std::string &refMessageString) { std::cout << "************" << refMessageString << std::endl << std::endl;

// Notice how we check for the exact type - we pass as a parameter // the static member that is a pointer of the class that we check against std::cout << "pI_TypeInfoClass..." << std::endl;

if (pI_TypeInfoClass-IsOfExactType(SubClassA::mClassType) ) std::cout << "... points to an exact type SubClassA" << std::endl; else std::cout << "... doesn't point an exact type SubClassA" << std::endl;

if ( pI_TypeInfoClass-IsOfExactType(SubSubClassC::mClassType) ) std::cout << "... points to an exact type SubSubClassC" << std::endl; else std::cout << "... doesn't point an exact type SubSubClassC" << std::endl;

std::cout << "***********************************" << std::endl << std::endl; }

/////////////////////////////////////////// // MAIN - duh! :-) /////////////////////////////////////////// int main(int argc, char* argv[]) { // First some code setup for the example SubClassA anA; SubSubClassC aC; SubClassA *pA = &anA; I_TypeInfoClass *pB = &anA; SubSubClassC *pC = &aC;

// check when pA points to SubClassA PrintTypeCheck(pA, std::string("When pA points to SubClassA..."));

pA = &aC;

// check when pA points to SubSubClassC PrintTypeCheck(pA, std::string("When pA points to SubSubClassC..."));

// check when pB points to SubClassA PrintTypeCheck(pB, std::string("When pB points to SubClassA..."));

pB = &aC;

// check when pB points to SubSubClassC PrintTypeCheck(pB, std::string("When pB points to SubSubClassC..."));

// check pC just for kicks PrintTypeCheck(pC, std::string("When pC points to SubSubClassC...")); return 0; }

/////////////////////////////////////////////////////////////////// // SAMPLE OUTPUT: /////////////////////////////////////////////////////////////////// /*

************When pA points to SubClassA...

pI_TypeInfoClass... ... points to an exact type SubClassA ... doesn't point an exact type SubSubClassC

************When pA points to SubSubClassC...

pI_TypeInfoClass... ... doesn't point an exact type SubClassA ... points to an exact type SubSubClassC

************When pB points to SubClassA...

pI_TypeInfoClass... ... points to an exact type SubClassA ... doesn't point an exact type SubSubClassC

************When pB points to SubSubClassC...

pI_TypeInfoClass... ... doesn't point an exact type SubClassA ... points to an exact type SubSubClassC

************When pC points to SubSubClassC...

pI_TypeInfoClass... ... doesn't point an exact type SubClassA ... points to an exact type SubSubClassC

Press any key to continue

*/

Couple of comments & improvements:

1. You can define declaration and implementation macros, to avoid more typing

2. This scheme only supports IsOfExactType, not IsOfType. The first will return true if and only if, the class type that is checked against is exactly the same type. The later takes into account class hierarchy - so for example, SubSubClassC would return true on a call to IsOfType(SubClassA::mClassType) but false on a call to IsOfExactType(SubClassA::mClassType).

You can implement IsOfType by making each class start "asking up the hierarchy" if the static member adresses match, if the original comparison with the current class fails. This requires the subclasses to implement more code and it is highly recommended you do some declaration and implementation macros.

As a quick snippet of code, here's how the I_TypeInfoClass would have to implement and what the other classes would have to implement, something like IsTypeOf:

I_TypeInfoClass modifications/additions:

    public: 
	static const I_TypeInfoClass * const mClassType; 

template <typename T bool IsTypeOf(const T * const & refClassType) const { // Let's use a variable for clarity unsigned long refClassTypeAddress = reinterpret_cast<unsigned long(&refClassType);

// Query up the hierarchy if necessary return GetClassTypeAddress() == refClassTypeAddress ? true : IsOfBaseType(refClassTypeAddress); }

protected: virtual bool IsOfBaseType(unsigned long pasUpAddress) const { return reinterpret_cast<unsigned long(&mClassType) == pasUpAddress; }

virtual unsigned long GetClassTypeAddress () const { return reinterpret_cast<unsigned long(&mClassType); }

SubClassA modifications/additions:

public: static SubClassA * const mClassType; protected: virtual bool IsOfBaseType(unsigned long pasUpAddress) const { // Query up the hierarchy if necessary return reinterpret_cast<unsigned long(&mClassType) == pasUpAddress ? true : I_TypeInfoClass::IsOfBaseType(pasUpAddress); } virtual unsigned long GetClassTypeAddress () const { return reinterpret_cast<unsigned long(&mClassType); }



Note that the snippets of code were taking out of working code and slightly modified, so there's a slight change they won't compile :-) I also used the classes I_TypeInfoClass and SubClassA in the spirit of continuing the example code, but you certainly do not have to use such names.

3. I am sure many of you will pick on more details :-)

Download the example project here: rhmrtti.zip (6k)

Comments welcome. Credit goes to Steve Anichini for letting me know how to F@$# get Visual C++ 6.0 to do pseudo template member explicit instantiations.

- Raist3d


The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

 

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.