1

In the C++, there are 6 bitwise operators:

Symbol  Operator
&       bitwise AND
|       bitwise inclusive OR
^       bitwise XOR (eXclusive OR)
<<      left shift
>>      right shift
~        bitwise NOT (one's complement) (unary)

In Qt, I have seen |, ~, and & used fairly often in functions:

file->open(QIODevice::WriteOnly | QIODevice::Truncate);

The source for that function looks like this:

bool QFile::open(OpenMode mode)
{
    Q_D(QFile);
    if (isOpen()) {
        qWarning("QFile::open: File (%s) already open", qPrintable(fileName()));
        return false;
    }
    if (mode & Append)
        mode |= WriteOnly;

    unsetError();
    if ((mode & (ReadOnly | WriteOnly)) == 0) {
        qWarning("QIODevice::open: File access not specified");
        return false;
    }
    if (fileEngine()->open(mode)) {
        QIODevice::open(mode);
        if (mode & Append)
            seek(size());
        return true;
    }
    QFile::FileError err = fileEngine()->error();
    if(err == QFile::UnspecifiedError)
        err = QFile::OpenError;
    d->setError(err, fileEngine()->errorString());
    return false;
}

The source code for the enumeration looks like this:

enum OpenModeFlag {
    NotOpen = 0x0000,
    ReadOnly = 0x0001,
    WriteOnly = 0x0002,
    ReadWrite = ReadOnly | WriteOnly,
    Append = 0x0004,
    Truncate = 0x0008,
    Text = 0x0010,
    Unbuffered = 0x0020,
    NewOnly = 0x0040,
    ExistingOnly = 0x0080
};
Q_DECLARE_FLAGS(OpenMode, OpenModeFlag)

In the book I learned Qt and C++ from, it does not adequately touch upon these design patterns, so I am not even quite sure how I am to read this code, or what extent I should use Bitwise Operators in tandem with my enumerations.

Questions

  1. How exactly is file->open(QIODevice::WriteOnly | QIODevice::Truncate)); evaluated in the first place? Does it evaluate the both enumerations like so:

    QIODevice::WriteOnly > 0x0002 > 0000 0000 0000 0000 0000 0010
    QIODevice::Truncate  > 0x0008 > 0000 0000 0000 0000 0000 1000
                                  | 0000 0000 0000 0000 0000 1010
    

    and run the function like this ?

    file->open(0000 0000 0000 0000 0000 1010); // 10 is not set in enum
    
  2. If this is the case, what is the relevance of the number 10 in binary?

  3. Why are the set numbers all powers of 2?
  4. Why use Hexadecimals for the numbers and not plain integers?
  5. Could every bitwise operator be applied to the function and how would I read it? This would be my rudamentary guess:

    file->open(QIODevice::WriteOnly & QIODevice::Truncate); // Both have to be true?
    file->open(QIODevice::WriteOnly | QIODevice::Truncate); // At least one has to be true?
    file->open(QIODevice::WriteOnly ^ QIODevice::Truncate); // Only one has to be true?
    file->open(QIODevice::WriteOnly ~ QIODevice::Truncate); // WriteOnly has to be true and Truncate has to be false
    file->open(QIODevice::WriteOnly << QIODevice::Truncate);// ??? 
    file->open(QIODevice::WriteOnly >> QIODevice::Truncate);// ???
    
  6. In the function source, I also see things such as |=; What does it do?

  7. How would I read this line of code: if ((mode & (ReadOnly | WriteOnly)) == 0) ?
  8. In more practical terms, what are the circumstances in which I would use each bitwise operator to use in tandem with enumerations?

Thanks.

1
  • 1
    ~ is a unary operator, so QIODevice::WriteOnly ~ QIODevice::Truncate isn't an expression
    – Caleth
    Commented Nov 28, 2018 at 9:52

1 Answer 1

5

Your understanding is pretty good. To answer your questions:

  1. Yes, it the function will receive 0x0000 0000 0000 0000 0000 1010 as an argument.
  2. The significance of the decimal number 10 in binary is that it is composed of 2 "on" bits in positions 1 and 3. There's little reason to think about the number in decimal.
  3. The numbers are powers of 2 so that each one can represent an independent flag without conflicting with the others.
  4. They are written in hexadecimal because it's more compact that binary, but still shows the binary nature of the numbers. Since each is a power of 2, it follows a pattern of 0,1,2,4,8 for each hex position. When you see that, you should immediately recognize it as a set of independent flags.
  5. You can apply any of those operators, but they probably wouldn't do what you are thinking from your description. Using bitwise or is the main intended use, and this is a pattern you'll see in many other code bases. Other patterns of use may come into play when you're passing flags that your function received and you don't want to manually check every possible combination of flags. For example, maybe you want to ensure that you only ever open a file as read-only, but want to keep all the other flags passed into the function. You could do something like flags = flags & ~WriteOnly. That says to perform a bitwise and with the flags and the inverse of WriteOnly. The inverse of WriteOnly has every bit set except for the one that you normally associate with WriteOnly. So this has the effect of saying, "I want everything to stay the same except the WriteOnly flag which I want to always be 0."
  6. This isn't a question, but if you see something like x |= y; it just means the same as x = x | y. It is to bitwise or as += is to addition.
  7. You would read it as "If the mode variable has neither ReadOnly nor WriteOnly set"
  8. Bitwise operators are used with enumerations when the values of the enumerations are powers of 2 that represent independent flags (booleans, essentially). Sometimes instead of making the enumerations be 2n, the enumerations are just n. In these cases, you construct the flag you want by doing x = 1 << flag where flag is n. So for bit 0, flag = 0, for bit 1, flag = 1.
4
  • Most of my questions have been answered, save #8. On that, I would like to know practical usecases for each of the bitwise operators working with enums. If that is too broad a question, let me know, otherwise after I know when I should be considering using any of the operators, I will give you the checkmark. Thanks.
    – Anon
    Commented Nov 28, 2018 at 8:19
  • 1
    @Akiva: Essentially, you'd use this technique when you have a bunch of on-off options (flags), and you want to be able to combine them easily, within a single parameter - leveraging the bitwise operators is a convenient way to do that. If you don't know what they do, check this out, but essentially, in this context you just use them to turn bits on or off (see this). There are other uses for these operators, but this is a common one. Commented Nov 28, 2018 at 9:46
  • 1
    @Akiva You are unlikely to use << and >> with enums, except with an iostream as the first parameter. The closest might be a flags definition of the form Value1 = 1 << 0, Value2 = 1 << 1, ...
    – Caleth
    Commented Nov 28, 2018 at 9:55
  • 1
    @Akiva I've expanded point 8 to show where you'll use the shift operators with enumerations. It's less common but does come up from time to time. Commented Nov 29, 2018 at 3:05

Not the answer you're looking for? Browse other questions tagged or ask your own question.