/* PLIB - A Suite of Portable Game Libraries Copyright (C) 1998,2002 Steve Baker This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA For further information visit http://plib.sourceforge.net */ /* Originally written by: Alexandru C. Telea */ /* This file provides support for RTTI and generalized (virtual-base to derived and separate hierarchy branches) casting. There is also support for RT obj creation from type names. In order to enable these features for a class, two things should be done: 1) insert the text UL_TYPE_DATA (without ';') in the class-decl. 2) in the .C file where the class's implementation resides, insert the following (without';'): UL_RTTI_DEF(classname) if the class has no bases with RTTI UL_RTTI_DEFn(classname,b1,...bn) if the class has bases b1,...bn with RTTI Use UL_RTTI_DEF_INST instead of UL_RTTI_DEF if you want to enable RT obj creation for classname. You should provide then a public default ctor. RTTI is used via a class called ulRTTItypeid. A typeid describes a type of a class. [..] They should provide all necessary support for any kind of RTTI/casting [..]. [..] REMARK: There are two classes related to RTTI: ulRTTItypeid and ======= ulRTTITypeinfo. A ulRTTItypeid is, as it says, an 'id for a type'. It actually wraps a ulRTTITypeinfo*, where a ulRTTITypeinfo contains the actual encoding of a class type. You can freely create/copy/destroy/manipulate ulRTTItypeid's, but you should NEVER deal directly with ulRTTITypeinfo. A ulRTTITypeinfo should actually be created ONLY by the UL_TYPE_DATA macros, as part of a class definition, since the ulRTTITypeinfo encodes a type info for an EXISTING class [..]. All type-related stuff should be therefore handled via ulRTTItypeid's. */ #ifndef _UL_RTTI_H_ #define _UL_RTTI_H_ #include #include "ul.h" class ulRTTITypeinfo /* Implementation of type-related info */ { private: char *n ; /* type name */ /* base types (NULL-ended array of ulRTTITypeinfo's for this's direct bases) */ const ulRTTITypeinfo** b ; int ns ; /* #subtypes of this type */ const ulRTTITypeinfo **subtypes ; /* types derived from this type */ /* convenience type info for a 'null' type */ static const ulRTTITypeinfo null_type ; void* (*new_obj)() ; /* func to create a new obj of this type */ void* (*cast)(int,void*) ; /* func to cast an obj of this type to ith baseclass of it or to itself */ /* adds a subtype to this's subtypes[] */ void add_subtype ( const ulRTTITypeinfo * ) ; /* dels a subtype from this's subtypes[] */ void del_subtype ( const ulRTTITypeinfo* ) ; friend class ulRTTItypeid ; /* for null_type */ public: ulRTTITypeinfo ( const char* name, const ulRTTITypeinfo* bb[], void* (*)(int,void*),void* (*)() ) ; ~ulRTTITypeinfo () ; /* Returns name of this ulRTTITypeinfo */ const char* getname () const { return n ; } /* Compares 2 ulRTTITypeinfo objs */ bool same ( const ulRTTITypeinfo *p ) const { /* First, try to see if it's the same 'physical' ulRTTITypeinfo (which should be the case, since we create them per-class and not per-obj). */ return ( this == p ) || !strcmp ( n, p->n ) ; } /* true if the arg can be cast to this, else false */ bool can_cast ( const ulRTTITypeinfo *p ) const { return same ( p ) || p->has_base ( this ) ; } /* true if this has the arg as some base, else false */ bool has_base ( const ulRTTITypeinfo *p ) const { for ( int i = 0 ; b[i] != NULL ; i++ ) /* for all bases of this... */ /* match found, return 1 or no match, search deeper */ if ( p->same ( b[i] ) || b[i]->has_base ( p ) ) return true ; return false ; /* no match at all, return false */ } /* get i-th subclass of this, if any, else NULL */ const ulRTTITypeinfo * subclass ( int i = 0 ) const { return ( i >= 0 && i < ns ) ? subtypes[i] : NULL ; } int num_subclasses () const { return ns ; } /* get # subclasses of this */ /* search for a subclass named char*, create obj of it and return it cast to the ulRTTITypeinfo* type, which is either this or a direct base of this. */ void * create ( const ulRTTITypeinfo *, const char * ) const ; /* Returns true if this type has a default ctor, else false */ bool can_create () const { return new_obj != NULL ; } } ; class ulRTTItypeid /* Main class for RTTI interface */ { protected: /* ulRTTItypeid implementation (the only data-member) */ const ulRTTITypeinfo* id ; public: /* Not for application use ! */ const ulRTTITypeinfo* get_info () const { return id ; } ulRTTItypeid ( const ulRTTITypeinfo* p ) : id ( p ) { } ulRTTItypeid () : id ( &ulRTTITypeinfo::null_type ) { } /* Compares 2 ulRTTItypeid objs */ bool isSame ( ulRTTItypeid i ) const { return id->same ( i.id ) ; } /* true if the arg can be cast to this, else false */ bool canCast ( ulRTTItypeid i ) const { return id->can_cast ( i.id ) ; } const char * getName () const { return id->getname () ; } /* Return # subclasses of this */ int getNumSubclasses () const { return id->num_subclasses () ; } /* Return ith subclass of this */ ulRTTItypeid getSubclass ( int i ) const { return id->subclass ( i ) ; } /* Return # baseclasses of this */ int getNumBaseclasses () const { int i ; for ( i = 0 ; id->b[i] != NULL ; i++ ) ; return i ; } /* Return ith baseclass of this */ ulRTTItypeid getBaseclass ( int i ) const { return id->b[i] ; } /* Tries to create an instance of a subclass of this having of type given by the ulRTTItypeid arg. If ok, it returns it casted to the class-type of this and then to void* */ void * create ( ulRTTItypeid t ) const { return id->create ( id, t.getName () ) ; } /* Returns true if this type is instantiable, else false */ bool canCreate () const { return id->can_create () ; } } ; class ulRTTIdyntypeid : public ulRTTItypeid /* Class for dynamic type creation from user strings. Useful for creating typeids at RT for comparison purposes. */ { private: static const ulRTTITypeinfo *a[] ; public: ulRTTIdyntypeid ( const char *c ) : /* create a dummy ulRTTITypeinfo */ ulRTTItypeid ( new ulRTTITypeinfo ( c, a, NULL, NULL ) ) { } ~ulRTTIdyntypeid () { delete id ; /* delete the dummy ulRTTITypeinfo */ } } ; /* Macros */ /* 'ulRTTItypeid' UL_STATIC_TYPE_INFO(T) T=RTTI-class name. Returns a ulRTTItypeid with T's type. If T hasn't RTTI, a compile-time error occurs. */ #define UL_STATIC_TYPE_INFO(T) T::RTTI_sinfo() /* 'T*' UL_PTR_CAST(T,p) T=RTTI-class, p=RTTI-class ptr. Returns p cast to the type T as a T*, if cast is possible, else returns NULL. If *p or T have no RTTI, a compile-time error occurs. Note that p can point to virtual base classes. Casting between separat branches of a class hierarchy is also supported, as long as all classes have RTTI. Therefore UL_PTR_CAST is a fully general and safe operator. If p==NULL, the operator returns NULL. */ #define UL_PTR_CAST(T,p) ((p != NULL)? (T*)((p)->RTTI_cast(UL_STATIC_TYPE_INFO(T))) : NULL) /* 'T*' UL_TYPE_NEW(T,t) T=RTTI-class, t=ulRTTItypeid Returns a new object of type t cast to the type T as a T*. t must represent a type identical to or derived from T. If t is not a type derived from T or not an instantiable type having a default constructor, NULL is returned. */ #define UL_TYPE_NEW(T,t) ((T*)t.create(T)) /* Definition of TYPE_DATA for a RTTI-class: introduces one static ulRTTITypeinfo data-member and a couple of virtuals. */ #define UL_TYPE_DATA \ protected: \ static const ulRTTITypeinfo RTTI_obj; \ static void* RTTI_scast(int,void*); \ static void* RTTI_new(); \ virtual ulRTTItypeid RTTI_vinfo() const { return &RTTI_obj; }\ public: \ static ulRTTItypeid RTTI_sinfo() { return &RTTI_obj; }\ virtual void* RTTI_cast(ulRTTItypeid); /* Definition of auxiliary data-structs supporting RTTI for a class: defines the static ulRTTITypeinfo object of that class and its associated virtuals. */ /* Auxiliary definition of the construction method: */ #define UL_RTTI_NEW(cls) void* cls::RTTI_new() { return new cls; } \ const ulRTTITypeinfo cls::RTTI_obj(#cls,RTTI_base_ ## cls,cls::RTTI_scast,cls::RTTI_new); #define UL_RTTI_NO_NEW(cls) const ulRTTITypeinfo cls::RTTI_obj(#cls,RTTI_base_ ## cls,cls::RTTI_scast,NULL); /* Top-level macros: */ #define UL_RTTI_DEF_BASE(cls) \ static const ulRTTITypeinfo* RTTI_base_ ## cls [] = { NULL }; \ void* cls::RTTI_cast(ulRTTItypeid t) \ { \ if (t.isSame(&RTTI_obj)) return this; \ return NULL; \ } \ void* cls::RTTI_scast(int i,void* p) \ { cls* ptr = (cls*)p; return ptr; } #define UL_RTTI_DEF1_BASE(cls,b1) \ static const ulRTTITypeinfo* RTTI_base_ ## cls [] = \ { UL_STATIC_TYPE_INFO(b1).get_info(), NULL }; \ void* cls::RTTI_cast(ulRTTItypeid t) \ { \ if (t.isSame(&RTTI_obj)) return this; \ void* ptr; \ if ((ptr=b1::RTTI_cast(t))) return ptr; \ return NULL; \ } \ void* cls::RTTI_scast(int i,void* p) \ { cls* ptr = (cls*)p; \ switch(i) \ { case 0: return (b1*)ptr; } \ return ptr; \ } #define UL_RTTI_DEF2_BASE(cls,b1,b2) \ static const ulRTTITypeinfo* RTTI_base_ ## cls [] = \ { UL_STATIC_TYPE_INFO(b1).get_info(), \ UL_STATIC_TYPE_INFO(b2).get_info(), NULL }; \ void* cls::RTTI_cast(ulRTTItypeid t) \ { \ if (t.isSame(&RTTI_obj)) return this; \ void* ptr; \ if ((ptr=b1::RTTI_cast(t))) return ptr; \ if ((ptr=b2::RTTI_cast(t))) return ptr; \ return NULL; \ } \ void* cls::RTTI_scast(int i,void* p) \ { cls* ptr = (cls*)p; \ switch(i) \ { case 0: return (b1*)ptr; \ case 1: return (b2*)ptr; \ } \ return ptr; \ } #define UL_RTTI_DEF3_BASE(cls,b1,b2,b3) \ static const ulRTTITypeinfo* RTTI_base_ ## cls [] = \ { UL_STATIC_TYPE_INFO(b1).get_info(), \ UL_STATIC_TYPE_INFO(b2).get_info(), \ UL_STATIC_TYPE_INFO(b3).get_info(), NULL }; \ void* cls::RTTI_cast(ulRTTItypeid t) \ { \ if (t.isSame(&RTTI_obj)) return this; \ void* ptr; \ if ((ptr=b1::RTTI_cast(t))) return ptr; \ if ((ptr=b2::RTTI_cast(t))) return ptr; \ if ((ptr=b3::RTTI_cast(t))) return ptr; \ return NULL; \ } \ void* cls::RTTI_scast(int i,void* p) \ { cls* ptr = (cls*)p; \ switch(i) \ { case 0: return (b1*)ptr; \ case 1: return (b2*)ptr; \ case 2: return (b3*)ptr; \ } \ return ptr; \ } #define UL_RTTI_DEF_INST(cls) \ UL_RTTI_DEF_BASE(cls) \ UL_RTTI_NEW(cls) #define UL_RTTI_DEF(cls) \ UL_RTTI_DEF_BASE(cls) \ UL_RTTI_NO_NEW(cls) #define UL_RTTI_DEF1_INST(cls,b1) \ UL_RTTI_DEF1_BASE(cls,b1) \ UL_RTTI_NEW(cls) #define UL_RTTI_DEF1(cls,b1) \ UL_RTTI_DEF1_BASE(cls,b1) \ UL_RTTI_NO_NEW(cls) #define UL_RTTI_DEF2_INST(cls,b1,b2) \ UL_RTTI_DEF2_BASE(cls,b1,b2) \ UL_RTTI_NEW(cls) #define UL_RTTI_DEF2(cls,b1,b2) \ UL_RTTI_DEF2_BASE(cls,b1,b2) \ UL_RTTI_NO_NEW(cls) #define UL_RTTI_DEF3_INST(cls,b1,b2,b3) \ UL_RTTI_DEF3_BASE(cls,b1,b2,b3) \ UL_RTTI_NEW(cls) #define UL_RTTI_DEF3(cls,b1,b2,b3) \ UL_RTTI_DEF3_BASE(cls,b1,b2,b3) \ UL_RTTI_NO_NEW(cls) #endif