1
votes

I have an application which consists of two primary modules. One is written in C, uses standard C runtime library and one written in Qt C++. They communicate with each other with IPC. C module creates a char array, fills it with data and sends to the module written in Qt. I want to deserialize received data using QDataStream, but my efforts didn't yield any result yet. Here's a simple example what I'm trying to achieve:

unsigned int pointer = 0;
const int IPC_MSG_LEN = 500;
const int IPC_MSG_HEADER = 200;
const int SOMETHING = 1443;
char api = 55;

char msg[IPC_MSG_LEN] = {0};
memcpy_s(msg, IPC_MSG_LEN, &IPC_MSG_HEADER, sizeof(int));
pointer = sizeof(unsigned int);
memcpy_s(&msg[pointer], IPC_MSG_LEN - pointer, &api, sizeof(char));
++pointer;
memcpy_s(&msg[pointer], IPC_MSG_LEN - pointer, &SOMETHING, sizeof(int));

QByteArray arr(msg, IPC_MSG_LEN);
QDataStream ds(&arr, QIODevice::ReadOnly);
qint32 header = 0, aa = 0;
qint8 t_api = 0;
ds >> header; //Doesn't work
ds >> t_api; //Works
ds >> aa; //Doesn't work

As you can see, the code is pretty simple, but header and aa variables are deserialized to a random number. However t_api (one byte variable) has correct value assigned. So what's the problem with this code? Does QDataStream uses a private data format which is not compatible with the one I'm using? Should I write my own QIODevice implementation or there is a quick fix I'm not aware of? :) Thanks, I appreciate your help.

UPDATE


Thank you very much guys, your solution worked perfectly with those primitive data types, but the problem is that I also need to be able to serialize/deserialize char* strings too.

wchar_t* name1 = L"something";
memcpy_s(&msg[pointer], IPC_MSG_LEN - pointer, name1, (wcslen(name1) + 1) * 2);

char* ai = new char[500];
ds >> ai; //ai becomes NULL here :|

Is there a way to achieve that? Thanks again

2

2 Answers

3
votes

QDataStream stores the numbers in the big endian format by default.
You can change that with:

ds.setByteOrder(QDataStream::ByteOrder(QSysInfo::ByteOrder));

which will use the detected host endianness instead.

3
votes
QDataStream::setByteOrder(QDataStream::LittleEndian);

#include <QDebug>
#include <QByteArray>
#include <QDataStream>
#include <QString>
#include <vector>

template<typename T> void writePtr(char*&dst, T data){
    *reinterpret_cast<T*>(dst) = data;
    dst += sizeof(T);
}

int main(int argc, char** argv){
    const size_t ipcSize = 512;
    std::vector<char> buffer(ipcSize, 0);
    quint32 sendVal1 = 0x12345678, recvVal1 = 0;
    quint8 sendVal2 = 0xee, recvVal2 = 0;   
    quint32 sendVal3 = 0x9999abcd, recvVal3 = 0;
    char* dst = &buffer[0];
    writePtr(dst, sendVal1);
    writePtr(dst, sendVal2);
    writePtr(dst, sendVal3);

    QByteArray byteArray(&buffer[0]);
    QDataStream stream(&byteArray, QIODevice::ReadOnly);
    stream.setByteOrder(QDataStream::LittleEndian);

    stream >> recvVal1 >> recvVal2 >> recvVal3;

    qDebug() << QString(QObject::tr("sent: %1, received: %2")).arg(sendVal1, 8, 16).arg    (recvVal1, 8, 16);
    qDebug() << QString(QObject::tr("sent: %1, received: %2")).arg(sendVal2, 2, 16).arg(recvVal2, 2, 16);
    qDebug() << QString(QObject::tr("sent: %1, received: %2")).arg(sendVal3, 8, 16).arg(recvVal3, 8, 16);

    return 0;
}

but the problem is that I also need to be able to serialize/deserialize char* strings too.

Qt data serialization format is explained (in detail) here. You MUST read that document if you want to use QDataStream for IPC. Qt has nice documentation, so use it.

Also this is not a char* string:

 wchar_t* name1 = L"something";

It is a wchar_t* string.

wchar_t has different size depending on compiler - either 4 or 2 bytes per wchar_t. Which means problem for IPC. unlike wchar_t, char is guaranteed to be 1 byte big. So either encode entire string to UTF8 (or use 8bit-encoded string with known codepage/encoding) and write it as raw data in QByteArray-compatible format:

void writeDataPtr(char*& ptr, const char* data, quint32 size){
    if (!data){
        size = 0xffffffff;
        writePtr(ptr, size);
        return;
    }

    memcpy(ptr, data, size);
    ptr += size;
}

Then use QString::fromUtf8 to decode it (or QTextCodec - if you decided to use other 8bit encoding instead of utf8). OR if you can ensure that your wchar_t* string is UTF16-compliant and sizeof(wchar_t) == 2, dump it in QString-compatible format.

By the way - If I were you, I'd avoid memcpy_s. It is not part of C++ standard, which is a very good reason to avoid it.


I want is to read wchar_t*/char* from QDataStream until stream position gets to null terminating character.

If this is homework, tag your post accordingly.

One of those should work:

QString readWcharString(QDataStream& stream){
    QVector<ushort> rawData;
    ushort tmp;
    do{
        stream >> tmp;
        rawData.push_back(tmp)
    }while(tmp);
    return QString::fromUtf16(rawData.data());
}

or

QString readWcharString(QDataStream& stream){
    QVector<wchar_t> rawData;
    ushort tmp;
    do{
        stream >> tmp;
        rawData.push_back(tmp)
    }while(tmp);
    return QString::fromWCharArray(rawData.data());
}