Details
-
Suggestion
-
Resolution: Unresolved
-
Not Evaluated
-
None
-
None
-
None
Description
Consider the following code:
myclass.h
#include <QObject> class MyClass : public QObject { Q_OBJECT public: enum LoadHint {A=1, B=2, C=4, D=8}; Q_DECLARE_FLAGS(LoadHints, LoadHint) Q_FLAG(LoadHints) enum Alignment {Left, Right}; Q_ENUM(Alignment) };
main.cpp
#include <QVariant> #include <QDebug> #include "myclass.h" int main(int, char *[]) { MyClass::Alignment al = MyClass::Right; QVariant alv = QVariant::fromValue(al); //Store Q_ENUM in a QVariant qDebug() << alv << alv.toInt() << alv.toString(); //Output: QVariant(MyClass::Alignment, "Right") 1 "Right" MyClass::LoadHints lhs(MyClass::A | MyClass::C); QVariant lhsv = QVariant::fromValue(lhs); //Store Q_FLAG in a QVariant qDebug() << lhsv << lhsv.toInt() << lhsv.toString(); //Output: QVariant(MyClass::LoadHints, ) 0 "" //Expected Output: QVariant(MyClass::LoadHints, "A|C") 5 "A|C" }
Receiving a flag-enumeration (e.g. QFlags<MyClass::LoadHints>) from a QVariant is currently cumbersome. It can't be converted to string and it also can't be converted to any integral type.
The main reason behind this is that
QMetaType::typeFlags(qMetaTypeId<MyClass::LoadHints>()).testFlag(QMetaType::IsEnumeration);
returns false.
I suggest to set the QMetaType::IsEnumeration bit on the QMetaType flags of the QFlags<T> Types and adjust qmetatype.h as follows (diff):
template<typename T>
struct QMetaTypeTypeFlags
{
enum { Flags = (QTypeInfoQuery<T>::isRelocatable ? QMetaType::MovableType : 0)
| (QTypeInfo<T>::isComplex ? QMetaType::NeedsConstruction : 0)
| (QTypeInfo<T>::isComplex ? QMetaType::NeedsDestruction : 0)
| (IsPointerToTypeDerivedFromQObject<T>::Value ? QMetaType::PointerToQObject : 0)
| (IsSharedPointerToTypeDerivedFromQObject<T>::Value ? QMetaType::SharedPointerToQObject : 0)
| (IsWeakPointerToTypeDerivedFromQObject<T>::Value ? QMetaType::WeakPointerToQObject : 0)
| (IsTrackingPointerToTypeDerivedFromQObject<T>::Value ? QMetaType::TrackingPointerToQObject : 0)
- | (std::is_enum<T>::value ? QMetaType::IsEnumeration : 0)
+ | ((std::is_enum<T>::value || QtPrivate::IsQEnumHelper<T>::Value) ? QMetaType::IsEnumeration : 0)
| (IsGadgetHelper<T>::IsGadgetOrDerivedFrom ? QMetaType::IsGadget : 0)
| (IsPointerToGadgetHelper<T>::IsGadgetOrDerivedFrom ? QMetaType::PointerToGadget : 0)
};
};
This would also harmonize the QMetaType::IsEnumeration usage.
The QMetaTypeIdQObject template already uses QtPrivate::IsQEnumHelper<T>::Value to set QMetaType::IsEnumeration.
template <typename T, int = QtPrivate::IsPointerToTypeDerivedFromQObject<T>::Value ? QMetaType::PointerToQObject : QtPrivate::IsGadgetHelper<T>::IsRealGadget ? QMetaType::IsGadget : QtPrivate::IsPointerToGadgetHelper<T>::IsRealGadget ? QMetaType::PointerToGadget : QtPrivate::IsQEnumHelper<T>::Value ? QMetaType::IsEnumeration : 0> struct QMetaTypeIdQObject { enum { Defined = 0 }; };
But the QMetaType flag QMetaType::IsEnumeration is only set if std::is_enum<T>::value is true.
Of course it makes sense to have QMetaType::IsEnumeration set for plain enums that are not part of a QObject class.
But QFlags are just better enums, so why should QMetaType::IsEnumeration not also be set on a QFlags class?
Benefits of this change:
- QVariant(QFlags<T>).toString() can be easily implemented, without hacks to detect if the payload is a QFlags<T> type
- Q_FLAG behaves more like Q_ENUM. Both have a assigned meta object, both can be converted to string and int using QVariant (new), QMetaEnum (existing) or QDebug (existing)
- We don't need reinterpret_cast hacks to receive an int from a QVariant(QFlags<T>) like here in qtwebchannel: https://codereview.qt-project.org/gitweb?p=qt/qtwebchannel.git;a=blobdiff;f=src/webchannel/qmetaobjectpublisher.cpp;h=6ea348028e7314a4cdd5c161d389032c5ee1219f;hp=9e08453a2651348f35382fe862cbb19065060e01;hb=38f097ba76fe5b26ccc15a5599e312336200e564;hpb=c80cbce7ac3b38a45a38b57795c7eb19e5177a17
- User Applications that use reflection with QMetaType/QVariant can now handle Q_FLAG.
Before I submit this change to gerrit I would like to hear your feedback.
In case you don't like to change the behaviour of QMetaType::IsEnumeration, we could also achieve the same functionality by either:
- Adding a new flag QMetaType::IsQEnumHelper to QMetaType instead.
- Or by modifying QVariant to/from string/int methods with hacks like the one in the linked webchannel change above.