0
votes

Say I have 3 class pointers classA , classB and ClassC all of these classes are derived from QObject. I wanted to know if there was a way for me to find out from the base type what is their derive type. For Instance if i had something like this

void SomeMethod(QObject* ptr)
{
 //How can I find out if a ClassA , ClassB or ClassC derived type was passed here ?

}
1
You might be able to try RTTI. But I would just make a pure virtual function in base class which returns a different identified for each class and switch on that. OR you might be able to use boost::variant or boost::any. - Borgleader
Building your own bespoke RTTI to do type-switching isn't any better than just using the RTTI you've already got. In fact, it's worse in most ways—more code, more CPU work, and, most importantly, more opportunities to get something wrong. - abarnert
By the way, notice that in this particular case you can use QObject's nontrivial introspection capabilities. Also, with QObject you should use qobject_cast instead of dynamic_cast. - Matteo Italia
@MatteoItalia: Good point. I updated my answer to include the second half of that. For the first half… there's not really much Qt introspection can do here beyond casting or explicit type switching, because that's all the functionality the OP wants. But it's definitely worth pointing out anyway. - abarnert

1 Answers

3
votes

This is exactly what dynamic_cast is for:

void SomeMethod(QObject *ptr) {
    A *a = dynamic_cast<A *>(ptr);
    if (a) { DoStuffA(a); return; }
    B *b = dynamic_cast<B *>(ptr);
    if (b) { DoStuffB(b); return; }
    C *c = dynamic_cast<C *>(ptr);
    if (c) { DoStuffC(c); return; }
    DoStuffNOTA(ptr);
}

Since you're using Qt, you'd probably want to use qobject_cast instead of dynamic_cast,* but otherwise everything is the same.

However, all that repetition is, as usually, a sign that you're doing something wrong. You can eliminate a lot of it by using templates, but usually, you usually don't want to do this at all. Instead, use the "double dispatch" (aka "reverse dispatch") pattern.

The whole reason we have virtual functions, and OO in general, is to avoid type switching. The problem is that virtual functions (the way C++ does them) only dispatch on the this object: Foo::SomeMethod(QObject *) can be overridden for subclasses of Foo, but not for subclasses of QObject. But you can just add a method to QObject—or, in this case, to an intemediate class (or a multiple-inheritance mixin, if that isn't possible)—that all of your real classes can override:

class ClassIntermediate: public QObject {
public:
    virtual void DoSomeMethodStuff() = 0;
};

class ClassA : public ClassIntermediate {
public:
    virtual void DoSomeMethodStuff() { /* DoStuffA code here */ }
    // all the stuff you were already doing in ClassA as a QObject
};

// likewise for ClassB and ClassC

class Foo {
    void SomeMethod(ClassIntermediate *ptr) { ptr->DoSomeMethodStuff(); }
};

If there's some work that has to be done for all types, leave it in SomeMethod, before or after the call to DoSomeMethodStuff. (If that work is intermixed with the subclass-specific stuff, and you can't rearrange it, just factor out multiple methods to double-dispatch to instead of just one.)

If the code needs the Foo object's this pointer, just pass it as an argument to DoSomeMethodStuff.

If you need to handle any QObject, not just your subclasses, then you may want to use a single dynamic_cast<ClassIntermediate *> and double-dispatch to that.

So, why is this better than type switching? Today, you have three classes, and one method that switches on them. If you add another method tomorrow, you have to write the same 3-way type switch again. If you then add a fourth class, you have to remember to go back and edit them both. If you end up with N classes and M methods that have to switch on them, you've written N*M blocks of code instead of N+M. All that extra code isn't just annoying; it virtually guarantees that you're going to miss one of the edits one day, leading to a painful debugging problem.


The main advantage of qobject_cast is that Qt provides its own, more extensive, RTTI system than what's built in to C++, and it's possible to build Qt without C++ RTTI at all (or, on Windows, in such a way that classes in different DLLs can be connected up via Qt RTTI but not C++ RTTI), and qobject_cast will still work.