Is it possible to mimic the functionality of an array of signals?
-
@JeKK666 Why don't you use a mapping between key and method pointer? Then you just need to change this mapping if you want to remap the keys. Something like:
enum class Keys { TopLeft, TopRight, BottomLeft, BottomRight } QMap<Keys, HERE_METHOD_POINTER> mapping; connect (this, &MyClass::keypadTopLeft, [this]() { mapping[Keys::TopLeft](); }); ... connect (this, &MyClass::keypadBottomRight, [this]() { mapping[Keys::BottomRight](); });
-
@JeKK666 Why don't you use a mapping between key and method pointer? Then you just need to change this mapping if you want to remap the keys. Something like:
enum class Keys { TopLeft, TopRight, BottomLeft, BottomRight } QMap<Keys, HERE_METHOD_POINTER> mapping; connect (this, &MyClass::keypadTopLeft, [this]() { mapping[Keys::TopLeft](); }); ... connect (this, &MyClass::keypadBottomRight, [this]() { mapping[Keys::BottomRight](); });
wrote on 29 Jun 2021, 16:26 last edited by@jsulm It would seem i am not able to declare a QMap which uses a pointer to a method as the T value, as in QMap<Key, T>.
I also have troubles reading your code:
- i don't understand what results putting curly braces within connect() is going to produce;
- i don't get what the square brackets around [this] mean;
- i fail to grasp this overload of connect(), which is not the standard
connect(senderObject, SIGNAL(sig()), receiverObject, SLOT(on_sig()))
Could you elaborate more?
-
@jsulm It would seem i am not able to declare a QMap which uses a pointer to a method as the T value, as in QMap<Key, T>.
I also have troubles reading your code:
- i don't understand what results putting curly braces within connect() is going to produce;
- i don't get what the square brackets around [this] mean;
- i fail to grasp this overload of connect(), which is not the standard
connect(senderObject, SIGNAL(sig()), receiverObject, SLOT(on_sig()))
Could you elaborate more?
wrote on 29 Jun 2021, 18:55 last edited by JonB@JeKK666
All your questions are covered by New Signal Slot Syntax. Change over from oldSIGNAL
/SLOT()
macros, and learn what a C++ lambda is (search that page for examples). -
@jsulm It would seem i am not able to declare a QMap which uses a pointer to a method as the T value, as in QMap<Key, T>.
I also have troubles reading your code:
- i don't understand what results putting curly braces within connect() is going to produce;
- i don't get what the square brackets around [this] mean;
- i fail to grasp this overload of connect(), which is not the standard
connect(senderObject, SIGNAL(sig()), receiverObject, SLOT(on_sig()))
Could you elaborate more?
@JeKK666 said in Is it possible to mimic the functionality of an array of signals?:
i don't understand what results putting curly braces within connect() is going to produce;
i don't get what the square brackets around [this] mean;Please read about lambdas in C++.
enum class Keys { TopLeft, TopRight, BottomLeft, BottomRight } typedef void (MyClass::*MethodPointer)(); QMap<Keys, MethodPointer> mapping; // I assume mapping is class member connect (this, &MyClass::keypadTopLeft, [this]() { (this->*mapping[Keys::TopLeft])(); }); ... connect (this, &MyClass::keypadBottomRight, [this]() { (this->*mapping[Keys::BottomRight])(); });
-
wrote on 30 Jun 2021, 08:33 last edited by
@JonB I wanted to retain the signal/slot model, since conceptually i connect the keypad to different Widgets, and each widget has a dedicated slot for the keypad signal to be connected to it; also, it is how this old software, tried and true, born with Qt4.8 is structured, so since it technically ain't broke, i am not going to fix it :P (code was subsequently ported to Qt 5.3.2, but never rewritten to use newer constructs).
@jsulm I always had trouble grasping how lambdas work, therefore i never used them and a can't recognize them at a glance; i followed your suggestions, and smashed my head on it a little bit longer, i think i now have a better understading.
The QMap was still giving me troubles until i read @jsulm's first answer, as i was not able to figure out the syntax i had to use for as a member function pointer.
It did, however, provide me a suggestion on how to manually implement a similar strategy, resorting to a more basic approach: since a matrix of signals is not available, i created a matrix of function pointers calledkeypadMapper
, where each function is a stupid wrapper for a call toemit signal();
.This gives me the code readability that i want while also avoiding code duplication for the connect-disconnect section in the main program:
in the keypad class, after decoding the button, i will call(this->*keypadMapper[TOP_LEFT][KP_MAP_TYPE])();
It might not be the most elegant solution, but it fits into the rest of the codebase with minimal effort on the rest of the classes, and to me also looks quite easy to read, which is a plus for maintainability.
Thanks!
-
@JonB I wanted to retain the signal/slot model, since conceptually i connect the keypad to different Widgets, and each widget has a dedicated slot for the keypad signal to be connected to it; also, it is how this old software, tried and true, born with Qt4.8 is structured, so since it technically ain't broke, i am not going to fix it :P (code was subsequently ported to Qt 5.3.2, but never rewritten to use newer constructs).
@jsulm I always had trouble grasping how lambdas work, therefore i never used them and a can't recognize them at a glance; i followed your suggestions, and smashed my head on it a little bit longer, i think i now have a better understading.
The QMap was still giving me troubles until i read @jsulm's first answer, as i was not able to figure out the syntax i had to use for as a member function pointer.
It did, however, provide me a suggestion on how to manually implement a similar strategy, resorting to a more basic approach: since a matrix of signals is not available, i created a matrix of function pointers calledkeypadMapper
, where each function is a stupid wrapper for a call toemit signal();
.This gives me the code readability that i want while also avoiding code duplication for the connect-disconnect section in the main program:
in the keypad class, after decoding the button, i will call(this->*keypadMapper[TOP_LEFT][KP_MAP_TYPE])();
It might not be the most elegant solution, but it fits into the rest of the codebase with minimal effort on the rest of the classes, and to me also looks quite easy to read, which is a plus for maintainability.
Thanks!
wrote on 30 Jun 2021, 08:53 last edited by@JeKK666 said in Is it possible to mimic the functionality of an array of signals?:
where each function is a stupid wrapper for a call to emit signal();.
You don't need that.
emit
is meaningless to the compiler, it's just for readability. Just create a pointer to the signal directly. A signal is not magic, it's a public member. The only special feature is that it's body is not written by you but by moc -
@JeKK666 said in Is it possible to mimic the functionality of an array of signals?:
where each function is a stupid wrapper for a call to emit signal();.
You don't need that.
emit
is meaningless to the compiler, it's just for readability. Just create a pointer to the signal directly. A signal is not magic, it's a public member. The only special feature is that it's body is not written by you but by mocwrote on 30 Jun 2021, 09:36 last edited by@VRonin I tried that several times over already (inspired from this): i based my assumptions on the fact that the declaration of a signal is syntactically identical to the declaration of a function prototype, save for the
signals:
keyword prepended to it, so i proceded to try and use a function pointer when passing arguments to the connect().Alas, i was never able to guess the correct syntax which would allow the darn thing to compile, i remember getting compile errors on the connect(), but could not reproduce now.
Also, that would still require me to write switch-case connects based on the currently selected keypad map, or at least switch-case assignment of the signal pointer, since when iemit
the signal, it still wouldn't accept array indexing (even more: decltype<...> threw at me all sorts of error, and i would also need a pointer for each signal) -
QMetaObject for the rescue:
#include <array> class SomeClass : public QObject { Q_OBJECT std::array<const char*, 3> arrayOfSignals{"signal1", "signal2", "signal3"}; std::array<const char*, 3> arrayOfSlots{"slot1", "slot2", "slot3"}; public: SomeClass(QObject *parent = nullptr) : QObject(parent) { QObject::connect(this, &SomeClass::signal1, this, &SomeClass::slot1); QObject::connect(this, &SomeClass::signal2, this, &SomeClass::slot2); QObject::connect(this, &SomeClass::signal3, this, &SomeClass::slot3); qDebug() << "Call slots via signal array"; for(auto entry : arrayOfSignals){ QMetaObject::invokeMethod(this, entry); } qDebug() << "Call Slots vis slot array"; for(auto entry : arrayOfSlots){ QMetaObject::invokeMethod(this, entry); } } signals: void signal1(); void signal2(); void signal3(); public slots: void slot1(){qDebug() << Q_FUNC_INFO;} void slot2(){qDebug() << Q_FUNC_INFO;} void slot3(){qDebug() << Q_FUNC_INFO;} };
does this do, what you want it to?
-
QMetaObject for the rescue:
#include <array> class SomeClass : public QObject { Q_OBJECT std::array<const char*, 3> arrayOfSignals{"signal1", "signal2", "signal3"}; std::array<const char*, 3> arrayOfSlots{"slot1", "slot2", "slot3"}; public: SomeClass(QObject *parent = nullptr) : QObject(parent) { QObject::connect(this, &SomeClass::signal1, this, &SomeClass::slot1); QObject::connect(this, &SomeClass::signal2, this, &SomeClass::slot2); QObject::connect(this, &SomeClass::signal3, this, &SomeClass::slot3); qDebug() << "Call slots via signal array"; for(auto entry : arrayOfSignals){ QMetaObject::invokeMethod(this, entry); } qDebug() << "Call Slots vis slot array"; for(auto entry : arrayOfSlots){ QMetaObject::invokeMethod(this, entry); } } signals: void signal1(); void signal2(); void signal3(); public slots: void slot1(){qDebug() << Q_FUNC_INFO;} void slot2(){qDebug() << Q_FUNC_INFO;} void slot3(){qDebug() << Q_FUNC_INFO;} };
does this do, what you want it to?
wrote on 5 Jul 2021, 07:32 last edited by JeKK666 7 May 2021, 07:46@J-Hilk Yes! I had also tried using the
QMetaObject::invokeMethod()
, but never got around to make it work: no compile time errors, just no execution of the slot whatsoever, and was never able to narrow it down to either signal emission or sig/slot connection...Anyhow, call me dumb but i find the syntax for this overall thing a lot more convoluted than a matrix of function pointers >.<'
Still, a much more elegant solution nonetheless, that's great.Thanks!
-
@J-Hilk Yes! I had also tried using the
QMetaObject::invokeMethod()
, but never got around to make it work: no compile time errors, just no execution of the slot whatsoever, and was never able to narrow it down to either signal emission or sig/slot connection...Anyhow, call me dumb but i find the syntax for this overall thing a lot more convoluted than a matrix of function pointers >.<'
Still, a much more elegant solution nonetheless, that's great.Thanks!
@JeKK666 said in Is it possible to mimic the functionality of an array of signals?:
Anyhow, call me dumb but i find the syntax for this overall thing a lot more convoluted than a matrix of function pointers >.<'
I would say, thats a debatable point :D
#include <array> class SomeClass : public QObject { typedef void (SomeClass::*SomeClassFunction)(); Q_OBJECT // std::array<const char*, 3> arrayOfSignals{"signal1", "signal2", "signal3"}; // std::array<const char*, 3> arrayOfSlots{"slot1", "slot2", "slot3"}; std::array<SomeClassFunction,3> arrayOfSignalsPointers{&SomeClass::signal1,&SomeClass::signal2, &SomeClass::signal3}; std::array<SomeClassFunction, 3> arrayOfSlotsPointers{&SomeClass::slot1, &SomeClass::slot2, &SomeClass::slot3}; public: SomeClass(QObject *parent = nullptr) : QObject(parent) { QObject::connect(this, &SomeClass::signal1, this, &SomeClass::slot1); QObject::connect(this, &SomeClass::signal2, this, &SomeClass::slot2); QObject::connect(this, &SomeClass::signal3, this, &SomeClass::slot3); // qDebug() << "Call slots via signal array"; // for(auto entry : arrayOfSignals){ // QMetaObject::invokeMethod(this, entry); // } // qDebug() << "Call Slots vis slot array"; // for(auto entry : arrayOfSlots){ // QMetaObject::invokeMethod(this, entry); // } //---- for(auto entry : arrayOfSignalsPointers){ (this->*entry)(); } for(auto entry : arrayOfSlotsPointers){ (this->*entry)(); } } signals: void signal1(); void signal2(); void signal3(); public slots: void slot1(){qDebug() << Q_FUNC_INFO;} void slot2(){qDebug() << Q_FUNC_INFO;} void slot3(){qDebug() << Q_FUNC_INFO;} };
12/12