Question

For a specific class hiararchy I need to know if a base class reference is an instance of a specific derived class. For different reasons, I can't use standard C++ RTTI here and I need to implement a custom instanceof mechanism.

The LLVM-stle RTTI would suite my needs but I was wondering if it would exists a way (somehow using templates) to automate the implementation of the classof method?

Are there other/simpler implementation of such mechanism that would allow to know if a base class is an instance of a derived class?

My constraints:

  • I don't have multiple inheritance but I have several level of inheritance.
  • Inpact on memory footprint must be as minimal as possible and it is not possible to perform dynamic allocation.
Was it helpful?

Solution

I was wondering if it would exists a way (somehow using templates) to automate the implementation of the classof method?

Yes, there are ways to automate the classof method, I really don't understand why the LLVM page would demonstrate a hand-rolled set of classof methods, since it is so much more scalable if you automate that very simple process.

Here is a very basic solution:

class TypedObject {
  public:
    virtual ~TypedObject() { };

    virtual int getClassId() const { return 0; };
    static int getStaticClassId() { return 0; };

    virtual bool isOfType(int aID) const { return (aID == 0); };

    template <typename T>
    bool isOfClass() const { return isOfType( T::getStaticClassId() ); };

};

The runtime-cast (i.e., dynamic_cast) functions would look like this:

template <typename T>
T* runtime_ptr_cast(TypedObject* p) {
  if( (p) && (p->isOfClass<T>()) )
    return static_cast<T*>( p );
  return NULL;
};

template <typename T>
typename std::enable_if< 
  std::is_const< T >::value,
T* >::type runtime_ptr_cast(const TypedObject* p) {
  if( (p) && (p->isOfClass<T>()) )
    return static_cast<T*>( p );
  return NULL;
};

then, all you need are MACROs to automate the creation of the virtual and static functions:

#define MY_RTTI_SYSTEM_CREATE_TYPE_1_BASE( NEWCLASSID, BASECLASSNAME ) \
  public: \
    virtual int getClassId() const { return NEWCLASSID; }; \
    static int getStaticClassId() { return NEWCLASSID; }; \
     \
    virtual bool isOfType(int aID) const { \
      return ((aID == NEWCLASSID) || BASECLASSNAME::isOfType(aID)); \ 
    };

Then, you can create a new class like this:

class Foo : public TypedObject {

  // ... some code, as usual ...

  // call the macro with a given ID number and the name of the base-class:
  MY_RTTI_SYSTEM_CREATE_TYPE_1_BASE(1, TypedObject)
};

Which leads to:

int main() {
  Foo f;
  TypedObject* b = &f;

  // check the type:
  if( b->isOfClass<Foo>() ) 
    std::cout << "b is indeed for class Foo!" << std::endl;

  // make a dynamic cast:
  Foo* pf = runtime_ptr_cast<Foo>( b );
  if( pf )
    std::cout << "cast to 'Foo*' was successful!" << std::endl;

  const TypedObject* cb = b; 
  const Foo* cpf = runtime_ptr_cast<const Foo>( cb );
  if( cpf )
    std::cout << "cast to 'const Foo*' was successful!" << std::endl;

  Foo* pf2 = runtime_ptr_cast<Foo>( cb ); // ERROR: no such function (invalid cast).
};

And of course, you can extend this to multiple inheritance too, by just creating more MACROs for registering the types. There are also countless variations on this scheme (personally, in my implementation, I register the types to a global repository and give access to factory-functions too).

I don't think that there is any practical way to avoid having to use a MACRO-call in each class that you create. I've thought about it for a while (some time ago, as I was making my own) and I concluded that the easiest and cleanest solution was to have a MACRO-call in the classes (even though I have great disdain for MACROs in general). But I don't know, maybe others have a better (template-based) solution to this that doesn't cause too much clutter or isn't too intrusive. I've been using this scheme for years, and it is very nice and clean.

I don't have multiple inheritance but I have several level of inheritance.

The above scheme works for any level of inheritance (i.e., it is a scalable solution). It can also easily be adapted to multiple-inheritance if one day you desire to do so.

Impact on memory footprint must be as minimal as possible

I know that LLVM prefers a solution without any virtual functions and using instead an integral-id data member in the base-classes. It becomes a bit harder to achieve the same kind of functionality as above with that kind of scheme (but possible). It's much easier with virtual functions, which occupy only the space of one pointer (vtable pointer) which often isn't much bigger than an integral-id data member. And if classes are already polymorphic, the cost is nothing at all. And, of course, the above is much lighter-weight than the built-in C++ RTTI. So, unless you really want to squeeze those few bytes that you could spare with an integral-id (or enum) solution, I would recommend you go with a solution based on virtual functions like I showed above.

it is not possible to perform dynamic allocation.

Dynamic allocation is not needed in general. Only the more complicated (and feature-rich) RTTI implementations would require some dynamic allocation. If all you want is to be able to do "classof()" (and thus, dynamic-casts), no dynamic memory allocation is needed, for sure.

OTHER TIPS

You want some kind of tree like data structure as a global variable to store your class hierarchy

class Foo : public Foo_Parent {
  IS_PART_OF_HIERARCHY

public:
  Foo();
  ...
}

#define IS_PART_OF_HIERARCHY
  private:
    static Hierarchy<string> *node;
  public:
    bool isChildOf( string parent ) const;
    bool isParentOf( string child ) const;

In .cpp file

INSERT_INTO_HIERARCHY( Foo, Foo_Parent )

Foo::Foo() {}
....

#define INSERT_INTO_HIERARCHY( class_name, parent_class_name )
  Hierarchy<string> class_name::node = classes_hierarchy.insertAfter( #parent_class_name );
  bool class_name::isChildOf const( string ) {
      auto *node = class_name::node;
      // traverse the parents of node
  }
  bool class_name::isParentOf const( string ) {
      auto *node = class_name::node;
      // traverse the children of node
  }

I can't find a hierarchy class in the STL, it is little tricky to implement one, I don't know if it is worth the effort.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top