Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using RTTI to determine inheritance graph in C++?

What, if any, c++ constructs are there for listing the ancestors of a class at runtime?

Basically, I have a class which stores a pointer to any object, including possibly a primitive type (somewhat like boost::any, which I don't want to use because I need to retain ownership of my objects). Internally, this pointer is a void*, but the goal of this class is to wrap the void* with runtime type-safety. The assignment operator is templated, so at assignment time I take the typeid() of the incoming pointer and store it. Then when I cast back later, I can check the typeid() of the cast type against the stored type_info. If it mismatches, the cast will throw an exception.

But there's a problem: It seems I lose polymorphism. Let's say B is a base of D. If I store a pointer to D in my class, then the stored type_info will also be of D. Then later on, I might want to retrieve a B pointer. If I use my class's method to cast to B*, then typeid(B) == typeid(D) fails, and the cast raises an exception, even though D->B conversion is safe. Dynamic_cast<>() doesn't apply here, since I'm operating on a void* and not an ancestor of B or D.

What I would like to be able to do is check is_ancestor(typeid(B), typeid(D)). Is this possible? (And isn't this what dynamic_cast<> is doing behind the scenes?)

If not, then I am thinking of taking a second approach anyway: implement a a class TypeInfo, whose derived classes are templated singletons. I can then store whatever information I like in these classes, and then keep pointers to them in my AnyPointer class. This would allow me to generate/store the ancestor information at compile time in a more accessible way. So failing option #1 (a built-in way of listing ancestors given only information available at runtime), is there a construct/procedure I can use which will allow the ancestor information to be generated and stored automatically at compile-time, preferably without having to explicitly input that "class A derives from B and C; C derives from D" etc.? Once I have this, is there a safe way to actually perform that cast?

like image 294
trbabb Avatar asked Aug 09 '11 07:08

trbabb


People also ask

What is RTTI in C?

Run-time type information (RTTI) is a mechanism that allows the type of an object to be determined during program execution. RTTI was added to the C++ language because many vendors of class libraries were implementing this functionality themselves. This caused incompatibilities between libraries.

What is runtime type casting?

RTTI (Run-Time Type Information) in C++ It allows the type of an object to be determined during program execution. Runtime Casts. The runtime cast, which checks that the cast is valid, is the simplest approach to ascertain the runtime type of an object using a pointer or reference.

How is RTTI implemented in C++?

Typically, RTTI is implemented by placing an additional pointer in a class s virtual function table. This pointer points to the type_info structure for that particular type.


1 Answers

I had a similar problem which I solved through exceptions! I wrote an article about that:

Part 1, Part 2 and code

Ok. Following Peter's advise the outline of the idea follows. It relies on the fact that if D derives from B and a pointer to D is thrown, then a catch clause expecting a pointer to B will be activated.

One can then write a class (in my article I've called it any_ptr) whose template constructor accepts a T* and stores a copy of it as a void*. The class implements a mechanism that statically cast the void* to its original type T* and throws the result. A catch clause expecting U* where U = T or U is a base of T will be activated and this strategy is the key to implementing a test as in the original question.

EDIT: (by Matthieu M. for answers are best self-contained, please refer to Dr Dobbs for the full answer)

class any_ptr {

    void* ptr_;
    void (*thr_)(void*);

    template <typename T>
    static void thrower(void* ptr) { throw static_cast<T*>(ptr); }

public:

    template <typename T>
    any_ptr(T* ptr) : ptr_(ptr), thr_(&thrower<T>) {}

    template <typename U>
    U* cast() const {
        try { thr_(ptr_); }
        catch (U* ptr) { return ptr; }
        catch (...) {}
        return 0;
    }
};
like image 100
Cassio Neri Avatar answered Oct 06 '22 10:10

Cassio Neri