Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-74639

QMetaType::IsEnumeration for QFlags<T>

    XMLWordPrintable

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:

      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.

      Attachments

        For Gerrit Dashboard: QTBUG-74639
        # Subject Branch Project Status CR V

        Activity

          People

            Unassigned Unassigned
            t-moe Timo Lang
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:

              Gerrit Reviews

                There are no open Gerrit changes