C++11 scoped enums with queued connections
-
[EDIT: Split from unrelated thread: http://qt-project.org/forums/viewthread/52056/ --JKSH]
Thanks all!!
one more thing I'm battling now:
I wanted to use c++-11 scoped enums "enum class <typename> {};" as the type of the parameter between signals and slots (enhancing readability and validating the arguments).
But when I switched the code (compiled and worked) from int parameters to scoped enum class parameters I got many obscure compile time errors from Qt internals about "no matching function call to qHash(const <my scoped enum type>&)".Following some web advice I tried adding "Q_DECLARE_METATYPE()" macros, but that didn't seem to work.
I guess this is a problem with a very elaborate macro/template/preprocessing framework - its difficult to comprehend compiler errors.....
Are scoped enums OK as signal/slot arguments?
If no - is there a similar Qt enum class which would be acceptable?I enjoy the depth of the reference manuals in "http://doc.qt.io/qt-5":http://doc.qt.io/qt-5/ , but is there a recommended book/web site with up to date (i.e. Qt-5.4 & C++-11/14) tutorials and best practice guides
thanks again
-
[quote author="hrs1" date="1421536494"]Following some web advice I tried adding “Q_DECLARE_METATYPE()” macros, but that didn’t seem to work.[/quote]The "Q_DECLARE_METATYPE() documentation":http://doc.qt.io/qt-5/qmetatype.html#Q_DECLARE_METATYPE says (emphasis added):
"Adding a Q_DECLARE_METATYPE() makes the type known to all template based functions, including QVariant. Note that if you intend to use the type in queued signal and slot connections or in QObject's property system, you also have to call qRegisterMetaType() since the names are resolved at runtime."
[quote author="hrs1" date="1421536494"]is there a recommended book/web site with up to date (i.e. Qt-5.4 & C++-11/14) tutorials and best practice guides[/quote]Currently, the only Qt 5 book out there (that I know of) is http://qmlbook.org/
-
qRegisterMetaType() doesn't seem to do the trick.
It seems that the signal/slot mechanism generates a qHash call with a const reference to my enum class type.
This (and other collections: QSet<enum type>) causes the compiler to complain about missing functions.could you try the following:
enum class EnumTestType {
TEST1,
TEST2,
TEST3
};.... Qt class definition....
signals:
void testSignal(EnumTestType);
public slots:
void testSlot(EnumTestType e); -
This worked just fine for me, on Qt 5.4.0 + MinGW 4.9.1:
@
// widget.h
#include <QWidget>
#include <QDebug>enum class EnumTestType {
TEST1,
TEST2,
TEST3
};
Q_DECLARE_METATYPE(EnumTestType)class Widget : public QWidget
{
Q_OBJECTsignals:
void testSignal(EnumTestType);public slots:
void testSlot(EnumTestType e)
{
qDebug() << static_cast<int>(e);
}public:
Widget(QWidget *parent = nullptr) : QWidget(parent)
{
connect(this, &Widget::testSignal,
this, &Widget::testSlot,
Qt::QueuedConnection);emit testSignal(EnumTestType::TEST2); }
};
@@
// main.cpp
#include "widget.h"
#include <QApplication>int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();return a.exec();
}
@In fact, I found out that the new Qt 5 connection syntax doesn't require qRegisterMetaType(), only Q_DECLARE_METATYPE(). If I got rid of Q_DECLARE_METATYPE(), the program still compiled without any errors. The only effect was that the connection failed at runtime.
Try:
Clean your project, run qmake, and build again
Post your code here
-
The example code works (though when I tried it on a Linux machine I got compile time errors on the "connect" line - but that is another issue...)
My problem was I tried to combine the enumeration in a QSet and send the set of values between the signal and slot. Actually declaring a class with a member QSet<EnumTestType>, adding a "Q_DECLARE_METATYPE" to the new class and using this class (containing the set collection of enums) as the shared parameter between the signal and the slot.
I tried various options, but all caused a compile time error:
C:\Qt\5.4\mingw491_32\include\QtCore\qhash.h:-1: In instantiation of 'uint qHash(const T&, uint) [with T = EnumTestType; uint = unsigned int]':Sending a QSet of int does work.
Is it possible to use QSet<enum class> as a parameter to a signal (either directly or within a class)?
If not, what would be the recommended way to send such a collection?
I could add helper function to convert QSet<enum class> to QSet<int> before sending the signal and converting back to QSet<enum class> after receiving data in the slot - but that seems like unnecessary overhead (both syntactic and performance wise)
-
QSet<EnumTestType> is a completely separate type from EnumTestType. If you want to send the QSet in a signal, do
@
Q_DECLARE_METATYPE(QSet<EnumTestType>)
@P.S. I recommend posting code instead of text descriptions. It's easier to read that way :)
-
I did declare the type.
here's the code:enumt.h:
@#ifndef ENUMT_H
#define ENUMT_H#include <QDebug>
#include <QSet>
enum class EnumTestType {
NONE,
TEST1,
TEST2,
TEST3,
COUNT
};
Q_DECLARE_METATYPE(EnumTestType)class EnumSet {
public:
EnumSet() {}
EnumSet(const EnumSet& es) {enumSet = es.enumSet;}
public:
QSet<EnumTestType> enumSet;
};
Q_DECLARE_METATYPE(EnumSet)
Q_DECLARE_METATYPE(QSet<EnumTestType>)QDebug operator<< (QDebug d, const EnumTestType &e);
QDebug operator<< (QDebug d, const EnumSet &es);#endif // ENUMT_H@
enumt.cpp:
@#include "enumt.h"QDebug operator<< (QDebug d, const EnumTestType &e) {
d << static_cast<int>(e);
return d;
}QDebug operator<< (QDebug d, const EnumSet &es) {
d << "[ ";
for (auto e : es.enumSet) {
d << e << " ";
}
d << "]";
return d;
}
@widget.h:
@#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QWidget>
#include <QDebug>#include "enumt.h"
class Widget : public QWidget
{
Q_OBJECTsignals:
void testSignal(EnumSet);public slots:
void testSlot(EnumSet eSet);public:
explicit Widget(QWidget *parent = nullptr);
};#endif // WIDGET_H
@widget.cpp:
@#include "widget.h"Widget::Widget(QWidget *parent) : QWidget(parent) {
connect(this, &Widget::testSignal,
this, &Widget::testSlot,
Qt::QueuedConnection);
EnumSet es;
es.enumSet += EnumTestType::TEST1;
es.enumSet += EnumTestType::TEST3;
es.enumSet += EnumTestType::TEST1;
emit testSignal(es);
}void Widget::testSlot(EnumSet eSet) {
qDebug() << eSet;
}
@main.cpp:
@#include "widget.h"
#include <QApplication>int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();return a.exec();
}@
enum.pro:
@#-------------------------------------------------Project created by QtCreator 2015-01-18T22:27:06
#-------------------------------------------------
QT += core gui
CONFIG += c++14
greaterThan(QT_MAJOR_VERSION, 4): QT += widgetsTARGET = enum
TEMPLATE = appSOURCES += main.cpp
widget.cpp
enumt.cppHEADERS += widget.h
enumt.hFORMS += widget.ui
@ -
Signals and slots are completely irrelevant here.
I just realized that you can't store a scoped enum in a QSet (or std::set) by default, just like how you can't store arbitrary classes. From the "documentation":http://doc.qt.io/qt-5/qset.html#details
"...there must also be a global qHash() function that returns a hash value for an argument of the key's type."
You need to overload qHash(), just like how you overloaded QDebug::operator<<().
By the way, defining EnumSet as a class is unnecessary work. If you want a short name, just make a typedef:
@typedef QSet<EnumTestType> EnumSet@