Register stream operator for containers
-
Hi all,
I'd like to be able to convert any kind of eastl::vector<T> with QVariant and serialize them.
- I've declared eastl::vector with as a container :
Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE
- I've implemented stream operator between std::vector and QDataStream :
template<typename T> QDataStream& operator>>(QDataStream& s, eastl::vector<T>& v) { v.clear(); size_t c; s >> c; v.reserve(c); for(int i = 0; i < c; ++i) { T t; s >> t; v.push_back( t); } return s; } template<typename T> QDataStream& operator<<(QDataStream& s, const eastl::vector<T>& v) { s << size_t(v.size()); for (eastl::vector<T>::const_iterator it = v.begin(); it != v.end(); ++it) s << *it; return s; }
- The last step is to register the stream operators to Qt, using :
qRegisterMetaTypeStreamOperators<MyClass>("MyClass");
But how can I write it with eastl::vector ??
I guess it should be the same as QVector does but couldn't find it in the source code.
Thanks by advance.
-
I'd like to be able to convert any kind of std::vector<T> with QVariant and serialize them.
I don't understand what exactly you're trying to do. Could you elaborate? Does your vector contain
QVariant
s, or do you want to putstd::vector
into aQVariant
, or something else?Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE
Vectors are not associative containers, but sequential ones.
The last step is to register the stream operators to Qt
You can do that for fully specialized templates (such that actually compile into code). Templates are just that - a "template" for the compiler to generate a class for you, without a specialization they don't generate anything tangible, i.e. they are not compiled.
I guess it should be the same as QVector does but couldn't find it in the source code.
The stream operators can be found here if that's what you were looking for.
Kind regards.
-
Thanks for the answer.
I edited my previous post because I use eastl::vector instead of std::vector and I effectively call Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE in my code.
Sorry for the misunderstanding.What I want is :
- To be able to store in QVariant several custom classes and the eastl::vector associated :
QVariant var1 = QVariant::fromValue<MyClass>(); QVariant var2 = QVariant::fromValue<eastl::vector<MyClass>>(); MyClass m = var1.value<MyClass>(); eastl::vector<MyClass> vm = var2.value<eastl::vector<MyClass>>();
- Be able to serialize both.
I successfully managed 1. but I was wondering how to register stream operator for eastl::vector.
By your answer, I understand that I have to explicitly declare all possible stream operators ?qRegisterMetaTypeStreamOperators<eastl::vector<MyClass1>>("eastl::vector<MyClass1>"); qRegisterMetaTypeStreamOperators<eastl::vector<MyClass2>>("eastl::vector<MyClass2>");
I've read qdatastream.h but I don't see where all stream operators are registered in case of QVector in the code.
Furthermore, do you know if there is a way to check at run time if a stream operator is declared for a given MetaType ?
Regards
-
To be able to store in QVariant several custom classes and the eastl::vector associated
Then
Q_DECLARE_METATYPE
andQ_DECLARE_SEQUENTIAL_CONTAINER_METATYPE
is just enough.QVariant
depends on the metatype declaration macros to do its work, not on the runtime registrations.Be able to serialize both.
That's a bit vague. Do you intend on using
QMetaType::load
andQMetaType::save
? For those you have to register both you classes and the container's stream operators with the runtime, e.g.:// Somewhere in the header(s) typedef eastl::vector<MyClass> MyClassVector; Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(eastl::vector) //< This goes in main, ordinarily qRegisterMetaType<MyClassVector>("MyClassVector"); qRegisterMetaTypeStreamOperators<MyClassVector>("MyClassVector");
I've read qdatastream.h but I don't see where all stream operators are registered in case of QVector in the code.
Probably aren't, as
QVector
is again a template and there couldn't be any presumption what specialization you use to register it with the runtime automatically.Furthermore, do you know if there is a way to check at run time if a stream operator is declared for a given MetaType
I don't think this is supported.
One thing, though, is that if you want to write your objects to a data stream (i.e. you want to serialize them), you don't really need the runtime registration. So, how/what are you trying to do exactly?
As said, this:eastl::vector<MyClass> vm = var2.value<eastl::vector<MyClass>>();
should work without doing
qRegisterMetaTypeStreamOperators
orqRegisterMetaType
at all.Kind regards.
-
Thanks for taking time to answer me. Again, I will clarify my purpose.
In my application I manage a map<QString, QVariant> (like QSettings) and I want to store my custom Class (+ eastl::vector<Class>) in this map.
When I write about "Be able to serialize both", I mean I want to be able to save / load the map in a binary file on the disk.
To do so, I convert each QVariant to/from QByteArray using QDataStream :// Method called when saving the map to the disk void FromVariantToByte(const QVariant & iVariant, QByteArray & oByteData) { QDataStream dataStream(&oByteData, QIODevice::WriteOnly); dataStream.setVersion(QDataStream::Qt_5_5); dataStream << iVariant; // Should work for QVariant storing MyClass and eastl::vector<MyClass> } // Method called when loading the map from the disk void FromByteToVariant(QByteArray & iByteData, QVariant & oVariant) { QDataStream dataStream(&iByteData, QIODevice::ReadOnly); dataStream.setVersion(QDataStream::Qt_5_5); dataStream >> oVariant; // Should work for QVariant storing MyClass and eastl::vector<MyClass> }
So yes, I intend to use QMetaType::save and QMetaType::load through QDataStream.
In my header, I have :
Q_DECLARE_METATYPE(MyClass1); Q_DECLARE_METATYPE(MyClass2); Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(eastl::vector);
In the .cpp
QDataStream& operator<<(QDataStream& out, const MyClass & c) { ... } QDataStream& operator>>(QDataStream& in, MyClass & c) { ... } template<typename T> QDataStream& operator>>(QDataStream& s, eastl::vector<T>& v){ ... } template<typename T> QDataStream& operator<<(QDataStream& s, const eastl::vector<T>& v){ ... } // This function is called at startup void RegisterOperators() { qRegisterMetaTypeStreamOperators<MyClass>("MyClass"); qRegisterMetaTypeStreamOperators<eastl::vector<MyClass>>("eastl::vector<MyClass>"); }
And it works. The stream operators are called correctly when FromVariantToByte / FromByToVariant are called (the downside is that I have to register all operators).
But then you lost me :-)
If I remove all registering from RegisterOperators() like you suggest, my operators are not called anymore, so how can itwork without doing qRegisterMetaTypeStreamOperators or qRegisterMetaType at all.
Either my code is wrong somewhere or I still don't understand how QVector works.
-
@NicolasS
Hello,Thanks for taking time to answer me.
You're welcome, that's kind of the reason I/we are lurking around ... ;)
Again, I will clarify my purpose.
Thanks, I now understand what you're trying to do.
If I remove all registering from RegisterOperators() like you suggest, my operators are not called anymore, so how can it
As it would be expected. Now what I meant is this:
eastl::vector<MyClass> somevector; QDataStream stream; stream << somevector;
Doesn't depend on the runtime registrations (
qRegisterMetaTypeStreamOperators
orqRegisterMetaType
), it directly invokes your operators. Since, however, you are storing your vector in a variant and then writing the variant, then the metatype system needs to associate that particular type with the corresponding stream operator (soQMetaType::load
andQMetaType::save
can work). Meaning this:eastl::vector<MyClass> somevector; QVariant<eastl::vector<MyClass>>myVariant = QVariant::fromValue(somevector); QDataStream stream; stream << myVariant;
does indeed need
qRegisterMetaTypeStreamOperators
.The template functions you use with variant, namely
QVariant::fromValue
andQVariant::value
don't care about the runtime (as they are template functions and are expanded at compile time), so to use them you need to haveQ_DECLARE_METATYPE
only.
And finally,Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE
is mostly for optimization, telling Qt that the data can be copied around withmemcpy
and is not necessary to do it element by element.I hope that clears it.
Kind regards. -
@NicolasS
No problem at all. And I'll just put another bit of comment here, just for anyone possibly stumbling later onto this thread.the downside is that I have to register all operators
This is simply how C++ works. As I said, templates have to be specialized to have code generated from them. Also there's the side effect that you can have a specialization for a specific type (different from the default template), which have to be accounted for:
template<typename T> QDataStream & operator << (QDataStream & s, const eastl::vector<T> & v) { // Some code for the unspecified eastl::vector<T> (T can be pretty much anything) } template<> QDataStream & operator << (QDataStream & s, const eastl::vector<int> & v) { // An implementation specifically tailored for eastl::vector<int> }
So that has to be accounted for and still not break
qRegisterMetaType
andqRegisterMetaTypeStreamOperators
. This would be one very good reason not to use a macro (for example) or some other type of auto-deduction magic for the runtime registration, but rather a template function as the ones put in Qt.Kind regards.