I'm not entirely sure I understand what you're after but it appears that given a QObject *emitter
and a signal name signal_one
you want to find all slots in a second QObject *receiver
that are compatible with signal_one
and automatically create the required connections. If so then I think that's possible using just the Qt
metadata infrastructure -- QMetaObject
, QMetaMethod
etc.
Firstly defined a function are_compatible
which compares two QMetaMethod
instances for calling compatibility.
bool are_compatible (const QMetaMethod &signal_meta,
const QMetaMethod &slot_meta)
{
/*
* Work on the assumption that the arity of the signal must be at least that
* of the slot.
*/
if (signal_meta.parameterCount() < slot_meta.parameterCount())
return false;
/*
* Now check that all required parameters have the same type.
*/
for (int i = 0; i < slot_meta.parameterCount(); ++i) {
if (signal_meta.parameterType(i) != slot_meta.parameterType(i))
return false;
}
return true;
}
Now define the main function we need that should automatically connect a named signal in the specified emitter to all compatible slots in a specified receiver.
void connect_where_possible (const char *signal_name,
QObject *emitter,
QObject *receiver)
{
const auto *emitter_meta_object = emitter->metaObject();
/*
* Look for signal_name in the emitter's metadata.
*/
auto index_of_signal = emitter_meta_object->indexOfSignal(signal_name);
if (index_of_signal == -1)
return;
/*
* Get the signal's associated QMetaMethod.
*/
const auto signal_meta_method = emitter_meta_object->method(index_of_signal);
/*
* Now go through the receiver's methods. We could naively attempt to
* connect to each and every slot knowing that the Qt runtime will only
* succeed when the signal and slot are compatible. A nicer/cleaner
* implementation is to use the metadata available to check for
* compatibility _before_ attempting to connect -- if only to avoid unwanted
* warning messages on the console.
*/
const auto *receiver_meta_object = receiver->metaObject();
for (int method_index = 0; method_index < receiver_meta_object->methodCount(); ++method_index) {
const auto receiver_slot_method = receiver_meta_object->method(method_index);
if (receiver_slot_method.methodType() == QMetaMethod::Slot) {
/*
* Found a slot so check it's compatibility and, if ok, try to connect.
*/
if (are_compatible(signal_meta_method, receiver_slot_method)) {
QObject::connect(emitter, signal_meta_method, receiver, receiver_slot_method);
}
}
}
}
By way of an example try the following code...
class signaler: public QObject {
Q_OBJECT;
signals:
void sig1(int i, float f);
};
class receiver: public QObject {
Q_OBJECT;
public slots:
void slot1 (int i, float f) const
{
std::cerr << "\nreceiver::slot1(i = " << i << ", f = " << f << ")";
}
void slot2 (int i) const
{
std::cerr << "\nreceiver::slot2(i = " << i << ")";
}
void slot3 (float f) const
{
std::cerr << "\nreceiver::slot3(f = " << f << ")";
}
};
...
signaler signaler;
receiver receiver;
connect_where_possible(QMetaObject::normalizedSignature("sig1(int, float)"), &signaler, &receiver);
signaler.sig1(10, 20.0f);
I see the output...
receiver::slot1(i = 10, f = 20)
receiver::slot2(i = 10)
So, as expected, receiver::slot1
and receiver::slot2
are connected but receiver::slot3
isn't as it's deemed incompatible.