Search code examples
c++qthexqbytearray

Append multiple hex numbers to QByteArray simultaneously


I have bunch of hex numbers, but I don't feel like doing

QByteArray ba;
ba.append(0x01);
ba.append(0x02);
ba.append(0x7A);
...

Can I do that in one line? Maybe with QString manipulation?

I'm sending messages via serial communication QExtSerialPort and I need to store Hex commands in QByteArray so I can use qint64 write(const QByteArray &data)


Solution

  • The first point is: there is no such thing as hex numbers. Numbers are numbers. We're talking of integers here. Whether you say 16, 0x10, 020, etc., it's all the same number. 0x10 is "hex" only in the sense that you write it out in hexadecimal notation. It doesn't change anything about the number itself, it doesn't make it "hex"! It's still the number you get by incrementing up from zero sixteen times.

    The only reason for hexadecimal is that probably the device's documentation gives command packets in hexadecimal. It's a completely arbitrary choice. You can copy the numbers in hexadecimal from the documentation, or convert them to another base if it makes more sense to you.

    So, the hexadecimal representation of course is completely up to you, you don't need to explicitly use it. But you do need to use the C constant arrays in some fashion - they make things simple and low-overhead. Suppose your command was to consist of three bytes with decimal values 16, 33, 47.

    You could encode it as follows:

    static const char cmdBuf[] = { 16, 33, 47 };
    const auto cmd = QByteArray::fromRawData(cmdBuf, sizeof(cmdBuf));
    

    The byte array cmd is initialized without copying the data, so that's fast and efficient. The cmdBuf must be static since it must exist as long as cmd or any of its (shallow) copies do.

    You could use a string literal to initialize the array, using hexadecimal escapes for non-printable characters, and printable characters otherwise.

    static const char cmdBuf[] = "\x10!/";
    const auto cmd = QByteArray::fromRawData(cmdBuf, sizeof(cmdBuf)-1);
    

    The reason for sizeof(...)-1 is that the cmdBuf is 4 bytes long - the string literal ends with a terminating zero that you don't really need.

    If you wished to use hex representation for all bytes, you would have

    static const char cmdBuf[] = "\x10\x21\x2F";
    const auto cmd = QByteArray::fromRawData(cmdBuf, sizeof(cmdBuf)-1);
    // or
    static const char cmdBuf[] = { 0x10, 0x21, 0x2F };
    const auto cmd = QByteArray::fromRawData(cmdBuf, sizeof(cmdBuf));
    

    Of course you could use octal as well:

    static const char cmdBuf[] = "\020\041\057";
    const auto cmd = QByteArray::fromRawData(cmdBuf, sizeof(cmdBuf)-1);
    // or
    static const char cmdBuf[] = { 020, 041, 057 };
    const auto cmd = QByteArray::fromRawData(cmdBuf, sizeof(cmdBuf));
    

    Or any mix of them!

    static const char cmdBuf[] = "\020\x21/";
    const auto cmd = QByteArray::fromRawData(cmdBuf, sizeof(cmdBuf)-1);
    //or
    static const char cmdBuf[] = { 16, 0x21, 057 };
    const auto cmd = QByteArray::fromRawData(cmdBuf, sizeof(cmdBuf));
    

    In the string literal, whether you encode it all as hex/octal escapes or use printable characters is up to you, it's a matter of style. If the values in your commands don't have the meaning of printable characters, the numerical (hex or octal) encoding in the string literal or array initializer is probably better.

    When choosing between octal and hex, follow the structure of the command bytes or your own preference. If the bytes have a structure that somehow breaks down into groups of 2+3+3 bits, then octal is a good way of making it human-readable. Otherwise, use hex or decimal. It's all about what makes the code easier to read - the machine doesn't care, the binary output will be identical no matter which way you go about it.