Consider the following class definition:
// exported.hpp
#include <QObject>
class Exported: public QObject {
Q_OBJECT
public:
using QObject::QObject;
enum class FOO { BAR };
Q_ENUM(FOO)
};
And the following main
file:
// main.cpp
#include <QApplication>
#include <QQmlApplicationEngine>
#include "exported.hpp"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QQmlApplicationEngine engine;
qmlRegisterType<Exported>("Package", 1, 0, "Exported");
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
return app.exec();
}
By doing this, I can easily access the named constants of my enum in QML.
As an example:
// main.qml
import QtQuick 2.7
import QtQuick.Controls 2.0
import Package 1.0
ApplicationWindow {
Rectangle {
Component.onCompleted: {
console.log(Exported.BAR)
}
}
}
This works as long as the declaration of the enum is enclosed in the class.
As an example, if I change the class definition as shown below, it doesn't work anymore:
// exported.hpp
#include <QObject>
enum class FOO { BAR };
class Exported: public QObject {
Q_OBJECT
public:
using QObject::QObject;
using FOO = ::FOO;
Q_ENUM(FOO)
};
Now, Exported.BAR
in the QML file is undefined
.
The most basic question is: why it doesn't work with using declarations?
Note that it works with forwarded declarations, as an example:
// exported.hpp
#include <QObject>
enum class FOO { BAR };
class Exported: public QObject {
Q_OBJECT
public:
using QObject::QObject;
enum class FOO;
Q_ENUM(FOO)
enum class FOO { BAR };
};
This is true to the documentation of Q_ENUM
actually (emphasis mine):
This macro registers an enum type with the meta-object system. It must be placed after the enum declaration in a class that has the Q_OBJECT or the Q_GADGET macro.
No mentions of a definition in the whole section.
On the other side, we have this from the standard:
A using-declaration introduces a set of declarations into the declarative region in which the using-declaration appears.
So, I was expecting it to work as well. Anyway, this was maybe a wrong expectation on my side.
That said, any suggestion on how to deal with such an inconvenient?
The only way I can see to work around it in case the enum is defined out of the class is to define another enum within the class and have a one-to-one mapping between them.
It's far from being maintainable and a bit tedious indeed.
This has nothing to do with standard C++, at least not directly. Qt's moc is quite limited in its understanding of C++ syntax rules (and with good reason1). Apparently this way of importing enums into class scopes is outside of it's abilities.
In my code, when I want moc to generate enum <-> string conversions for me, I go with the alias, but in the opposite direction:
class Exported: public QObject {
Q_OBJECT
public:
using QObject::QObject;
enum class FOO { BAR };
Q_ENUM(FOO)
};
using FOO = Exported::Foo;
This keeps moc happy and is valid C++. The downside is that you pull up the definition of Exported
into every scope you use FOO
's definition in, and you can't forward-declare FOO
.
1 it was created before libclang was a thing and the implementation cost of a full C++ parser for a few corner cases would be extremely uneconomical.