Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Iterate over enums set in a QFlags value
QtWS25 Last Chance

Iterate over enums set in a QFlags value

Scheduled Pinned Locked Moved General and Desktop
9 Posts 5 Posters 8.8k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • D Offline
    D Offline
    DerManu
    wrote on 16 Jan 2013, 22:44 last edited by
    #1

    Hi all,

    I've seen a certain pattern emerge in some places of my code lately. It goes approximately like this:
    @
    enum myEnum {e1 = 0x1
    ,e2 = 0x2
    ,e3 = 0x4};
    Q_DECLARE_FLAGS(myEnumFlags, myEnum)

    void someFunction(myEnumFlags p)
    {
    QVector<myEnum> flagsInP;
    if (p.testFlag(e1)) flagsInP.append(e1);
    if (p.testFlag(e2)) flagsInP.append(e2);
    if (p.testFlag(e3)) flagsInP.append(e3);
    for (int i=0; i<flagsInP.size(); ++i)
    {
    // do something with flagsInP.at(i)
    }
    }
    @

    Now in my opinion this isn't very elegant, since a QFlags is already a container in some sense and there must be some elegant way to iterate its set flags, i.e. retrieving the set enum values iteration by iteration.

    So when someFunction(e1|e2) is called, I'd like to loop over {e1, e2}.

    Any Ideas?

    1 Reply Last reply
    0
    • J Offline
      J Offline
      joonhwan
      wrote on 17 Jan 2013, 00:26 last edited by
      #2

      That is one of my questionares in learning Qt and c++.
      I've done similar thing to meet your requirement using Qt's meta object and property system.
      Key points for me to know what kind of flag values are available and iterate over them.

      Take a look(modified to look like your enum decl) and hope this helps

      @
      #include <QtCore>
      #include <QDebug>

      class MyData : public QObject
      {
      Q_OBJECT
      public:
      enum myEnum {
      e1 = 0x1,
      e2 = 0x2,
      e3 = 0x4,
      e4 = 0x8,
      };
      Q_DECLARE_FLAGS(myEnumFlags, myEnum)
      Q_FLAGS(myEnum myEnumFlags)

      void test()
      {
      qDebug() << "testing enum inside of decl";
      myEnumFlags value = e1 | e4;
      QMetaEnum me = MyData::staticMetaObject.enumerator(0);
      for (int i=0; i<me.keyCount(); ++i) {
      if (value.testFlag((myEnum)me.value(i))) {
      qDebug() << "flag is on over " << me.key(i);
      }
      }
      }
      protected:
      };

      int main(int argc, char** argv)
      {
      qDebug() << "testing enum outside of class";
      {
      MyData::myEnumFlags value = MyData::e1 | MyData::e4;
      QMetaEnum me = MyData::staticMetaObject.enumerator(0);
      for (int i=0; i<me.keyCount(); ++i) {
      if (value.testFlag((MyData::myEnum)me.value(i))) {
      qDebug() << "flag is on over " << me.key(i);
      }
      }
      }
      MyData tester;
      tester.test();
      }

      #include "main.moc"
      @

      and the result (tested /w visual studio 2008) ...

      @
      testing enum outside of class
      flag is on over e1
      flag is on over e4
      testing enum inside of decl
      flag is on over e1
      flag is on over e4
      계속하려면 아무 키나 누르십시오 . . .
      @

      PS: I always am very shy to answer the question whose poster level is higher than me. :-o

      joonhwan at gmail dot com

      1 Reply Last reply
      0
      • C Offline
        C Offline
        Chris Kawa
        Lifetime Qt Champion
        wrote on 17 Jan 2013, 01:25 last edited by
        #3

        While working fine, QMetaEnum is not very comfy because you have to embed your enum inside a QObject derived class and then instantiate that class to get to the meta object. Things complicate even more if you have more than one enum inside that class, since you can no longer do just MyData::staticMetaObject.enumerator(0), but have to find the correct index.

        A somewhat different, not without its drawback of course, solution is to use this "java style" flag iterator class:
        @
        template <typename T, typename U>
        class FlagIterator
        {
        public:
        FlagIterator(T& flags) : mFlags((unsigned)flags), mFlag(0) {}
        inline U value() { return static_cast<U>(mFlag); }
        inline bool hasNext() { return mFlags > mFlag; }
        void next() { if(mFlag == 0) mFlag = 1; else mFlag <<= 1;
        while((mFlags & mFlag) == 0) mFlag <<= 1; mFlags &= ~mFlag; }
        private:
        unsigned mFlags;
        unsigned mFlag;
        };
        @
        Having that and your enum declared with Q_DECLARE_FLAGS(SomeFlags, SomeFlag), you can do this:
        @
        FlagIterator<SomeFlags, SomeFlag> fi(f);
        while(fi.hasNext()) {
        fi.next();
        //do somethig with fi.value()
        }
        @

        1 Reply Last reply
        0
        • J Offline
          J Offline
          joonhwan
          wrote on 17 Jan 2013, 07:33 last edited by
          #4

          Looks nice and neat. Though i'm not OP, but it helps. thanks.

          joonhwan at gmail dot com

          1 Reply Last reply
          0
          • L Offline
            L Offline
            lgeyer
            wrote on 17 Jan 2013, 10:14 last edited by
            #5

            [quote author="Krzysztof Kawa" date="1358385940"]While working fine, QMetaEnum is not very comfy because you have to embed your enum inside a QObject derived class and then instantiate that class to get to the meta object. Things complicate even more if you have more than one enum inside that class, since you can no longer do just MyData::staticMetaObject.enumerator(0), but have to find the correct index.[/quote]On a sidenote: You actually don't have to.

            You can use Q_GADGET instead of Q_OBJECT for enumerations, which does not depend on QObject.
            @
            class SomeClass
            {
            Q_GADGET
            Q_ENUMS(SomeEnum)

            public:
            enum SomeEnum
            {
            Value1 = 0x1,
            Value2 = 0x2,
            Value3 = 0x4
            };
            };

            int someEnumIndex = SomeClass::staticMetaObject.indexOfEnumerator("SomeEnum");
            QMetaEnum someEnum = SomeClass::staticMetaObject.enumerator(someEnumIndex);
            @

            1 Reply Last reply
            0
            • C Offline
              C Offline
              Chris Kawa
              Lifetime Qt Champion
              wrote on 17 Jan 2013, 10:42 last edited by
              #6

              Good point. Q_GADGET is a blind spot of mine. No matter how many times I've seen it I always forget about it :)
              As for the index - again, good point, but I personally avoid using "string references". I know it's not uncommon in Qt world, but the problem is - if one day someone changes the name of the enum you won't get a nice compiler error, just a -1 index which you might not catch right away, and it's still more monkey typing than iterator :P

              But again - there are pros and cons to both solutions.

              1 Reply Last reply
              0
              • D Offline
                D Offline
                DerManu
                wrote on 17 Jan 2013, 21:34 last edited by
                #7

                Thanks for all your ideas.
                I'm aware of Qt's meta system and QMetaEnum, but somehow I find that unelegant too, especially the string indexing, as Krzysztof said. And the code looks hacky. I treat qt's meta-object system always as a last resort.

                The double-templated Iterator idea is pretty neat, didn't think of that. Produces very beautiful code (and is itself as beautiful as a double-template can be ;). I guess I'll use that sooner or later. They should include it in Qt, if you ask me, so I don't have to include it in my library, where it doesn't belong.

                [quote author="joonhwan" date="1358382382"]I always am very shy to answer the question whose poster level is higher than me. [/quote] Naah. Don't respect people for the post-count next to their name. There's no proportionality to the IQ (unfortunately).

                1 Reply Last reply
                0
                • C Offline
                  C Offline
                  Chris Kawa
                  Lifetime Qt Champion
                  wrote on 17 Jan 2013, 22:01 last edited by
                  #8

                  [quote author="DerManu" date="1358458466"]as beautiful as a double-template can be ;)[/quote]
                  Well, since enums are pretty much ints anyway (in most, the simplest, cases) you could probably go ahead with one template param:
                  @
                  template <typename T>
                  class FlagIterator
                  {
                  public:
                  FlagIterator(unsigned flags) : mFlags(flags), mFlag(0) {}
                  inline T value() { return static_cast<T>(mFlag); }
                  inline bool hasNext() { return mFlags > mFlag; }
                  void next() { if(mFlag == 0) mFlag = 1; else mFlag <<= 1;
                  while((mFlags & mFlag) == 0) mFlag <<= 1; mFlags &= ~mFlag; }
                  private:
                  unsigned mFlags;
                  unsigned mFlag;
                  };
                  @
                  or, if you don't really care about types and don't mind casting in user code, with no templates at all:
                  @
                  inline unsigned value() { return mFlag; }
                  @

                  1 Reply Last reply
                  0
                  • B Offline
                    B Offline
                    bam80
                    wrote on 17 Jul 2014, 15:51 last edited by
                    #9

                    Here is my subclass of QFlags with iterator over flags. Improvements welcome.
                    Sorry it's not quite readable here.

                    @/**

                    • @brief custom QFlags class

                    • QFlags with iterator and methods to search next set flag
                      /
                      template <typename T>
                      class qtvFlags : public QFlags<T>
                      {
                      public:
                      /
                      TODO:

                        • check that *this != 0
                        • register QMetaEnum here
                          */
                          qtvFlags(const QFlags<T> & other) : QFlags<T>(other) {}
                          qtvFlags(T e) : QFlags<T>(e) {}
                          qtvFlags(QFlag value) : QFlags<T>(value) {}

                      /**

                      • @brief Search nearest set flag

                      • @param flag enum value to start search from

                      • @return next set flag found

                      • Iterate to the next set bit in the bit-set, nearest to the passed one while

                      • searching forward

                      • inline?
                        */
                        const T next(unsigned flag = 1) const {
                        if (!flag) flag = 1;
                        else flag <<= 1;
                        if (flag > *this || !flag) return (T)0; // check !m in case max enum flag set or *this==0
                        // consider: 'm > *this | !m' to eliminate branching

                        while ( !( *this & flag ) ) flag <<= 1;

                        return static_cast<T>(flag);
                        }

                      /**

                      • @brief Circular search nearest set flag

                      • @param flag enum value to start search from

                      • @param forward search direction

                      • @return next set flag found

                      • Iterate to the next set bit in the bit-set, nearest to the passed one while

                      • searching by circle in the specified direction

                      • *this must not be 0

                      • inline?
                        */
                        const T circularNext(unsigned flag = 1, const bool forward = true) const {
                        if (!flag) flag = 1;
                        const unsigned shift = forward ? sizeof(flag) * CHAR_BIT - 1 : 1;

                        /* GCC usually recognizes certain circular shift expressions to introduce "rotate" instruction on CPUs that support it.

                        • It was checked GCC does it here too at least for x86 and ARM.
                        • ARM has only ROR, so use right shift expression here.
                          */
                          while ( !( *this & (flag = ( flag >> shift | flag << (sizeof(flag) * CHAR_BIT - shift) )) ) );

                        return static_cast<T>(flag);
                        }

                      class const_iterator : public std::iterator<std::forward_iterator_tag, const T> {
                      const qtvFlags<T>* /*const */i; // const avoiding gives us copy assignable class by default
                      unsigned int m;

                      public:
                      inline const_iterator(const qtvFlags<T>* const p) : i(p), m(0) {}

                       inline const T& operator*() const { return static_cast<const T>(m); }
                      
                       inline bool operator==(const const_iterator &o) const { return m == o.m /*&& i == o.i*/; }
                       inline bool operator!=(const const_iterator &o) const { return m != o.m /*|| i != o.i*/; }
                       inline const_iterator& operator++() { m = i->next(m); return *this;}
                       inline const_iterator operator++(int) { const const_iterator n(*this); m = i->next(m); return n; }
                      

                      };

                      const_iterator begin() const { return ++const_iterator(this);}
                      const_iterator end() const { return const_iterator(this); }
                      };@

                    1 Reply Last reply
                    0

                    • Login

                    • Login or register to search.
                    • First post
                      Last post
                    0
                    • Categories
                    • Recent
                    • Tags
                    • Popular
                    • Users
                    • Groups
                    • Search
                    • Get Qt Extensions
                    • Unsolved