Iterate over enums set in a QFlags value (2024)

  • DOffline

    DOffline

    DerManu

    wrote on 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 ReplyLast reply

  • JOffline

    JOffline

    joonhwan

    wrote on 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 ReplyLast reply

  • COnline

    COnline

    Chris KawaLifetime Qt Champion

    wrote on 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 ReplyLast reply

  • JOffline

    JOffline

    joonhwan

    wrote on last edited by

    #4

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

    joonhwan at gmail dot com

    1 ReplyLast reply

  • LOffline

    LOffline

    lgeyer

    wrote on 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 ReplyLast reply

  • COnline

    COnline

    Chris KawaLifetime Qt Champion

    wrote on 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 ReplyLast reply

  • DOffline

    DOffline

    DerManu

    wrote on 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 ReplyLast reply

  • COnline

    COnline

    Chris KawaLifetime Qt Champion

    wrote on 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 ReplyLast reply

  • BOffline

    BOffline

    bam80

    wrote on 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 ReplyLast reply

Iterate over enums set in a QFlags value (2024)
Top Articles
How To Get Comped in Las Vegas - Sin City VIP
Vegas Low Roller Lawsuit: Exploring the Legal Battle of a Small Stakes Gambler - Law Empower
Dte Outage Map Woodhaven
Brady Hughes Justified
Farepay Login
Ets Lake Fork Fishing Report
Greedfall Console Commands
Robot or human?
Summit County Juvenile Court
Collision Masters Fairbanks
Devotion Showtimes Near Mjr Universal Grand Cinema 16
Georgia Vehicle Registration Fees Calculator
Jennette Mccurdy And Joe Tmz Photos
라이키 유출
Phenix Food Locker Weekly Ad
AB Solutions Portal | Login
What Was D-Day Weegy
Tribune Seymour
Wunderground Huntington Beach
Cooktopcove Com
Chris Hipkins Fue Juramentado Como El Nuevo Primer Ministro De...
Classroom 6x: A Game Changer In The Educational Landscape
Available Training - Acadis® Portal
How Much Is Tay Ks Bail
Pay Boot Barn Credit Card
Van Buren County Arrests.org
Nz Herald Obituary Notices
Okc Body Rub
Talk To Me Showtimes Near Marcus Valley Grand Cinema
Sorrento Gourmet Pizza Goshen Photos
Craigslist Dubuque Iowa Pets
The Banshees Of Inisherin Showtimes Near Broadway Metro
Dal Tadka Recipe - Punjabi Dhaba Style
Where to eat: the 50 best restaurants in Freiburg im Breisgau
My Dog Ate A 5Mg Flexeril
Mastering Serpentine Belt Replacement: A Step-by-Step Guide | The Motor Guy
Spy School Secrets - Canada's History
Appleton Post Crescent Today's Obituaries
Robeson County Mugshots 2022
Atlanta Musicians Craigslist
Husker Football
Weather Underground Corvallis
The Listings Project New York
Anderson Tribute Center Hood River
Parent Portal Pat Med
Craigslist/Nashville
Brother Bear Tattoo Ideas
Top 1,000 Girl Names for Your Baby Girl in 2024 | Pampers
Bonecrusher Upgrade Rs3
Missed Connections Dayton Ohio
Puss In Boots: The Last Wish Showtimes Near Valdosta Cinemas
Mike De Beer Twitter
Latest Posts
Article information

Author: Stevie Stamm

Last Updated:

Views: 5959

Rating: 5 / 5 (80 voted)

Reviews: 95% of readers found this page helpful

Author information

Name: Stevie Stamm

Birthday: 1996-06-22

Address: Apt. 419 4200 Sipes Estate, East Delmerview, WY 05617

Phone: +342332224300

Job: Future Advertising Analyst

Hobby: Leather crafting, Puzzles, Leather crafting, scrapbook, Urban exploration, Cabaret, Skateboarding

Introduction: My name is Stevie Stamm, I am a colorful, sparkling, splendid, vast, open, hilarious, tender person who loves writing and wants to share my knowledge and understanding with you.