[Qt-interest] Will there be an end of wrong QVariant behavior?

Maximilian Hrabowski hrabowski at clausmark.com
Thu Nov 12 16:01:08 CET 2009


Hi all,

up to now it has been more than once that I reported failures QVariant core functionality.
All the time I got the very same unsatisfying answer: Sorry, we can't fix this as it make break already existing code.

Here a few bugs as example:
http://bugreports.qt.nokia.com/browse/QTBUG-1413
http://bugreports.qt.nokia.com/browse/QTBUG-4053
http://bugreports.qt.nokia.com/browse/QTBUG-1583

Since Trolltech/Nokia has always answered that they won't change the behavior of such a core class like QVariant since it will make existing code cease to work I wonder who actually has code that relies on this wrong behavior.

Please look below for a detailed explanation of the isNull/isValid issue that creates most problems for me.

In my opinion (at least when it comes to the isNull/isValid functionality as explained below) code that relies on this misbehavior only works by pure luck. Until now I have a real hell of type dependent QVariant workarounds in my code and really can't believe that nobody has similar problems or that some people even rely on this misbehavior.

What do you think about this? Does anyone experience similar problems? Who really relies on this misbehavior?

I don't want to blame anyone, I just want know how true the statement "it will break existing code" is for Qt customers.
As a software engineer I really understand the fear of changing core classes but in this case I think that this is way to conservative.

I would also be very interested in some Troll's opinions about that topic - I'm sure there have been discussions...

Please comment.

--------- DETAIL

Here is the background about what I'm complaining most:

When mentioning QVariant's core functionality I have basically the following features in mind:
(1) A QVariant has a type (isValid == true)
(2) A QVariant can be NULL i.e. must not contain a value (isNull == true)
(3) A QVariant that does not has a type (isValid == false) is always NULL (if isValid == false => isNull == true)
(4) A QVariant that has a type can be NULL if no value is set (isValid == true && isNull == true)
(5) A QVariant can be extended to hold custom types using the meta system
All these features are documented as is in Qt Assistant.
There are some other problems regarding comparison that are also very annoying but they are easy to work around.

The main problem is that QVariant does not simply behave wrong - but even worse sometimes (i.e. for some types) it behaves correct and sometimes not.
This is a nightmare to maintain and test and since we're using the QMetaType and QVariant throughout our applications this has already cost much effort and tedious debug sessions since there really is a point to decide between NULL and 0 - especially with DBMS related code.

Attached to this message you can find a simple code example and the output that demonstrates the isNull/isValid problem.
It uses the two c'tors
(a) QVariant( QVariant::Type ) and
(b) QVariant( int typeOrUserType, const void* )
to create NULL instances for each built in type and just dumps the isNull and isValid state for both instances.

You can see that from the output that (a) works quite well and gives expected results for most simple built in types but not for Size and SizeF. For those two types it seems to be not even possible to create a valid NULL variant. Also for most complex types like e.g. QFont or QRegExp isNull returns true but isValid returns false - so the type information is lost.  (b) does not work as expected for the built in types but it does work for user types. For QVariant::Color it even crashes.

To summarize:
- QVariant works different depending on the type it holds
- Sometimes there exists a(n easy) workaround and sometimes not (e.g. Size/SizeF)

Please comment.

Cheers,

Maximilian


----------------- CODE
#include <QtCore/QCoreApplication> 
#include <QVariant> 
#include <QDebug> 


int main(int argc, char *argv[]) 
{ 
QCoreApplication a(argc, argv); 


for( int i=QVariant::Invalid; i<QVariant::UserType; i++) 
{ 

QVariant::Type type = QVariant::Type(i); 

QString name = QString::fromAscii(QVariant::typeToName(type)); 
if( !name.isEmpty() ) 
{ 
QVariant v1 = QVariant(type); 
const void* copy = 0; 
QVariant v2 = QVariant(i, copy); 

QString str = "%6: %1 %2==%3 %4==%5"; 

str = str 
.arg(name,-20,' ') 
.arg(QString(v1.isNull() ? "null" : "not null"),10,' ') 
.arg(QString(v2.isNull() ? "null" : "not null"),-10,' ') 
.arg(QString(v1.isValid() ? "valid" : "not valid"),10,' ') 
.arg(QString(v2.isValid() ? "valid" : "not valid"),-10,' ') 
.arg(QString::number(i),2,' '); 

qDebug() << str << " - " << v1 << "==" << v2; 
} 
} 
return 0; 
} 
----------------- EXAMPLE OUTPUT Qt 4.5.2
" 1: bool null==not null valid==valid " - QVariant(bool, false) == QVariant(bool, false) 
" 2: int null==not null valid==valid " - QVariant(int, 0) == QVariant(int, 0) 
" 3: uint null==not null valid==valid " - QVariant(uint, 0) == QVariant(uint, 0) 
" 4: qlonglong null==not null valid==valid " - QVariant(qlonglong, 0) == QVariant(qlonglong, 0) 
" 5: qulonglong null==not null valid==valid " - QVariant(qulonglong, 0) == QVariant(qulonglong, 0) 
" 6: double null==not null valid==valid " - QVariant(double, 0) == QVariant(double, 0) 
" 7: QChar null==null valid==valid " - QVariant(QChar, ' 
" 8: QVariantMap null==not null valid==valid " - QVariant(QVariantMap, QMap() ) == QVariant(QVariantMap, QMap() ) 
" 9: QVariantList null==not null valid==valid " - QVariant(QVariantList, () ) == QVariant(QVariantList, () ) 
"10: QString null==null valid==valid " - QVariant(QString, "") == QVariant(QString, "") 
"11: QStringList null==not null valid==valid " - QVariant(QStringList, () ) == QVariant(QStringList, () ) 
"12: QByteArray null==null valid==valid " - QVariant(QByteArray, "") == QVariant(QByteArray, "") 
"13: QBitArray null==null valid==valid " - QVariant(QBitArray, ) == QVariant(QBitArray, ) 
"14: QDate null==null valid==valid " - QVariant(QDate, QDate("") ) == QVariant(QDate, QDate("") ) 
"15: QTime null==null valid==valid " - QVariant(QTime, QTime("") ) == QVariant(QTime, QTime("") ) 
"16: QDateTime null==null valid==valid " - QVariant(QDateTime, QDateTime("") ) == QVariant(QDateTime, QDateTime("") ) 
"17: QUrl null==not null valid==valid " - QVariant(QUrl, QUrl("") ) == QVariant(QUrl, QUrl("") ) 
"18: QLocale null==not null valid==valid " - QVariant(QLocale, ) == QVariant(QLocale, ) 
"19: QRect null==null valid==valid " - QVariant(QRect, QRect(0,0 0x0) ) == QVariant(QRect, QRect(0,0 0x0) ) 
"20: QRectF null==null valid==valid " - QVariant(QRectF, QRectF(0,0 0x0) ) == QVariant(QRectF, QRectF(0,0 0x0) ) 
"21: QSize not null==not null valid==valid " - QVariant(QSize, QSize(-1, -1) ) == QVariant(QSize, QSize(-1, -1) ) 
"22: QSizeF not null==not null valid==valid " - QVariant(QSizeF, QSizeF(-1, -1) ) == QVariant(QSizeF, QSizeF(-1, -1) ) 
"23: QLine null==null valid==valid " - QVariant(QLine, QLine(QPoint(0,0) , QPoint(0,0) ) ) == QVariant(QLine, QLine(QPoint(0,0) , QPoint(0,0) ) ) 
"24: QLineF null==null valid==valid " - QVariant(QLineF, QLineF(QPointF(0, 0),QPointF(0, 0))) == QVariant(QLineF, QLineF(QPointF(0, 0),QPointF(0, 0))) 
"25: QPoint null==null valid==valid " - QVariant(QPoint, QPoint(0,0) ) == QVariant(QPoint, QPoint(0,0) ) 
"26: QPointF null==null valid==valid " - QVariant(QPointF, QPointF(0, 0)) == QVariant(QPointF, QPointF(0, 0)) 
"27: QRegExp null==not null valid==valid " - QVariant(QRegExp, ) == QVariant(QRegExp, ) 
"28: QVariantHash null==not null valid==valid " - QVariant(QVariantHash, QHash() ) == QVariant(QVariantHash, QHash() ) 
"63: QColorGroup null==not null not valid==not valid " - QVariant(, ) == QVariant(, ) 
"64: QFont null==not null not valid==not valid " - QVariant(, ) == QVariant(, ) 
"65: QPixmap null==not null not valid==not valid " - QVariant(, ) == QVariant(, ) 
"66: QBrush null==not null not valid==not valid " - QVariant(, ) == QVariant(, ) 
"67: QColor null==not null not valid==not valid " - QVariant(, ) == QVariant(, ) 
"68: QPalette null==not null not valid==not valid " - QVariant(, ) == QVariant(, ) 
"69: QIcon null==not null not valid==not valid " - QVariant(, ) == QVariant(, ) 
"70: QImage null==not null not valid==not valid " - QVariant(, ) == QVariant(, ) 
"71: QPolygon null==not null not valid==not valid " - QVariant(, ) == QVariant(, ) 
"72: QRegion null==not null not valid==not valid " - QVariant(, ) == QVariant(, ) 
"73: QBitmap null==not null not valid==not valid " - QVariant(, ) == QVariant(, ) 
"74: QCursor null==not null not valid==not valid " - QVariant(, ) == QVariant(, ) 
"75: QSizePolicy null==not null not valid==not valid " - QVariant(, ) == QVariant(, ) 
"76: QKeySequence null==not null not valid==not valid " - QVariant(, ) == QVariant(, ) 
"77: QPen null==not null not valid==not valid " - QVariant(, ) == QVariant(, ) 
"78: QTextLength null==not null not valid==not valid " - QVariant(, ) == QVariant(, ) 
"79: QTextFormat null==not null not valid==not valid " - QVariant(, ) == QVariant(, ) 
"80: QMatrix null==not null not valid==not valid " - QVariant(, ) == QVariant(, ) 
"81: QTransform null==not null not valid==not valid " - QVariant(, ) == QVariant(, )
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.qt-project.org/pipermail/qt-interest-old/attachments/20091112/509ce7a0/attachment.html 


More information about the Qt-interest-old mailing list