15 June 2013

Mastering Interfaces with QT

from high languages like java or c# interfaces are known since the very first time, unfortunately this feature is not available in c++. But c++ has a different feature that java for instance does not support: multi inheritance.

Now you may wonder how these obvious different things are connected? Quite simple: pure abstract classes can be used as interfaces; since c++ can inherit from multiple classes this is the solution for the missing interface feature.

Since all this is not very new and i guess you already know about this i promise now to speed up the out pointing.

meta object system and multiple inheritance

in general QT meta object system does not like multi inheritance whenever both classes inherit QObject, which is the recommended default scenario in each QT code tree.
It took me a while research how this problem can be solved in a QT compatible way. Looking backward is sounds totally easy, even if your interfaces inherit from each other: not let your interface inherit from QObject and use the macro Q_DECLARE_INTERFACE.

The following lines shows how this could look like:
// IContainer.h

class IContainer {

public:
    virtual bool isValid() = 0;
    virtual ~IContainer() {};
};
Q_DECLARE_INTERFACE(
    IContainer,
    "com.sample.Demo.IContainer/1.0"
)

Later when you want to implement a concrete class that support that interface you have to write one small macro to say the meta system that you implement a given interface Q_INTERFACES.
It could look like this:
// MessageContainerBase.h

#include "IContainer.h"

class MessageContainerBase : public QObject, 
                             public IContainer {
    Q_OBJECT
    Q_INTERFACES(IContainer)
public:
    MessageContainerBase();
    virtual             ~MessageContainerBase() { }

    virtual bool        isValid();
};

The Benefit

The main benefit is simple type safeness.
Type safeness in terms of down cast from a given pointer to that interface. With qobject_cast you can easily convert a QObject* into a IContainer* pointer with the safeness of typing. Means whenever you have QObject* or something else you can cast it into the Interface and can check if the resulting pointer is not null. If it is not null you can be sure that the instance supports your requested interface.
// Foo.cpp

#include "IContainer.h"

void Foo::bar(QObject* bak) {

    IContainer* container = NULL;
    bool valid = false;

    container = qobject_cast< IContainer* >(bak);
    if(container) {
        // inerface is supported, do some nice stuff
        valid = container->isValid();
    }
}

2 comments:

  1. Anal, but QT is quicktime, Qt is the toolkit ;p

    ReplyDelete
    Replies
    1. Next time i'll take care of it ;)

      Delete