Qt analog to C struct?



  • Hi -

    My app uses a message header of a fixed size. I'd intended to do this:

        struct
        {
            QByteArray hmac = QByteArray(32, '\0');
            QByteArray iv = QByteArray(16, '\0');
            QByteArray username = QByteArray(100, '\0');
            QByteArray password = QByteArray(100, '\0');
            QByteArray nonce = QByteArray(16, '\0');
            uint8_t timestamp[23];
            uint8_t reserved[1];
        } msgHeader;
    
    

    But I seem to get pointers to the QByteArrays in the structure, so this might not be the right approach.

    Is there a Qt data structure that allows me to do this, or should I just keep all the fields as uint8_t, and convert to/from QByteArrays for processing?


  • Qt Champions 2016

    @mzimmers said in Qt analog to C struct?:

    But I seem to get pointers to the QByteArrays in the structure, so this might not be the right approach.

    Rightfully so. QByteArray uses implicit sharing and is the size of void * the actual data is kept in the private object.

    Is there a Qt data structure that allows me to do this

    No.

    or should I just keep all the fields as uint8_t

    Yes, or use qint8 (they're aliases)

    convert to/from QByteArrays for processing

    Attach a QByteArray to the data without copying it. Use QByteArray::fromRawData for this and then pass it on for processing.

    PS:
    @Wieland also suggested, rightfully so, to use std::array instead in this case.



  • I'm not sure I understand...what should I use std::array for exactly?


  • Qt Champions 2016

    std::array is a thin wrapper (template) around a statically sized array of a given type. So you could use it instead of int[], for example:

    struct
    {
        // ...
        std::array<uint8_t, 23> timestamp;
        std::array<uint8_t, 1> reserved;
    } msgHeader;
    

  • Moderators

    As a replacement for the QByteArrays and the C-style array:

    struct MsgHeader
    {
        std::array<uint8_t, 32> hmac = {};
        // ...
        std::array<uint8_t, 23> timestamp = {};
    };
    


  • @Wieland OK. What is the advantage of this -- automatic range checking or some other safeties?


  • Qt Champions 2016

    yes, also somewhat more type-safe as it does not decay readily to void *, is a C++ object ... and, well, the cool kids are using it (not that I approve, but that's whole other rant) ... ;)


  • Moderators

    Yes, automatic range checking, increased safety against unintentional pointer mistakes, and compared to QByteArray, increased performance.



  • OK, thank you both.


  • Moderators

    Lot of cross-posting going on here ^_^


  • Qt Champions 2016

    Twice in less than 10 minutes, must be a forum record or something.



  • Just to pursue this a little further, if I do use the std::array template, can I still do a memcpy, or do I have to assign the elements individually? I couldn't see anything in the cPPreference page on this.

    Also, would it be better just to create one large QByteArray and use pointers to access delimited areas within it? (I'm still a little surprised that Qt doesn't have some construct to handle this).


  • Moderators

    @mzimmers said in Qt analog to C struct?:

    can I still do a memcpy

    The correct way to copy the array is to use std::copy. It's up to the implementation of your standard library to then find the fastest way to copy your data.


  • Qt Champions 2016

    @mzimmers said in Qt analog to C struct?:

    if I do use the std::array template, can I still do a memcpy

    Yes, std::array is an aggregate (means it has only one member that is your regular type[] array). Although(!), you should copy it like any other object - it is an object after all.

    Also, would it be better just to create one large QByteArray and use pointers to access delimited areas within it?

    And throw away type safety? Why? There's no significant difference between that and having a struct containing fixed sized arrays.

    I'm still a little surprised that Qt doesn't have some construct to handle this

    To handle what exactly?


  • Moderators

    @mzimmers said in Qt analog to C struct?:

    Also, would it be better just to create one large QByteArray and use pointers to access delimited areas within it?

    I don't see why you would do that. Just complicates your code for no reason.



  • I'm just looking for a clean way to handle a data structure. In all candor, both the memcpy_s and the std::array/std::copy approaches are tough on the eyes. I guess, though, that's the price you pay to avoid overrunning boundaries.


  • Qt Champions 2016

    I don't follow, perhaps I'm missing something. Suppose you use std::array. It'd look something like this:

    struct X
    {
        std::array<int, 4> a;
        std::array<double, 2> b;
    };
    

    You use it as any POD struct.

    X x = {
        { 1, 2, 3, 4 }, // This is an initializer list for std::array<int, 4>
        { 0.1, 12.3 }   // The second member's intialization
    };
    
    X y = x; // This is all, copying of the arrays is taken care for you by the STL and the compiler
    
    double z[2];
    std::memcpy(z, x.b.data(), x.b.size()); // and voila, we copied the data to `z`
    


  • I'm probably being too fussy, but given how easily one can assign/copy QByteArray and QString objects, it's a shame IMO that there's no way to aggregate them as in a C struct. But it's probably their very flexibility that makes such use unfeasible.

    It's OK...I'm content to use memcpy_s, but it's just not attractive code.



  • @Wieland said in Qt analog to C struct?:

    Yes, automatic range checking,

    You said this in response to why std::array should be used/preferred. What "automatic range checking" are you saying it provides, under what circumstances?


  • Qt Champions 2016

    @mzimmers said in Qt analog to C struct?:

    I'm probably being too fussy,

    Not really, but it would help to know what you dislike about the above example and why you think you need to use memcpy?

    @JNBarchan said in Qt analog to C struct?:

    What "automatic range checking" are you saying it provides, under what circumstances?

    std::array<int, 3> x;
    int z = x[3]; //< Regular arrays allow this (generally)
    x[3] = 0;     //< Regular arrays mostly allow this too
    

    e.g. run this through your debugger:

    int main(int argc, char ** argv)
    {
        int z[2];
        z[2] = 0;
    }
    


  • @kshegunov said in Qt analog to C struct?:

    @JNBarchan said in Qt analog to C struct?:
    What "automatic range checking" are you saying it provides, under what circumstances?

    std::array<int, 3> x;
    int z = x[3]; //< Regular arrays allow this (generally)
    x[3] = 0;     //< Regular arrays mostly allow this too
    

    I'm not understanding you. If you are saying with std::array<int, 3> x; then x[3] will be range checked, then I say it will not (that's why I asked)! Only x.at(3) will be. See docs. If your x[3] is range checked, that is a compiler-specific behaviour and/or you have to pass flags to compiler to ask it to do that.


  • Qt Champions 2016

    @JNBarchan said in Qt analog to C struct?:

    I say it will not

    Indeed, I'm in error.
    PS.
    My STL (g++ 7.2.x) doesn't even have an assertion there ... :|
    .at will throw an exception if out of bounds on the other hand.



  • @kshegunov said in Qt analog to C struct?:

    PS.
    My STL (g++ 7.2.x) doesn't even have an assertion there ... :|
    .at will throw an exception if out of bounds on the other hand.

    Yup! But some places suggest "Enable the flag _GLIBCXX_DEBUG to do bounds checking on STL containers", though others claim that was still not the case for std::array. Might depend on compiler release. Also see https://stackoverflow.com/a/1290488/489865 for how Visual C++ does have explicit range checking, and https://stackoverflow.com/a/8375312/489865 using __glibcxx_check_subscript(), which you might put into your own code without tampering with the supplied libraries!


  • Qt Champions 2016

    Well, as I mentioned in my first third post I'm not big on the new standards. Honestly, if it were me, I'd use the regular old statically sized arrays and leave the compiler to generate the copy constructor. Always has worked for me, and I don't see any good reason to use templates endlessly and unnecessarily, but that's me, the standards committee doesn't agree ...



  • @kshegunov
    Again, agree with you totally!



  • @kshegunov I'm using C structs and memcpy_s because in my mind it's simpler to use and understand than the C++ STL for arrays. For example:

    struct
    {
        ...
        uint8_t iv[16];
        ...
    }
    ...
    memcpy_s(msgHeader.iv, sizeof(msgHeader.iv), enc->getIv(), sizeof(msgHeader.iv));
    

    is admittedly unattractive, but it works. Consider the alternative:

    struct
    {
        ...
        std::array<uint8_t, 16> iv;
    }
    ...
    std::copy(enc->getIv().begin(), enc->getIv().end(), msgHeader.iv.begin());
    

    This doesn't work (it causes a memory problem that results in a segmentation fault). The routine getIv is of type QByteArray.


  • Qt Champions 2016

    @mzimmers said in Qt analog to C struct?:

    is admittedly unattractive, but it works.

    What is enc->getIv()? Is it how you read that part of the header from the device?
    You could for example do something like this:

    struct
    {
        ...
        uint8_t iv[16];
        ...
    } msgHeader;
    
    QByteArray iface = QByteArray::fromRawData(reinterpret_cast<const char *>(&msgHeader), sizeof(msgHeader));
    

    And then you can write and read directly into/from the byte array (on both sides when serializing/deserializing). This may require you to pack the structure however so you get sensible things from the reinterpret cast and it byte-order should be considered. Another thing, which is what I usually prefer is just to provide streaming from/to a data stream for the struct through
    the << and >> operators. Then it's cleaner as you don't have any memcopy stuff outside of the struct/relevant functions. E.g.:

    struct X
    {
        char a[10], b[5];
    };
    
    QDataStream & operator << (QDataStream & out, const X & x)
    {
        out.writeRawData(x.a, sizeof(X::a));
        out.writeRawData(x.b, sizeof(X::b));
        return out;
    }
    
    QDataStream & operator >> (QDataStream & in, X & x)
    {
        if (in.readRawData(x.a, sizeof(X::a)) != sizeof(X::a) || in.readRawData(x.b, sizeof(X::b)) != sizeof(X::b))
            in.setStatus(QDataStream::ReadCorruptData);
        return in;
    }
    

    In this case you can stream the whole structure in one go from the binary representation:

    QByteArray data; //< However this comes to you, if it's a raw C-array then attach a the byte array with `QByteArray::fromRawData`
    
    QDataStream in(&data, QIODevice::ReadOnly);
    
    X msgHandler;
    in >> msgHandler;
    
    if (in.status() != QDataStream::Ok)
        ; // Handle the error
    

    This is one possibility only, there are other options as well.



  • @kshegunov thanks for the detailed reply. Your post made me aware of two things I hadn't ever noticed before: the fromRawData method, and the reinterpret_cast.

    In answer to your question, getIv is just a convenience function to return a QByteArray containing a member object. The actual member is an array of 4 32-bit unsigned integers.

    QByteArray Encryption::getIv()
    {
        QByteArray a = QByteArray((char *) m_iv, sizeof(m_iv));
        return a;
    }
    

  • Qt Champions 2016

    The reinterpret_cast is generally unsafe, so I'd advise against using it, especially if you have not taken into account all relevant peculiarities of the memory layout (byte-order, alignment and padding). The second part is my recommended approach and what I do in my own programs - clean and efficient enough.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.