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.

    1. I've declared eastl::vector with as a container :
    Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE
    
    1. 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;
    }
    
    1. 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.


  • Qt Champions 2016

    @NicolasS

    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 QVariants, or do you want to put std::vector into a QVariant, 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 :

    1. 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>>();
    
    1. 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


  • Qt Champions 2016

    @NicolasS

    To be able to store in QVariant several custom classes and the eastl::vector associated

    Then Q_DECLARE_METATYPE and Q_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 and QMetaType::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 or qRegisterMetaType 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 it

    work without doing qRegisterMetaTypeStreamOperators or qRegisterMetaType at all.

    Either my code is wrong somewhere or I still don't understand how QVector works.


  • Qt Champions 2016

    @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 or qRegisterMetaType), 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 (so QMetaType::load and QMetaType::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 and QVariant::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 have Q_DECLARE_METATYPE only.
    And finally, Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE is mostly for optimization, telling Qt that the data can be copied around with memcpy and is not necessary to do it element by element.

    I hope that clears it.
    Kind regards.



  • Very clear ! Thanks a lot.


  • Qt Champions 2016

    @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 and qRegisterMetaTypeStreamOperators. 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.


Log in to reply
 

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