PDA

View Full Version : Problem with QDataStream operators



d_stranz
3rd September 2014, 00:59
I have a class derived from QMap<> which I am using to store an association between two string values. I am having trouble with the serialization using QDataStream operator<<() and operator>>(). Here is the complete code for the class:



// Header .h

#include <QMetaType>
#include <QMap>
#include <QString>
#include <QVariant>

class QDataStream;

class CClassColorMap
: public QMap< QString, QVariant >
{
public:
CClassColorMap();
CClassColorMap( const CClassColorMap & rhs );
virtual ~CClassColorMap();
};

Q_DECLARE_METATYPE( CClassColorMap );

QDataStream & operator<<( QDataStream & stream, const CClassColorMap & classColors );
QDataStream & operator>>( QDataStream & stream, CClassColorMap & classColors );


// Implementation .cpp

#include "ClassColorMap.h"
#include <QDataStream>

static CClassColorMap sClassColors;
static bool sbRegistered = false;

CClassColorMap::CClassColorMap(void)
: QMap< QString, QVariant >()
{
if ( !sbRegistered )
{
qRegisterMetaTypeStreamOperators< CClassColorMap >( "CClassColorMap" );
sbRegistered = true;
}
}

CClassColorMap::CClassColorMap( const CClassColorMap & rhs )
: QMap< QString, QVariant >()
{
if ( !sbRegistered )
{
qRegisterMetaTypeStreamOperators< CClassColorMap >( "CClassColorMap" );
sbRegistered = true;
}

*this = rhs;
}

CClassColorMap::~CClassColorMap(void)
{
}

QDataStream & operator<<( QDataStream & stream, const CClassColorMap & classColors )
{
return operator<<( stream, (const QMap< QString, QVariant > &) classColors );
}

QDataStream & operator>>( QDataStream & stream, CClassColorMap & classColors )
{
return operator>>( stream, (QMap< QString, QVariant > &) classColors );
}


I am using this for QSettings. The issue is that the QDataStream operators are never called, either when retrieving the QSettings instance or when storing it. I verified this by settings breakpoints in the dbugger.

I have other standalone custom classes (i.e. not derived from Qt base classes) that are used in the same QSettings, and that are declared and implemented in the identical fashion as the class above. The QDataStream operators are invoked just fine in those cases (again using the debugger to verify).

Can anyone see what is wrong with the definition above? Or suggest a reason why this isn't working? The only critical difference is the QMap<> base class, but I don't understand why this could cause a problem.

Update: I changed the class so it uses the QMap<> as a member variable rather than a base class. Now the stream operators are invoked. Why?

wysota
3rd September 2014, 08:10
I think this is because QDataStream already has an operator for serializing QMap which might come first before your specialized operator.

d_stranz
3rd September 2014, 20:06
I think this is because QDataStream already has an operator for serializing QMap which might come first before your specialized operator.

That is my conclusion as well. This means that if I derive a class from QMap (or some other Qt class with defined serializing operators) and add more features to it, it will be impossible for me to serialize it using QDataStream operators. I would have thought that C++ would resolve any ambiguity by taking the more specialized class (mine) over the less specialized one (QMap), but maybe the qRegisterMetaTypeStreamOperators() method is the problem and not C++ type resolution rules.

wysota
4th September 2014, 08:31
I think QMap is handled using a template function, maybe you should provide an explicit template specialization for this to work.

d_stranz
4th September 2014, 22:26
maybe you should provide an explicit template specialization for this to work.

Well, maybe, but the QMap<> base class for my CClassColorMap class is fully specialized. My QDataStream operators use my derived class, not QMap<>. Even if I provided a set of QDataStream operators for a fully specialized QMap<>, that still is not the same as the operators for my own class. If I had additional data members in my class that I wanted to serialize, operators for a fully specialized QMap<> wouldn't know about them.

I think this is a defect in the lookup system for the QDataStream operators used in QMetaType. I haven't tried this with classes derived from other Qt base classes (like QString, f.e.) but my hunch is the same thing would happen. I'm under deadline pressure and unfortunately don't have the time to investigate.

Edit: OK, I threw my deadline out the window and tested this using a class derived from QString. Everything I said above is completely wrong. At first, I thought that my string class QDataStream operators were not being called, so I changed the derived class to be a top level class with a QString member. The operators still weren't called. Huh?

Then I realized that on import if QSettings doesn't find a value with the key you are asking for, it doesn't call operator>>() because there is nothing to serialize. When I exited the program (and saved the QSettings back out to the file), the operator<<() was called. I changed my string class back to being derived from QString, and sure enough, it works now on import too, because now there's a key in the QSettings file.

I did the same for my QMap<> class - turned it back into a derived class of QMap<>, and it works now too. To make sure, I manually deleted all of the relevant entries from the QSettings file. On import, the operator>>() is not called, but on export operator<<() is called as it is supposed to be.

My bad. Jumped to a wrong conclusion and because I was doing everything in the debugger and exiting before the export to the QSettings file occurred, I assumed a defect.

I apologize for wasting everyone's time chasing a wrong conclusion.

wysota
5th September 2014, 06:46
At least we all learned something.