A small suggestion for improving Qt
-
Hello, All!
I want to suggest a small improvement to Qt. To do this, add two public virtual methods to the QObject class:
virtual void LoadFromStream( QDataStream &/ST/ ) {};
virtual void SaveToStream( QDataStream &/ST/ ) {};There are numerous examples in my program "Shrila Prabhupada's Dictionary"- https://github.com/Navadvipa-Chandra-das/PrabhupadaDictionaryQt . It's not quite finished yet, but it absolutely does not prevent me from demonstrating the things that I want to offer you to use!
The point is what? Let's say we have a descendant object of the QObject class. We configured it at will, then saved it either in memory, or on a hard disk file, or in a database - to choose from. Now we can destroy the object. Then we create a new object and read its state very, very simply - with one function call! I know that there is already a QSettings class to save settings in Qt. I suggest a Storage class with similar functionality, but still there are differences between them.
Disadvantages of QStorage:
- When changing the structure of settings, all previous settings are subject to deletion. This happens when updating versions of the program.
- It is quite difficult to edit the settings using external programs, such as a text editor and a database manager.
Disadvantages of QSettings :
- Can work only with files
- Stores all data in text format, event binary data.
- Requires to create a unique string key for each portion of settings.
- These disadvantages lead to slower loading and saving of settings. Moreover, as the size of the ini file grows, the speed of key-value search will fall all the time.
Advantages of QStorage:
- Better performance.
- No need to compose unique string key names for portions of settings.
- The ability to choose the storage location of settings - File, DB, Memory.
Of course, I would like to see these changes in future versions of Qt, so that QStorage classes and other necessary changes appear there. I haven't implemented this class as a library yet, but only as a local class in my program, but I want to do it, and knowledge of how to create libraries in Qt is not enough yet.
Thany You!
With best regards, Navadvipa Chandra das.
-
@Navadvipa-Chandra-das
Hi. This is a user forum --- users of Qt just like yourself. If you wish to suggest an "improvement" to Qt nothing will happen from here. You need to report it as a bug/suggestion over at https://bugreports.qt.io/, or perhaps the developers' mailing list. -
Hello Jon and All! Thank You!
With best regards, Navadvipa Chandra das. -
I have implemented the exact same thing:
struct ArchiverInterface { virtual void archiveWithStream(QDataStream& stream) const =0; virtual bool unArchiveWithStream(QDataStream& stream)=0; }; }
and an Archiver class to deal with.
In fact, it is a clone of the NSArchiver concept in Cocoa on Mac.
There was a discussion about this subject here:
https://forum.qt.io/topic/132485/how-to-save-and-load-a-qt-window-to-file?_=1690465618760 -
Hello @mpergand! Hello All!
Thanks!
I have created a special request-offer. Let's hope that the ice will move!
https://bugreports.qt.io/browse/QTBUG-115571
With best regards, Navadvipa Chandra das. -
Keep in mind that adding any virtual methods to QObject would destroy binary compatibility of pretty much entire library, so anything like that won't happen before Qt7.
Also, this adds serialization functionality to a fundamental class, that most of the time doesn't need it. It would have to provide at least the base implementation of serializing QObject itself, and handle versioning. It's a feature bloat, so I don't see it being added in the form you suggested. This should be, if at all, handled by external class of which sole purpose is serialization, so conceptually instead of
object.loadFromStream(stream)
something likeQObjectSerializer(object).loadFromStream(stream)
. -
Hello Chris and All!
I didn't even think about binary compatibility!
If I understood you correctly, then the Storage class should not serve all QObjects, but only a select few who are heirs of the QObjectSerializer. Well, it's not difficult to fix it. In this case,class QStorageMainWindow : virtual public QMainWindow virtual public QObjectSerializer
. We need to try whether the compiler will agree. In any case, if each object can be serialized, it seems to me not bad. The size of the VTable is increasing, but I have no idea how this will affect the performance of Qt.
Thank you very much for the answer!
With best regards, Navadvipa Chandra das. -
If I understood you correctly, then the Storage class should not serve all QObjects, but only a select few who are heirs of the QObjectSerializer
No, that's not what I meant. It wouldn't be of much use if it only supported selected classes inheriting from something.
class QStorageMainWindow : virtual public QMainWindow virtual public QObjectSerializer
That's still the same issue as before, just shifted up in the class hierarchy.
What I'm saying is serialization should not be part of a class that is being serialized. That's mixing concerns. Imagine you have 50 existing classes in your app and then want to serialize them - with your approach you have to modify code of 50 existing classes! That's not a good design.
What I'm suggesting is more like this (and keep in mind this is just loose idea, not a concrete implementation):
class MyClass { /* Knows nothing about serialization */ }; // Interface for serialization class ISerializer { virtual void serialize(QObject* obj, QDataStream& stream) = 0; virtual void deserialize(QObject* obj, QDataStream& stream) = 0; } // Implementation for MyClass class MyClassSerializer : public ISerializer { void serialize(QObject* obj, QDataStream& stream) override { ... }; void deserialize(QObject* obj, QDataStream& stream) override { ... }; }
Then you'd register this particular serializer for the specific meta type of that class. Could be a static method on the QObjectSerializer or some automatic way, but in any case the constructor
QObjectSerializer(object)
would look up the registered serializer for that specific type and use it. If it couldn't find serializer for the specific type it could go up the inheritance chain via meta object lookup and look for serializers for base classes, until it reached the QObject. A serializer for that could e.g. go through all Q_PROPERTies and serialize those.Adding support for new types would be noninvasive - it's just adding new serializer classes without touching the original classes or the QObjectSerializer. That's cleanly extendible and binary compatible.
-
Hello Chris and All!
I understood. Thanks! I agree that for version 6 of Qt it is better to have separate classes for serialization for binary compatibility. But for the seventh version of Qt - I do not agree. As a user, I want to be provided with a full-fledged class that can serialize itself. For example, when buying a car, I want the first-aid kit to be put inside the car, and not told that here is a car for you, and you will carry the first-aid kit on a special cart. In addition, you can not change the code of all 50 classes, but simply make 50 heirs. In this case, the parity is 50 heirs against 50 serializer classes. In addition, these 50 serializer classes will have to repeat the hierarchical tree of heirs, which in itself is tedious. And when the hierarchy of these 50 classes changes, then the tree of the 50 serializer classes will have to change, hurrying after them. It is a very tedious, thankless task to synchronize disparate pieces of code. In addition, this is a clear overspending of both memory and processor time. In any case - I can't make any decision - it will be made by other people. I remember there were object databases for C++ and C#. For C#, one such was once called FastObject. The bottom line is that any object can be saved in the database! They even had a slogan: "Stop thinking square!" Although they were based on quite a square idea - a table of pairs - key and value.
Thank you very much!
With best regards, Navadvipa Chandra das. -
@Navadvipa-Chandra-das As a counter-argument - I'm another user that has absolutely no use for such a functionality. I don't use databases. I don't serialize my QObjects. I have large number of classes that I want to keep as slim as possible due to memory constraints. Why do I have to pay in every single one of my QObject derived classes for a feature that you need? And if you need serialization and you want to stuff it in every class then I might want my feature there too, and someone else might want another... Someone might wan thread safety and a mutex in every method. Someone might want network replication that drags more dependencies in. Someone might want a printable version of objects for logging and debug purposes. This is a straight way to bloat on a class that is fundamental and already quite heavy. IMO in such large frameworks fundamental base objects should be as slim as possible and provide only the absolute minimum of functionalities.
-
Hello, Chris Kawa and All!
Well, I agree. Except that the LoadFromStream( QDataStream& ) and SaveToStream( QDataStream& ) functions themselves do not require a database. Databases already require the Storage class, so it can be moved to a separate library.class ISerializer : public QObject { public: static std::map< QMetaObject*, QMetaObject* > m_Map; static void Register( QMetaObject* AObject, QMetaObject* ASerializer ); public slots: virtual void LoadFromStream( QObject* obj, QDataStream& stream) = 0; virtual void SaveToStream( QObject* obj, QDataStream& stream) = 0;
ISerializer must be inherited from QOject in order to have the QMetaObject* metaObject() function. I haven't tried compiling anything yet, I'm just guessing. public slots: I think it is necessary for invokeMethod to work.
Then you need to do something like:
QMetaObject::invokeMethod( AObject
, "LoadFromStream"
, Qt::DirectConnection
, AStream );Is this supposed to work? I will try!
Thank You!
With best regards, Navadvipa Chandra das. -
This post is deleted!
-
Hello, Chris Kawa and All!
Thiago Macieira from Qt teams gave me the idea to use Qt metadata instead of a virtual function in class QObject! Now there is binary compatibility with Qt!
https://github.com/Navadvipa-Chandra-das/PrabhupadaDictionaryQt
The main change is that two virtual functions in the QObject base class have been removed! Now, the class that wants to be saved and restored using the Storage class must make two slots:public slots: void LoadFromStream( QDataStream &ST ); void SaveToStream( QDataStream &ST );
Meta-information is not inherited, and each class must still make these two slots, even if they will just call the ancestor method without adding any new code! But you can build an inheritance hierarchy and it works well. Probably the call is a little slower than calling a virtual function, but not the QObject class remains as fast as possible!
That's what happened:
O->LoadFromStream( *m_Stream );
And here's what happened now:
InvokeSuccess = QMetaObject::invokeMethod( O, "LoadFromStream", Q_ARG( QDataStream&, *m_Stream ) );
Now let's see what the Qt team says! Interesting!
Thank You!With best regards, Navadvipa Chandra dasa.