Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Can't reset QFlag in simple class



  • Hi,

    I'm trying implement QFlags. I wrote enum in MyClass but in this class I can only set this flag only once. After that I can't see any changes in Debugger in variable MyFlags flag;. Why? Here is the code:

    #include <QFlags>
    
    class MyClass
    {
    public:
        enum MyFlag {
            Yes, // default
            No
        };
        Q_DECLARE_FLAGS(MyFlags, MyFlag)
    
        MyClass(){
            flag.setFlag(Yes); // Yes(0)
            flag.setFlag(No); // No(1)
            flag.setFlag(Yes); // No(1)
            flag.setFlag(No); // No(1)
            flag.setFlag(Yes); // No(1)
        }
    
    private:
        MyFlags flag;
    };
    

  • Lifetime Qt Champion

    Hi
    I think its due to using a value of zero for one of the enums.

    try with

    enum MyFlag {
            Yes = 1, // default
            No =2
        };
    

    then it looks as expected
    alt text


  • Lifetime Qt Champion

    Did you actually check the current value after setting it? Otherwise I don't see how you would really know that it does not work.


  • Lifetime Qt Champion

    Hi
    Its store OR combination of the enums. (in your case zero and 1 )
    The code is

     Q_DECL_RELAXED_CONSTEXPR inline QFlags &setFlag(Enum flag, bool on = true) noexcept
        {
            return on ? (*this |= flag) : (*this &= ~Int(flag));
        }
    
    

    becomes
    value =0
    value |= 0
    value |= 1
    value |= 0
    ...

    So it seems you are expecting it to behave differently?



  • @Christian-Ehrlicher yes, I set a breakpoint and line by line watch the status of variable flag. And in other my application the behavior is the same and I can see this not only with debugger but also how the application runs.

    @mrjj thank you for explanation but this code exceeds my knowledge on C/C++ operators :)
    I wanted that QFlag would be a trigger wich has two positions: Yes and No. I could replace it with bool variable but I want to get experience with QFlags (or C++ flags).
    So inside my class I need to be able to change the satus of flag manually. I thought if I write flag.setFlag(Yes); then flag.testFlag(Yes); should return true and in opposite: flag.setFlag(No); flag.testFlag(No); should return true
    But this doesn't work like I expected...


  • Lifetime Qt Champion

    @Please_Help_me_D
    Hi
    yes a bool would act as a toggle but QFlags is
    actually more like a list of bools.
    You can then set one or more of these bools.

    so say you had something to describe a person

     enum MyFlag {
            Clever, // default
            Handsome,
           Rich
        };
    

    then
    flag.setFlag( Clever );
    flag.setFlag( Handsome );

    Then you set the flags for the person to be Clever and handsome.

    For your exmaple to make it flip/flop you would have to do
    flag.setFlag(Yes ,true ); // turn on
    flag.setFlag(Yes, false); // turn off
    flag.setFlag(No); // turn ON

    Setting No alone , will not turn off Yes.
    It means that No is on also. (or off)

    Hope this makes better sense then :)



  • @mrjj thank, now I understood but the problem is in this way they also don't work.
    I just tried to rewrite MyClass here it is:

    #include <QFlags>
    
    class MyClass
    {
    public:
        enum MyFlag {
            Yes, // default
            No
        };
        Q_DECLARE_FLAGS(MyFlags, MyFlag)
    
        MyClass(){
            flag.setFlag(Yes, true);
            bool a = flag.testFlag(Yes); // a == true
    
            flag.setFlag(Yes, false);
            bool b = flag.testFlag(Yes); // b == true
    
            flag.setFlag(Yes, true);
            bool c = flag.testFlag(Yes); // c == true
        }
    
    private:
        MyFlags flag;
    };
    

    Why all the three bool vars are true? Is that ok?
    6cc5c7b0-2e62-4e1f-9593-5cd74e6329c1-image.png


  • Lifetime Qt Champion

    Hi
    I think its due to using a value of zero for one of the enums.

    try with

    enum MyFlag {
            Yes = 1, // default
            No =2
        };
    

    then it looks as expected
    alt text



  • @mrjj huh now it works :)
    thank you!


  • Lifetime Qt Champion

    Hi,

    It is shown in and explained in the QFlags that each item of your flags need to have a different value (1 -> 2 -> 4 -> 8 -> etc.) that can be ORed otherwise you cannot establish which was set. You can do that using a binary format for your number, it will make things clearer.



  • @SGaist thank you!
    But it is always difficult for me to read documentation about something new that I don't have any idea. It becomes much easier when I already have some representation about studied topic.
    As example I've read both this and that but nothing helped me to solve my task. I'm like a slowpoke :)
    But now when I know that QFlag is something like a list of boolean variables I can match documentation with my needs. I always search "how to solve my problem" in Internet and only then I disturb people from communities.
    This is just my excuses so you would not think that I don't do anything to solve my problem by myself :)

    I really appreciate forum's people help because you save a lot of time and nerves and honestly saying I would have never learned programming (Matlab and C/C++) if there were not such communities.


  • Lifetime Qt Champion

    @Please_Help_me_D

    Hi
    Yes its like a list
    but to make it take up far less space, it uses bits instead of a bool.
    So basically QFlags helps you get/and set the bits.

    You know for programming a value can also be shown as binary 0 and 1 ?

    When you OR something, then just one of the bit has to be set then it becomes set in the result too.

    0 1 0 1 OR
    0 1 1 0
    -------
    0 1 1 1
    

    with AND operator both bits must be set to be set in the result.

    Thats how QFlags works interinally

    if we look at

     Q_DECL_RELAXED_CONSTEXPR inline QFlags &setFlag(Enum flag, bool on = true) noexcept
        {
            return on ? (*this |= flag) : (*this &= ~Int(flag));
        }
    
    

    then it says

    if (on is true )
    value = value OR flag ( in your case yes )
    else if on is false ( to turn off )
    value = value AND (negative) flag

    the last part is taking out the bits that belong to the value for the flag.

    So AND / OR is not that difficult to understand at a high level as it simply
    manipulate the values as bits.



  • @mrjj thank you!
    I understood something but some interesting things I don't. Here what I get.
    Yes I understand byte/bit

    This:

    on ? (*this |= flag) : (*this &= ~Int(flag));
    

    is equal to:

    if ( on == true ){
        *this = (*this || flag);
    } else {
        *this = (*this &~ Int(flag));
    }
    

    right?
    If so then to understand the mathematics I need to understand what in bit representation is: *this, flag, Int(flag)
    In my case flag is equal to 1 right ( enum MyFlag { Yes = 1} )? in bit representation 0001
    But what is in *this before assigning anything (operator =) and what function Int() does? simply converts flag to integer? as I know this type of casting doesn't change bits of the number it simply tells "these bits should be read as int"
    Something like that:)

    |________|________________________
    By the way how to turn on notifications when somebody answers me or writes in topic created by me?


  • Lifetime Qt Champion

    @Please_Help_me_D

    Yes odd syntax is called Conditional ternary operator:
    but is just a one liner if else.

    (condition) ? (if_true) : (if_false)

    The *this de reference the this pointer.
    so its what it points to.
    This is done to trigger the operator = which QFlags have overloaded.
    so its a way to assign the value to it self using =

    the Int() is a type cast like (Int)flag;
    But its a bit more if we look at source

    #if defined(Q_CC_MSVC) || defined(Q_CLANG_QDOC)
        // see above for MSVC
        // the definition below is too complex for qdoc
        typedef int Int;
    #else
        typedef typename std::conditional<
                std::is_unsigned<typename std::underlying_type<Enum>::type>::value,
                unsigned int,
                signed int
            >::type Int;
    #endif
    

    so when not MSVC compiler it also checks the type

    And if you think that looks strange, you are right :)

    Its c++ templates and while really useful, its also
    often ugly and unreadable. So ignore it until later in your training.



  • @mrjj all the things that depend on the compiler features are far outside of my current possibilities :)
    So I think I begin to understand the magic behind QFlag.
    returned value is of type bool and bool is not a single bit but rather a byte (maybe few bytes). So any numbers written in Yes except zero in this expression:

    *this || flag
    

    returnes some byte (bytes) where even a single (or more) bits is not zero. A bool which has even a single (or more) non zero bit should return true right?
    And I believe that behind this dark veil:

    *this &~ Int(flag)
    

    are hidden all zeroed bits :)

    By the way is it necessary to assign numbers to enum definitions (Yes, No etc) that are equal the power of 2 (1, 2, 4, 8, 16 ...)? as in many examples


  • Lifetime Qt Champion

    @Please_Help_me_D

    Ok that's good :)
    maybe you are ready for
    https://www.youtube.com/watch?v=TKEaakjKrMc
    Bitwise AND / OR

    Actually the dark veil:
    is using AND and BINARY NOT
    to remove the bits for a flag

    Yes, you must use power of 2 values as else some of the values will have
    overlapping bit representation and things get funny when you combine certain flags.



  • @mrjj thank you
    Video helped me to remember what XOR. Also now it I understand how to easilly transform binary data representation to decimals :)


Log in to reply