44

How to declare a Qt signal in an abstract class / interface when the implementing class is already derrived from QObject/QWidget?

class IEmitSomething
{
   public:
     // this should be the signal known to others
     virtual void someThingHappened() = 0;
}

class ImplementEmitterOfSomething : public QWidget, public IEmitSomething
{
     // signal implementation should be generated here
     signals: void someThingHappended();
}
Louis Langholtz
  • 2,913
  • 3
  • 17
  • 40
Beachwalker
  • 7,685
  • 6
  • 52
  • 94

4 Answers4

66

As I found out in the last days... the Qt way of doing this is like this:

class IEmitSomething
{
   public:
     virtual ~IEmitSomething(){} // do not forget this

   signals: // <- ignored by moc and only serves as documentation aid
            // The code will work exactly the same if signals: is absent.
     virtual void someThingHappened() = 0;
}

Q_DECLARE_INTERFACE(IEmitSomething, "IEmitSomething") // define this out of namespace scope

class ImplementEmitterOfSomething : public QWidget, public IEmitSomething
{
   Q_OBJECT
   Q_INTERFACES(IEmitSomething)

   signals:
      void someThingHappended();
}

Now you can connect to those interface signals.

If you don't have access to the implementation when connecting to the signal your connect statement will require a dynamic cast to QObject:

IEmitSomething* es = ... // your implementation class

connect(dynamic_cast<QObject*>(es), SIGNAL(someThingHappended()), ...);

... and this way you're not forced to expose the implementation class to subscribers and clients. Yeah!!!

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
Beachwalker
  • 7,685
  • 6
  • 52
  • 94
  • 1
    Time goes by... I don't remember the reference but if you got the keywords Q_INTERFACES and Q_DECLARE_INTERFACE then you can google for that. The problem at the time of my post was that I didn't knew them. You can read something about here http://doc.qt.io/qt-5/qtplugin.html#Q_DECLARE_INTERFACE and browse along the infos about that interface. – Beachwalker Feb 18 '15 at 08:36
  • Any idea why virtual void someThingHappended(); results in a compiler warning, see here: http://stackoverflow.com/questions/28614607/virtual-override-for-signal-signals-cannot-be-declared-virtual – Horst Walter Feb 19 '15 at 19:00
  • maybe because of c++11 changes in Visual Studio (override is a new keyword) – Beachwalker Feb 21 '15 at 08:25
  • According to [this](https://stackoverflow.com/a/27921486/4358570) signals are not supposed to be virtual. – Super-intelligent Shade Jun 11 '17 at 20:49
  • 4
    I am amazed that this actually works, but bummed out by the fact that it requires the old-style SIGNAL()/SLOT() syntax... – Sty Dec 08 '17 at 16:39
  • So in this example is the derived class' someThingHappended() overriding the inherited class' pure virtual someThingHappended()? It must be right or else this wouldn't compile. Also why does IEmitSomething not derive QObject/Use the QObject macro? Others were saying that introduces the diamond inheritance problem, but I don't see it. – C. Zach Martin Sep 12 '19 at 22:12
  • Don't forget to completely clean and rebuild the whole project after ANY modification to the interface class. Otherwise the Qt MOC goes wild and throws errors: – Equilibrius Apr 17 '20 at 12:46
  • The problem is if you try to connect to ImplementEmitterOfSomething with regular syntax : connect (obj,&ImplementEmitterOfSomething::somethingHappened,...) The connect fails – AlGrenadine Oct 13 '21 at 14:42
  • For anyone with an interface using slots, this approach also works. Put `public slots:` in the interface and it'll build fine allowing you to require those slots in any derived class. – Paul Masri-Stone Mar 31 '22 at 08:31
18

In Qt, "signals" is synonim for "protected". But it helps MOC to generate necessary code. So, if you require interface with some signals - you should declare them as virtual abstract protected methods. All neccessary code will be generated by MOC - you may see details, that "emit somesignal" will be replaced with virtual call of protected method with same name. Note, that the body of with method aslo generated by Qt.

UPDATE: Sample code:

MyInterfaces.h

#pragma once

struct MyInterface1
{
signals:
    virtual void event1() = 0;
};

struct MyInterface2
{
signals:
    virtual void event2() = 0;
};

MyImpl.h

#ifndef MYIMPL_H
#define MYIMPL_H

#include <QObject>
#include "MyInterfaces.h"

class MyImpl
    : public QObject
    , public MyInterface1
    , public MyInterface2
{
    Q_OBJECT

public:
    MyImpl( QObject *parent );
    ~MyImpl();

    void doWork();

signals:
    void event1();
    void event2();
};

class MyListner
    : public QObject
{
    Q_OBJECT

public:
    MyListner( QObject *parent );
    ~MyListner();

public slots:
    void on1();
    void on2();
};

#endif // MYIMPL_H

MyImpl.cpp

#include "MyImpl.h"
#include <QDebug>

MyImpl::MyImpl(QObject *parent)
    : QObject(parent)
{}

MyImpl::~MyImpl()
{}

void MyImpl::doWork()
{
    emit event1();
    emit event2();
}

MyListner::MyListner( QObject *parent )
{}

MyListner::~MyListner()
{}

void MyListner::on1()
{
    qDebug() << "on1";
}

void MyListner::on2()
{
    qDebug() << "on2";
}

main.cpp

#include <QCoreApplication>
#include "MyImpl.h"

int main( int argc, char *argv[] )
{
    QCoreApplication a( argc, argv );

    MyImpl *invoker = new MyImpl( NULL );
    MyListner *listner = new MyListner( NULL );

    MyInterface1 *i1 = invoker;
    MyInterface2 *i2 = invoker;

    // i1, i2 - not QObjects, but we are sure, that they will be.
    QObject::connect( dynamic_cast< QObject * >( i1 ), SIGNAL( event1() ), listner, SLOT( on1() ) );
    QObject::connect( dynamic_cast< QObject * >( i2 ), SIGNAL( event2() ), listner, SLOT( on2() ) );

    invoker->doWork();

    return a.exec();
}
Dmitry Sazonov
  • 8,801
  • 1
  • 35
  • 61
  • if you are sure that i1 is a QObject, then dynamic_cast is not needed. static_cast works fine in this case. – Fernando Pelliccioni May 05 '15 at 21:27
  • 2
    @FernandoPelliccioni no, you completely wrong. Because interface does not inherit `QObject`. Only `reinterpret_cast` may be used, but it doesn't work - because there is multiply inheritance. – Dmitry Sazonov May 06 '15 at 09:36
  • 1
    @SaZ Oh! I misread the code. I didn't see the cross-casting. In my misreading I visualized a down-casting. Sorry for the noise. – Fernando Pelliccioni May 06 '15 at 21:38
6

There are two problems with declaring signals as abstract methods in interfaces:

  1. A signal is a signal from Qt's viewpoint only when implemented in a particular way - namely, when the implementation is generated by moc, and is included in the metadata for the object.

  2. It is usually bad design to emit signals directly from the outside of an object.

As a corollary, since the interface is abstract, you don't really need to declare its signals at all - it serves no purpose other than for documenting the intent, since:

  1. If a signal is implemented in a class that derives from the interface, you can use the metaobject system to verify its presence.

  2. You're not supposed to directly call these signal methods anyway.

  3. Once you dynamically cast the non-object interface to QObject, it doesn't matter anymore that the implementation was deriving from the interface.

The only valid reasons left for doing such gymnastics would be to:

  1. Coax doxygen or another documentation generator into providing documentation for your code.

  2. Force the concrete class to have an implementation of a method with the same name. This doesn't of course guarantee that it is in fact a signal.

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • I feel that there is one another reason to declare signal in an abstract interface: Imagine you have some widget, that accepts object by pointer to this interface. Let object has abstract methods `QString getName()`, `setName(QString)` and signal `nameModified`. Then your widget can subscribe itself to this signal in constructor to get notified by concrete implementation about name changes. –  Oct 26 '18 at 08:54
  • 1
    This will work just fine with the connection made by name (QT4 syntax), and the base class doesn’t need to even have a signal, as long as the derived class does. What the base class will need instead (and all such “derive QObject from interface” classes do need it!) is a `virtual QObject *object() = 0;` method – otherwise you won’t be able to get a pointer to object to begin with :) – Kuba hasn't forgotten Monica Oct 26 '18 at 15:49
  • There's another reason: if you are deriving from an abstract interface, it means you are using multiple inheritance (because you must derive drom QObject to use signals and abstract interface doesn't derive from QObject). Multiple inheritance prevents you from ysing Qt5 form of connect() – Hedede Jul 27 '21 at 06:50
  • (Because comparing pointers to virtual member functions is unspecified in C++ and QMetaObject fails to find the signal.) – Hedede Jul 27 '21 at 06:58
3

We all want to get rid of MOC for good, but until that happens I want to add an alternative that works without including QObject.h and without using Q_OBJECT and Q_INTERFACE in the interface class.

First define an abstract connect function in the interface:

class I_Foo
{
public:
    virtual void connectToSignalA(const QObject * receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection) = 0;
};

Now in the derived class, override the function. Also declare the signal, add Q_OBJECT etc.

class Bar : public QObject, public I_Foo
{
    Q_OBJECT

public:
    void connectToSignalA(const QObject * receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection);

signals:
    void A();
};

Then inside that classes .cpp do the connect:

Bar::connectToSignalA(const QObject * receiver, const char *method, Qt::ConnectionType void type)
{
    connect(this, SIGNAL(A()), receiver, method, type);
}

The caveat is, that you have to write the connect function in every derived class and you have to use the old-style-connect (or maybe use a template function), but that's about it.

Bim
  • 1,008
  • 1
  • 10
  • 29