Search code examples
qtbit-fieldsqfileqdatastream

Write/Read bit fields structure to/from file


I am trying to write bit fields structure to file and then read it.

For example:

typedef struct{
     ushort 
              a:4, 
              b:4, 
              c:4, 
              d:4;
} teststruct;

I try to write and read it like this

QDataStream &operator <<(QDataStream &st, const teststruct &a)
{
    st <<a.a << a.b << a.c << a.d;
    return st;
}

QDataStream &operator >>(QDataStream &st, teststruct &a)
{
    st >>a.a >> a.b >> a.c >> a.d;
    return st;
}

teststruct str1, str2;   
str1.a = 1;
str1.b = 0;
str1.c = 1;
str1.d = 0;

QFile f("testfile");
f.open(QFile::WriteOnly);
QDataStream st(&f);

st << str1;
f.close();
f.open(QFile::ReadOnly);
QDataStream st(&f);   
st >> str2;
f.close();

But in QDataStream::operator>> I got an error

error: cannot bind bitfield 'a.teststruct::a' to 'quint16& {aka short unsigned int&}'

What can I do with >> operator or maybe there is other way to read data to my structure?


Solution

  • In your example you should notice that the data saved to file is probably incorrect. So for example, having the following struct:

    struct BitStruct
    {
        uint8_t     b1:4;
        uint8_t     b2:4;
    };
    

    and the operator written as:

    QDataStream &operator <<(QDataStream &st, const BitStruct &a)
    {
        st <<a.b1 << a.b2;
        return st;
    }
    

    when you write sample data BitStruct bits{0x1, 0x2}; to file you will have 2 bytes written. Binary contents of the file would be 0x01 0x02 which is probably not what you want to achieve.

    It happens due to the fact that calling st << a.b1 results in the b1 field being converted to one of the types handled by QDataStream which in this case is most probably quint8 (you can read more in docs).

    To fix this behaviour you can modify the QDataStream::operator<< implementation to:

    st.writeRawData(reinterpret_cast<const char*>(&a), sizeof(BitStruct));
    

    On the other hand, to read the data to such structure you should do a similar update in the QDataStream::operator>> implementation:

    st.readRawData(reinterpret_cast<char*>(&a), sizeof(BitStruct));
    

    This would allow to write the structure in a compact manner as intended and read particular bit fields accordingly.

    This way you have your whole structure written/read in a single approach and you don't have to worry about further growth of the structure (additional fields) and updating both operator implementations.