Объявлять абстрактный сигнал в classе интерфейса

Как объявить Qt-сигнал в абстрактном classе / интерфейсе, когда class реализации уже вырван из 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(); } 

    Как я узнал в последние дни … Qt способ сделать это выглядит так:

     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(); } 

    Теперь вы можете подключиться к этим интерфейсным сигналам.

    Если у вас нет доступа к реализации при подключении к сигналу, оператор вашего соединения потребует динамического добавления в QObject :

     IEmitSomething* es = ... // your implementation class connect(dynamic_cast(es), SIGNAL(someThingHappended()), ...); 

    ... и таким образом вы не должны подвергать class реализации подписчикам и клиентам. Да!!!

    В Qt «сигналы» являются синонимичными для «защищенных». Но это помогает MOC генерировать необходимый код. Итак, если вам нужен интерфейс с некоторыми сигналами – вы должны объявить их как виртуальные абстрактные защищенные методы. Весь необходимый код будет создан MOC – вы можете увидеть подробности, что «emit somesignal» будет заменен виртуальным вызовом защищенного метода с тем же именем. Обратите внимание, что тело с методом также генерируется Qt.

    ОБНОВЛЕНИЕ: Пример кода:

    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  #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  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  #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(); } 

    Есть две проблемы с объявлением сигналов в виде абстрактных методов в интерфейсах:

    1. Сигнал является сигналом с точки зрения Qt только тогда, когда он реализуется определенным образом, а именно, когда реализация генерируется moc и включается в метаданные для объекта.

    2. Обычно плохой дизайн излучает сигналы непосредственно снаружи объекта.

    В качестве следствия, поскольку интерфейс является абстрактным, вам совсем не нужно декларировать свои сигналы вообще – он не служит цели, кроме как для документирования намерения, поскольку:

    1. Если сигнал реализован в classе, который получен из интерфейса, вы можете использовать систему метаобъектов, чтобы проверить его присутствие.

    2. В любом случае вы не должны напрямую обращаться к этим методам сигнала.

    3. После того, как вы динамически передаете необъектный интерфейс в QObject , QObject не имеет значения, что реализация была получена из интерфейса.

    Единственными действительными причинами, положенными для такой гимнастики, было бы:

    1. Коаксиальный doxygen или другой генератор документации для предоставления документации для вашего кода.

    2. Заставить конкретный class иметь реализацию метода с тем же именем. Это, конечно, не гарантирует, что это сигнал.

    Давайте будем гением компьютера.