No, there's no way to do it, and you should not think of it this way. The sender should perform the same no matter what number of slots are connected to a signal. That's the basic contract of the signal-slot mechanism: the sender is completely decoupled from, and unaware of, the receiver.
What you're trying to do is qualified dispatch: there are multiple receivers, and each receiver can process one or more message types. One way of implementing it is as follows:
Emit (signal) a QEvent
. This lets you maintain the signal-slot decoupling between the transmitter and the receiver(s).
The event can then be consumed by a custom event dispatcher that knows which objects process events of given type.
The objects are sent the event in the usual fashion, and receive it in their event()
method.
The implementation below allows the receiver objects to live in other threads. That's why it needs to be able to clone events.
class <QCoreApplication>
class <QEvent>
class ClonableEvent : public QEvent {
Q_DISABLE_COPY(ClonableEvent)
public:
ClonableEvent(int type) : QEvent(static_cast<QEvent::Type>(type)) {}
virtual ClonableEvent * clone() const { return new ClonableEvent(type()); }
}
Q_REGISTER_METATYPE(ClonableEvent*)
class Dispatcher : public QObject {
Q_OBJECT
QMap<int, QSet<QObject*>> m_handlers;
public:
Q_SLOT void dispatch(ClonableEvent * ev) {
auto it = m_handlers.find(ev->type());
if (it == m_handlers.end()) return;
for (auto object : *it) {
if (obj->thread() == QThread::currentThread())
QCoreApplication::sendEvent(obj, ev);
else
QCoreApplication::postEvent(obj, ev.clone());
}
}
void addMapping(QClonableEvent * ev, QObject * obj) {
addMapping(ev->type(), obj);
}
void addMapping(int type, QObject * obj) {
QSet<QObject*> & handlers = m_handlers[type];
auto it = handlers.find(obj);
if (it != handlers.end()) return;
handlers.insert(obj);
QObject::connect(obj, &QObject::destroyed, [this, type, obj]{
unregister(type, obj);
});
m_handlers[type].insert(obj);
}
void removeMapping(int type, QObject * obj) {
auto it = m_handlers.find(type);
if (it == m_handlers.end()) return;
it->remove(obj);
}
}
class EventDisplay : public QObject {
bool event(QEvent * ev) {
qDebug() << objectName() << "got event" << ev.type();
return QObject::event(ev);
}
public:
EventDisplay() {}
};
class EventSource : public QObject {
Q_OBJECT
public:
Q_SIGNAL void indication(ClonableEvent *);
}
#define NAMED(x) x; x.setObjectName(#x)
int main(int argc, char ** argv) {
QCoreApplication app(argc, argv);
ClonableEvent ev1(QEvent::User + 1);
ClonableEvent ev2(QEvent::User + 2);
EventDisplay NAMED(dp1);
EventDisplay NAMED(dp12);
EventDisplay NAMED(dp2);
Dispatcher d;
d.addMapping(ev1, dp1); // dp1 handles only ev1
d.addMapping(ev1, dp12); // dp12 handles both ev1 and ev2
d.addMapping(ev2, dp12);
d.addMapping(ev2, dp2); // dp2 handles only ev2
EventSource s;
QObject::connect(&s, &EventSource::indication, &d, &Dispatcher::dispatch);
emit s.indication(&ev1);
emit s.indication(&ev2);
return 0;
}
#include "main.moc"