A few posts ago I described two methods of serializing C++ enums. Of these, method 2 serialized the Qt::Key enum. The approach, however, relied on some behind the scenes magic that I wasn't fully aware of nor did I fully document. This approach remedies that and describes in full the requirements for serializing C++ enums.
Method 2 - Reevaluated
The goal for this method is to take an enum called MyKey in the MyNS namespace and serialize it. In brief, the code for that looks like the following:
namespace MyNS
{
enum MyKey {
MyKey_Return = 0,
MyKey_Enter = 1
};
}
Since we want Qt to be able to serialize the above enum, it needs to know about the enum, so we're going to need Q_ENUMS(MyKey). But unless moc sees a Q_OBJECT or Q_GADGET macro, our Q_ENUMS macro will result in a compilation error. To get around this, we need to convince moc to process the file as if it were a class. We can do that by adding some preprocessor defines:
#ifndef Q_MOC_RUN
namespace MyNS
#else
class MyNS
#endif
{
#if defined(Q_MOC_RUN)
Q_GADGET
Q_ENUMS(MyKey)
public:
#endif
enum MyKey {
MyKey_Return = 0,
MyKey_Enter = 1
};
}
At this point we've convinced moc to look at and process the file, but that alone isn't enough. The code that moc generates assumes that a const staticMetaObject has been declared, but at this point one hasn't been declared. Although some compilers will let us get away with this, we'll declare it as follows:
// ... continuing at the enum
enum MyKey {
MyKey_Return = 0,
MyKey_Enter = 1
};
extern const QMetaObject staticMetaObject;
}
With that in place, we're ready to serialize the enum. The first step is to get a copy of the QMetaEnum object. We do so by accessing the static reference directly and then calling indexOfEnumerator to get the appropriate index:
// get the QMetaEnum object
const QMetaObject &mo = MyNS::staticMetaObject;
int enum_index = mo.indexOfEnumerator("MyKey");
QMetaEnum metaEnum = mo.enumerator(enum_index);
With the QMetaEnum instance in hand, we can now serialize the enum as demonstrated in my prior post:
// convert to a string
MyNS::MyKey key = MyNS::MyKey_Return;
QByteArray str = metaEnum.valueToKey(key);
qDebug() << "Value as str:" << str;
// convert from a string
int value = metaEnum.keyToValue("MyKey_Enter");
key = static_cast(value);
qDebug() << "key is MyKey_Enter? : " << (key == MyNS::MyKey_Enter);
With all the above in place, we've used a bit of magic to trick moc into thinking MyNS was a class. This causes moc to generate a MyNS::staticMetaObject instance and store the necessary serialization meta data. With everything in place, we get the following output:
Value as str: "MyKey_Return" key is MyKey_Enter? : true
References:
- "Secrets of Qt Full" developer days presentation
- src/tools/moc/generator.cpp:340
- src/corelib/global/qnamespace.h:53,80

That Q_MOC_RUN + Q_GADGET hack helped me a lot! Thanks for posting!
ReplyDeleteIt helped a lot..
ReplyDeleteThanks for the post :)
Dear Kaleb,
ReplyDeleteThank you for this solution.
However, exposing MyKey to QML using: qmlRegisterType("MyNS", 1, 0, "MyKey");
fails to compile due to the following error:
c:\qt\5.5\msvc2013_64\include\qtqml\qqml.h(244): error C2838: 'staticMetaObject' : illegal qualified name in member declaration
Any ideas on how to solve this?
ps. I declared the MyKey as 'enum class MyKey : quint16', but I guess this is irrelevant for above problem.
Thank you kindy in advance for your answer.
Now from Qt-5.5, there is some evoultion.
ReplyDeleteWe can use Q_NAMESPACE macro and Q_ENUM_NS inside the namespace:
namespace NS {
Q_NAMESPACE
enum class MyEnum : int { enum1, enum2, enum3 };
Q_ENUM_NS(MyEnum);
}
from there we can use QMetaEnum class.
And after enum class MyEnum : int {...}, we have to declare Q_ENUM_NS(myEnum);
Great and I have a swell offer you: Who To Contact For House Renovation house interior renovation
ReplyDelete