Template Q_GADGET with Q_ENUM
-
Hello everyone!
I'm having deal with the enums that should be accessible in QML, so I create one class for a one enum in the following way:
class DisplayInterfaceType { Q_GADGET public: enum Value { UNDEFINED = -1, DIGITAL, ANALOG }; Q_ENUM(Value); static void registerQML() { qmlRegisterUncreatableType<DisplayInterfaceType>("App.Enums", 1, 0, "DisplayInterfaceType", "DisplayInterfaceType"); qRegisterMetaType<DisplayInterfaceType::Value>("DisplayInterfaceType::Value"); } static DisplayInterfaceType::Value fromInt(int value) { QMetaEnum metaEnum = QMetaEnum::fromType<DisplayInterfaceType::Value>(); const char* ptr = metaEnum.valueToKey(value); if (ptr != nullptr) { bool result = false; metaEnum.keyToValue(ptr, &result); if (result) { return static_cast<DisplayInterfaceType::Value>(value); } } return DisplayInterfaceType::Value::UNDEFINED; } };
But now I have a lot of enums created in this way. So every time I need to create one more class, copy already implemented, change class name, change strings in registration method and add necessary enums. Can this process be simplified? I thought about a template inheritance, but Q_GADGET cannot be derived. So my question is: can adding these enums be simplified?
-
Hello everyone!
I'm having deal with the enums that should be accessible in QML, so I create one class for a one enum in the following way:
class DisplayInterfaceType { Q_GADGET public: enum Value { UNDEFINED = -1, DIGITAL, ANALOG }; Q_ENUM(Value); static void registerQML() { qmlRegisterUncreatableType<DisplayInterfaceType>("App.Enums", 1, 0, "DisplayInterfaceType", "DisplayInterfaceType"); qRegisterMetaType<DisplayInterfaceType::Value>("DisplayInterfaceType::Value"); } static DisplayInterfaceType::Value fromInt(int value) { QMetaEnum metaEnum = QMetaEnum::fromType<DisplayInterfaceType::Value>(); const char* ptr = metaEnum.valueToKey(value); if (ptr != nullptr) { bool result = false; metaEnum.keyToValue(ptr, &result); if (result) { return static_cast<DisplayInterfaceType::Value>(value); } } return DisplayInterfaceType::Value::UNDEFINED; } };
But now I have a lot of enums created in this way. So every time I need to create one more class, copy already implemented, change class name, change strings in registration method and add necessary enums. Can this process be simplified? I thought about a template inheritance, but Q_GADGET cannot be derived. So my question is: can adding these enums be simplified?
Seems to me what you need is actually a macro:
#define REGISTERED_ENUM(ClassName, EnumName) \ Q_ENUM(EnumName); \ static void registerQML() { \ qmlRegisterUncreatableType<ClassName>("App.Enums", 1, 0, #ClassName, #ClassName); \ qRegisterMetaType<ClassName::EnumName>(#ClassName"::"#EnumName); \ }
class DisplayInterfaceType { Q_GADGET public: enum Value { UNDEFINED = -1, DIGITAL, ANALOG }; REGISTERED_ENUM(DisplayInterfaceType,Value); static DisplayInterfaceType::Value fromInt(int value) { QMetaEnum metaEnum = QMetaEnum::fromType<DisplayInterfaceType::Value>(); const char* ptr = metaEnum.valueToKey(value); if (ptr != nullptr) { bool result = false; metaEnum.keyToValue(ptr, &result); if (result) { return static_cast<DisplayInterfaceType::Value>(value); } } return DisplayInterfaceType::Value::UNDEFINED; } };
@St-Stanislav said in Template Q_GADGET with Q_ENUM:
Q_GADGET cannot be derived.
I don't think this is correct.
-
Hello everyone!
I'm having deal with the enums that should be accessible in QML, so I create one class for a one enum in the following way:
class DisplayInterfaceType { Q_GADGET public: enum Value { UNDEFINED = -1, DIGITAL, ANALOG }; Q_ENUM(Value); static void registerQML() { qmlRegisterUncreatableType<DisplayInterfaceType>("App.Enums", 1, 0, "DisplayInterfaceType", "DisplayInterfaceType"); qRegisterMetaType<DisplayInterfaceType::Value>("DisplayInterfaceType::Value"); } static DisplayInterfaceType::Value fromInt(int value) { QMetaEnum metaEnum = QMetaEnum::fromType<DisplayInterfaceType::Value>(); const char* ptr = metaEnum.valueToKey(value); if (ptr != nullptr) { bool result = false; metaEnum.keyToValue(ptr, &result); if (result) { return static_cast<DisplayInterfaceType::Value>(value); } } return DisplayInterfaceType::Value::UNDEFINED; } };
But now I have a lot of enums created in this way. So every time I need to create one more class, copy already implemented, change class name, change strings in registration method and add necessary enums. Can this process be simplified? I thought about a template inheritance, but Q_GADGET cannot be derived. So my question is: can adding these enums be simplified?
@St-Stanislav said in Template Q_GADGET with Q_ENUM:
But now I have a lot of enums created in this way. So every time I need to create one more class, copy already implemented, change class name, change strings in registration method and add necessary enums. Can this process be simplified? I thought about a template inheritance, but Q_GADGET cannot be derived. So my question is: can adding these enums be simplified?
You could create on class who centralize all enums.
Then you only have to register this one. -
Hello everyone!
I'm having deal with the enums that should be accessible in QML, so I create one class for a one enum in the following way:
class DisplayInterfaceType { Q_GADGET public: enum Value { UNDEFINED = -1, DIGITAL, ANALOG }; Q_ENUM(Value); static void registerQML() { qmlRegisterUncreatableType<DisplayInterfaceType>("App.Enums", 1, 0, "DisplayInterfaceType", "DisplayInterfaceType"); qRegisterMetaType<DisplayInterfaceType::Value>("DisplayInterfaceType::Value"); } static DisplayInterfaceType::Value fromInt(int value) { QMetaEnum metaEnum = QMetaEnum::fromType<DisplayInterfaceType::Value>(); const char* ptr = metaEnum.valueToKey(value); if (ptr != nullptr) { bool result = false; metaEnum.keyToValue(ptr, &result); if (result) { return static_cast<DisplayInterfaceType::Value>(value); } } return DisplayInterfaceType::Value::UNDEFINED; } };
But now I have a lot of enums created in this way. So every time I need to create one more class, copy already implemented, change class name, change strings in registration method and add necessary enums. Can this process be simplified? I thought about a template inheritance, but Q_GADGET cannot be derived. So my question is: can adding these enums be simplified?
@St-Stanislav
you can place the enums in a namespace and register the namespace in QML:
https://doc.qt.io/qt-5/qqmlengine.html#qmlRegisterUncreatableMetaObject -
@St-Stanislav
you can place the enums in a namespace and register the namespace in QML:
https://doc.qt.io/qt-5/qqmlengine.html#qmlRegisterUncreatableMetaObject@raven-worx Thank you for the answer! Can the conversion method (fromInt in my example) be implemented within this approach? I mean it looks like I cannot implement one conversion method to serve every enum in the defined namespace. Moreover, can this approach be used for registering every enum as a meta type? I have read somewhere that Q_ENUM and Q_ENUMS (maybe Q_ENUM_NS as well) should register it as a meta type. But I remember that I have faced some cases when I needed to implement it directly, because there were some errors (don't remember details, though).
-
@raven-worx Thank you for the answer! Can the conversion method (fromInt in my example) be implemented within this approach? I mean it looks like I cannot implement one conversion method to serve every enum in the defined namespace. Moreover, can this approach be used for registering every enum as a meta type? I have read somewhere that Q_ENUM and Q_ENUMS (maybe Q_ENUM_NS as well) should register it as a meta type. But I remember that I have faced some cases when I needed to implement it directly, because there were some errors (don't remember details, though).
@St-Stanislav
see https://doc.qt.io/qt-5/qobject.html#Q_NAMESPACE
For slots / invokable methods you need a QObject -
@St-Stanislav
see https://doc.qt.io/qt-5/qobject.html#Q_NAMESPACE
For slots / invokable methods you need a QObject@raven-worx Yes, I know about QObject, but can not get, how can it help in this case.
Anyway, I have found some kind of a workaround with making one base Q_GADGET class with Q_ENUM and template method to register every derived class.
-
Hello everyone!
I'm having deal with the enums that should be accessible in QML, so I create one class for a one enum in the following way:
class DisplayInterfaceType { Q_GADGET public: enum Value { UNDEFINED = -1, DIGITAL, ANALOG }; Q_ENUM(Value); static void registerQML() { qmlRegisterUncreatableType<DisplayInterfaceType>("App.Enums", 1, 0, "DisplayInterfaceType", "DisplayInterfaceType"); qRegisterMetaType<DisplayInterfaceType::Value>("DisplayInterfaceType::Value"); } static DisplayInterfaceType::Value fromInt(int value) { QMetaEnum metaEnum = QMetaEnum::fromType<DisplayInterfaceType::Value>(); const char* ptr = metaEnum.valueToKey(value); if (ptr != nullptr) { bool result = false; metaEnum.keyToValue(ptr, &result); if (result) { return static_cast<DisplayInterfaceType::Value>(value); } } return DisplayInterfaceType::Value::UNDEFINED; } };
But now I have a lot of enums created in this way. So every time I need to create one more class, copy already implemented, change class name, change strings in registration method and add necessary enums. Can this process be simplified? I thought about a template inheritance, but Q_GADGET cannot be derived. So my question is: can adding these enums be simplified?
Seems to me what you need is actually a macro:
#define REGISTERED_ENUM(ClassName, EnumName) \ Q_ENUM(EnumName); \ static void registerQML() { \ qmlRegisterUncreatableType<ClassName>("App.Enums", 1, 0, #ClassName, #ClassName); \ qRegisterMetaType<ClassName::EnumName>(#ClassName"::"#EnumName); \ }
class DisplayInterfaceType { Q_GADGET public: enum Value { UNDEFINED = -1, DIGITAL, ANALOG }; REGISTERED_ENUM(DisplayInterfaceType,Value); static DisplayInterfaceType::Value fromInt(int value) { QMetaEnum metaEnum = QMetaEnum::fromType<DisplayInterfaceType::Value>(); const char* ptr = metaEnum.valueToKey(value); if (ptr != nullptr) { bool result = false; metaEnum.keyToValue(ptr, &result); if (result) { return static_cast<DisplayInterfaceType::Value>(value); } } return DisplayInterfaceType::Value::UNDEFINED; } };
@St-Stanislav said in Template Q_GADGET with Q_ENUM:
Q_GADGET cannot be derived.
I don't think this is correct.
-
Seems to me what you need is actually a macro:
#define REGISTERED_ENUM(ClassName, EnumName) \ Q_ENUM(EnumName); \ static void registerQML() { \ qmlRegisterUncreatableType<ClassName>("App.Enums", 1, 0, #ClassName, #ClassName); \ qRegisterMetaType<ClassName::EnumName>(#ClassName"::"#EnumName); \ }
class DisplayInterfaceType { Q_GADGET public: enum Value { UNDEFINED = -1, DIGITAL, ANALOG }; REGISTERED_ENUM(DisplayInterfaceType,Value); static DisplayInterfaceType::Value fromInt(int value) { QMetaEnum metaEnum = QMetaEnum::fromType<DisplayInterfaceType::Value>(); const char* ptr = metaEnum.valueToKey(value); if (ptr != nullptr) { bool result = false; metaEnum.keyToValue(ptr, &result); if (result) { return static_cast<DisplayInterfaceType::Value>(value); } } return DisplayInterfaceType::Value::UNDEFINED; } };
@St-Stanislav said in Template Q_GADGET with Q_ENUM:
Q_GADGET cannot be derived.
I don't think this is correct.
@VRonin Thank you for your MACROS version, I thought about it, but firstly have tried templates. I will check your suggestion, thanks again. My template solution is:
template<typename T> static void registerQML() { QString typeName = QString(typeid (T).name()).split(" ").last(); qmlRegisterUncreatableType<T>("App.Enums", 1, 0, typeName.toLocal8Bit().data(), typeName); qRegisterMetaType<T::Value>(QString("%1::Value").arg(typeName).toLocal8Bit().data()); } template<typename T> static auto fromInt(int value) { QMetaEnum metaEnum = QMetaEnum::fromType<T::Value>(); const char* ptr = metaEnum.valueToKey(value); if (ptr != nullptr) { bool result = false; metaEnum.keyToValue(ptr, &result); if (result) { return static_cast<typename T::Value>(value); } } return T::UNDEFINED; }
@VRonin said in Template Q_GADGET with Q_ENUM:
I don't think this is correct.
Yes, I found my typo, I meant "templated" :)
-
@VRonin Thank you for your MACROS version, I thought about it, but firstly have tried templates. I will check your suggestion, thanks again. My template solution is:
template<typename T> static void registerQML() { QString typeName = QString(typeid (T).name()).split(" ").last(); qmlRegisterUncreatableType<T>("App.Enums", 1, 0, typeName.toLocal8Bit().data(), typeName); qRegisterMetaType<T::Value>(QString("%1::Value").arg(typeName).toLocal8Bit().data()); } template<typename T> static auto fromInt(int value) { QMetaEnum metaEnum = QMetaEnum::fromType<T::Value>(); const char* ptr = metaEnum.valueToKey(value); if (ptr != nullptr) { bool result = false; metaEnum.keyToValue(ptr, &result); if (result) { return static_cast<typename T::Value>(value); } } return T::UNDEFINED; }
@VRonin said in Template Q_GADGET with Q_ENUM:
I don't think this is correct.
Yes, I found my typo, I meant "templated" :)
@St-Stanislav said in Template Q_GADGET with Q_ENUM:
Yes, I found my typo, I meant "templated" :)
There is a workaround for this: https://github.com/woboq/verdigris but I don't think it's worth it in your case