14

In my program I work a lot with serial communication so QByteArray is used very often.

I was wondering if there was a shorter way to initialize a QByteArray with specific bytes than:

const char test_data[] = {
    static_cast<char>(0xB1), static_cast<char>(0xB2),
    0x5, static_cast<char>(0xFF),
    static_cast<char>(0xEE), static_cast<char>(0xEE),
    static_cast<char>(0x0)}; // Note QByteArray should be able to hold 0 byte
const QCanBusFrame frame = QCanBusFrame(0xA1, QByteArray(test_data));

The static_cast<char> is necessary because otherwise C++11 gives an error about narrowing, because the range 0x7F to 0xFF is bigger than a char could fit--but a char is what the QByteArray constructor asks for.

This is the QByteArray constructor being used:

QByteArray::QByteArray(const char *data, int size = -1)

DBedrenko
  • 4,871
  • 4
  • 38
  • 73
  • What happens when you declare the `test_data` as unsigned char array (unsigned char*) and then cast it to char array (char*)? – OnurA Mar 31 '16 at 08:03
  • @OnurA Are you sure the asterisk needs to be used? The line that inits `test_data` throws "invalid conversion from 'int' to 'const unsigned char*" – DBedrenko Mar 31 '16 at 08:09
  • You can easily create a function which create a `QByteArray` with what you want as argument. – Garf365 Mar 31 '16 at 08:10
  • can any of your byte values be 0x00 - if not you could use the const char* constructor with a string literal – Jimmy Mar 31 '16 at 08:10
  • Sorry about confusion, an array is actually a pointer that's what I meant. In your case it should be: const unsigned char test_data[] = {...} Then you should probably use: QByteArray((char*)test_data) – OnurA Mar 31 '16 at 08:10
  • @Garf365 haha.... an obvious solution but I didn't think of it. That's what I'll do, thanks! – DBedrenko Mar 31 '16 at 08:11
  • @SpaghettiCat You're welcome ;) – Garf365 Mar 31 '16 at 08:12
  • @Jimmy but I wouldn't rely on the possibility that one of the bytes can/can't be 0x00 – OnurA Mar 31 '16 at 08:13
  • 1
    @Jimmy You're right, it's possible to pass "\x01\xB2\xB4" to the constructor, but it is much better to work with integers (you can use constant names, etc.). I should be able to have a "0" byte though... – DBedrenko Mar 31 '16 at 08:14

6 Answers6

36

Simple and effective:

QByteArray b = QByteArrayLiteral("\x12\x00\xa4\x42\x51\x00\x00\x99");
peppe
  • 21,934
  • 4
  • 55
  • 70
  • Hmm I don't see `QByteArrayLiteral` documented anywhere, but by God it works better than the exact same argument passed to `QByteArray`, because the "\x00" are not treated as a string terminator. The same can be achieved with `QByteArray` but you have to pass the correct `size` argument. Not a bad solution, thanks :) – DBedrenko Mar 31 '16 at 08:41
  • 1
    @DBedrenko It's just a macro: http://doc.qt.io/qt-5/qbytearray.html#QByteArrayLiteral – rbaleksandar Oct 25 '17 at 14:15
  • 2
    I wonder _who_ [documented](https://codereview.qt-project.org/#/c/195544/) that... – peppe Oct 25 '17 at 18:26
  • Only present starting from Qt5 – garlix Apr 17 '18 at 13:03
  • 2
    Any other major version of Qt has reached EOL. – peppe Apr 17 '18 at 14:23
  • Nice! , It's worth mentioning that the QByteArrayLiteral will allow the x00 byte to be written, instead of meaning "End" – Francisco D. Hurtado Aug 21 '23 at 09:34
5

As an alternative to QByteArrayLiteral, you can roll your own, if you wish:

#include <QByteArray>

template <int N> QByteArray arrayFromLiteral(const char (&data)[N]) {
   return QByteArray::fromRawData(data, N-1);
}

int main() {
   const auto arr = arrayFromLiteral("\xB1\xB2\0\1");
   Q_ASSERT(arr.size() == 4);
   Q_ASSERT(arr[0] == (char)0xB1);
   Q_ASSERT(arr[1] == (char)0xB2);
   Q_ASSERT(arr[2] == (char)0x00);
   Q_ASSERT(arr[3] == (char)0x01);
}
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
5

May be works slowly:

QByteArray ba = QByteArray::fromHex(QVariant("B1B2FFEEEE00").toByteArray());
Andrey Reeshkov
  • 331
  • 5
  • 14
4

Being inspired by the answers above this is what I finally came up with:

const quint8 testData[] {0xB1, 0x00, 0xB2, 0x00};
const QCanBusFrame cFrame = QCanBusFrame(
    0xA1, QByteArray(reinterpret_cast<const char*>(testData), sizeof(testData)));

I much prefer to have the bytes as byte numbers rather than literal characters when working with serial communication.

After having a discussion on ##c++ I was advised that reinterpret_cast is appropriately used in this situation.

DBedrenko
  • 4,871
  • 4
  • 38
  • 73
3

like this:

const unsigned char str[] = {0xff, 0xed, 0xba, 0xd1};
QByteArray ba(reinterpret_cast<const char*>(&str[0]),std::extent<decltype(str)>::value);

now QByteArray constructor looks weird, but byte sequences are clear. You can also add terminating 0-byte to array instead of using std::extent, but in general you can have zero-bytes in the middle of sequence.

Andrei R.
  • 2,374
  • 1
  • 13
  • 27
0

Have you tried the following:

const unsigned char test_data[] = {
    static_cast<char>(0xB1), static_cast<char>(0xB2),
    0x5, static_cast<char>(0xFF),
    static_cast<char>(0xEE), static_cast<char>(0xEE),
    static_cast<char>(0xB3)};
const QCanBusFrame frame = QCanBusFrame(0xA1, QByteArray((char*)test_data));

You are using the constructor: QByteArray::QByteArray(const char *data, int size = -1).

If size is negative, data is assumed to point to a nul-terminated string and its length is determined dynamically. The terminating nul-character is not considered part of the byte array.

OnurA
  • 611
  • 2
  • 11
  • 25
  • 1
    I just tried it and it compiles successfully. It works without all the `static_cast` (the whole benefit of your solution is that those casts aren't needed). I'm not sure what I should be careful about with 0 bytes: I need to be able to use 0 bytes as well. – DBedrenko Mar 31 '16 at 08:21
  • 1
    You absolutely need to call `QByteArray((char*) test_data, sizeof(test_data));`! The single parameter ctor (or with size == -1) will try to copy the data as normal c-string, which would have to be null-terminated! – Aconcagua Mar 31 '16 at 08:25
  • 1
    Probably if you have a 0x00 in the middle, it will detect it as the end of your char array. That's why I would pass the array size as argument – OnurA Mar 31 '16 at 08:26
  • This is my favourite solution, but the C-style casting is not good. I'm trying now to figure out a way to use a `static_cast` somehow. – DBedrenko Mar 31 '16 at 09:02