4

I am going to save a struct in a binary file and load it later on. I found that one way is to use QVariant. Here is a simplified Qt Widget Application example that I have created. But when I run it the binary file remains empty. Could you please help me with that. Also, Is there a better method to do such a thing?

mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QFile>
#include <QFileDialog>
#include <QDataStream>
#include <QString>

#include "mystruct.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
};
#endif

mainwindow.cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    /* create an object of the struct */
    myStruct * aStruct = new myStruct;
    aStruct->myStringVar = QString("aaaaa");

    /* put the object in QVariant */
    QVariant aVariant;
    aVariant.setValue(aStruct);

    /* save the QVariant to binary file */
    QFile myFile("myFile");
    QDataStream save(&myFile);
    save.setVersion(QDataStream::Qt_4_6);
    save << aVariant;
    myFile.close();

    /* load the QVariant from binary file */
    QDataStream load(&myFile);
    load.setVersion(QDataStream::Qt_4_6);
    QVariant theVariant;
    load >> theVariant;
    QString theVar = theVariant.value<myStruct*>()->myStringVar;
    myFile.close();
}

MainWindow::~MainWindow()
{
    delete ui;
}

mystruct.h:

#ifndef MYSTRUCT_H
#define MYSTRUCT_H

#include <QMetaType>
#include <QString>

struct myStruct
{
    QString myStringVar;
};

Q_DECLARE_METATYPE(myStruct*)

#endif // MYSTRUCT_H

Comment: As a reference, this link shows a similar problem, but I could not understand/implement the suggestions correctly. For example, I tried to define a new operator, as it is explained here, but it did not work.

eyllanesc
  • 190,383
  • 15
  • 87
  • 142
Keyvan
  • 73
  • 7
  • Always check IO operations for errors. Further debugging is pointless until you know there isn't any IO error. – hyde Sep 13 '18 at 04:36

2 Answers2

8

First of all you should not save the pointer since this is only the memory address of the variable, what should be saved is the value. On the other hand if you want to use QDataStream you must overwrite stream operators, finally if you want to save as QVariant you must use qRegisterMetaTypeStreamOperators. In addition to the above, your code has several errors such as not opening the QFile.

mystruct.h

#ifndef MYSTRUCT_H
#define MYSTRUCT_H

#include <QString>
#include <QMetaType>

struct myStruct
{
    QString myStringVar;
    bool operator==(const myStruct & o){
        return o.myStringVar == this->myStringVar;
    }
    friend QDataStream &operator<<(QDataStream &out, const myStruct &rhs){
        out << rhs.myStringVar;
        return out;
    }
    friend QDataStream &operator>>(QDataStream &in, myStruct &rhs){
        in >> rhs.myStringVar;
        return in;
    }
};
Q_DECLARE_METATYPE(myStruct)

#endif // MYSTRUCT_H

main.cpp

#include "mystruct.h"

#include <QDataStream>
#include <QFile>
#include <QVariant>

int main(int argc, char *argv[])
{
    qRegisterMetaTypeStreamOperators<myStruct>("myStruct");

    // create struct
    myStruct aStructIn{"aaa"};

    // myStruct to QVariant
    QVariant aVariant;
    aVariant.setValue(aStructIn);

    //open file
    QFile myFile("myFile");
    if(!myFile.open(QIODevice::WriteOnly))
        return -1;

    // save QVariant
    QDataStream save(&myFile);
    save.setVersion(QDataStream::Qt_4_6);
    save << aVariant;
    myFile.close();

    //open file
    if(!myFile.open(QIODevice::ReadOnly))
        return -1;

    // read QVariant
    QDataStream load(&myFile);
    load.setVersion(QDataStream::Qt_4_6);
    QVariant theVariant;
    load >> theVariant;
    myFile.close();

    // QVariant to myStruct
    myStruct aStructOut = theVariant.value<myStruct>();

    Q_ASSERT(aStructOut == aStructIn);

    return 0;
}
eyllanesc
  • 190,383
  • 15
  • 87
  • 142
  • Thanks. The answer works. But since the overload operators are taking `QDataStream` and `myStruct`, do I still need to use `QVariant` ? I removed `QVariant` and it worked. Can I define the overload operators between `QDataStream` and `QVariant`, so it can be general, not specific to a struct? – Keyvan Sep 13 '18 at 19:36
  • 1
    @K1-ZR Obviously, you can remove the QVariant part. There is no general method to serialize a structure since it will depend on what you want to save, besides you can only save copying things, for example you can not save a pointer. If my answer helps you, do not forget to mark it as correct, if you do not know how to do it, review the [tour], that is the best way to thank. – eyllanesc Sep 14 '18 at 00:08
2

Here is the example that worked for me after doing all the corrections. This example shows how I save/load a struct, or a class, into a file using QDataStream. The struct, i.e. myStruct, has three members: an int, a string, and a struct, i.e. mySubStruct.

  • For each struct/class, the << and >> operators need to be overloaded.
  • In each overloaded operator, save/load of the struct is accomplished by breaking up the struct into its primitive members. The primitive members are the types that are supported by QDataStream.

comment 1: The following example uses overloading the operators to a struct. To see how to overload the operators to a class see this link.

comment 2: This link is a complete example.

mysubstruct.h

#ifndef MYSUBSTRUCT_H
#define MYSUBSTRUCT_H

#include <QString>
#include <QDataStream>

struct mySubStruct
{
    int intVar;
    QString strVar;

    /* overload the operators */
    friend QDataStream &operator<< (QDataStream &out, const mySubStruct &rhs)
    {
        out << rhs.intVar << rhs.strVar;
        return out;
    }

    friend QDataStream &operator>> (QDataStream &in, mySubStruct &rhs)
    {
        in >> rhs.intVar >> rhs.strVar;
        return in;
    }
};
#endif // MYSUBSTRUCT_H

mystruct.h

#ifndef MYSTRUCT_H
#define MYSTRUCT_H

#include <QString>
#include <QDataStream>
#include "mysubstruct.h"

struct myStruct
{
    int intVar;
    QString strVar;
    mySubStruct subStructObj;

    /* overload the operators */
    friend QDataStream &operator<< (QDataStream &out, const myStruct &rhs)
    {
        out << rhs.intVar << rhs.strVar << rhs.subStructObj;
        return out;
    }

    friend QDataStream &operator>> (QDataStream &in, myStruct &rhs)
    {
        in >> rhs.intVar >> rhs.strVar >> rhs.subStructObj;
        return in;
    }
};
#endif // MYSTRUCT_H

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QFile>
#include <QString>
#include <QDebug>
#include "mystruct.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

mainwindow.cpp shows how the operators are implemented.

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    /* create struct */
    mySubStruct subStructIn;
    subStructIn.intVar = 1;
    subStructIn.strVar = "a";

    myStruct structIn;
    structIn.intVar = 11;
    structIn.strVar = "aa";
    structIn.subStructObj = subStructIn;

     /* open file */
     QFile myFile("myFile");

     /* save */
     if(myFile.open(QIODevice::WriteOnly))
     {
         QDataStream save(&myFile);
         save.setVersion(QDataStream::Qt_4_6);
         save << structIn;
         myFile.close();
     }

     /* load */
     myStruct structOut;
     if(myFile.open(QIODevice::ReadOnly))
     {
         QDataStream load(&myFile);
         load.setVersion(QDataStream::Qt_4_6);
         load >> structOut;
         myFile.close();
     }

     qDebug() << structOut.intVar;
     qDebug() << structOut.strVar;
     qDebug() << structOut.subStructObj.strVar;
     qDebug() << structOut.subStructObj.intVar;
}

MainWindow::~MainWindow()
{
    delete ui;
}
Keyvan
  • 73
  • 7