For some time now we’ve had an example as part of the Qt package which would show how easy it is to create and use a syntax highlighter in a QTextEdit. Great! I hear you say, so whats the problem? Well unfortunately this was actually relying what is at best questionable behaviour in QString::indexOf(). The relevant code is snipped below.
commentEndExpression = QRegExp("\\*/");
...
while (startIndex >= 0) {
int endIndex = text.indexOf(commentEndExpression, startIndex);
int commentLength;
if (endIndex == -1) {
setCurrentBlockState(1);
commentLength = text.length() - startIndex;
} else {
commentLength = endIndex - startIndex + commentEndExpression.matchedLength();
}
setFormat(startIndex, commentLength, multiLineCommentFormat);
startIndex = text.indexOf(commentStartExpression, startIndex + commentLength);
}
As you can see QString::indexOf() is taking a QRegExp as an object, and then later querying it for the matchedLength() if it found a match inside the string. Now this looks safe when you look at it like this, but if you take a look at the documentation for this at
http://doc.trolltech.com/4.4/qstring.html#indexOf-3 then you will notice that the QRegExp object passed in is actually a const object. Therefore
QString::indexOf() was actually causing the modification of a const object, this happened because it was calling QRegExp::indexIn() on the QRegExp which is actually documented to modify the QRegExp object despite the function being a const function itself.
So what does this mean for you if you used this and you are now going to use Qt 4.5? Well if you leave the code as it is then you will either get a crash in your code or it will simply cause the application to hang because the QRegExp object passed into QString::indexOf() is no longer modified. The good news is that there is a solution which you can use now even if you are not switching to Qt 4.5 straight away. What you should do is change the calls from QString::indexOf() to QRegExp::indexIn(). Everything else can stay the same, so the example above will just change from:
int endIndex = text.indexOf(commentEndExpression, startIndex);
to
int endIndex = commentEndExpression.indexIn(text, startIndex);
and you won’t experience any changes in the functionality. There will be a FAQ put up about this as well so hopefully the Qt Software Support Team will avoid having to answer this question a lot
Possibly related posts:
5 comments
Does this have to do with TaskTracker issue 232828?
I didn’t notice until you pointed it out, but QRegExp is a weird beastie. It changes its state significantly when used for matching. The Principle of Least Astonishment suggests that state should not change when comparing, matching, searching, etc. Imagine QStrings remembering the result of their last compare().
Ralf: Indeed it does, this is basically referring to the change that is intentional, matchedLength() originally was returning the actual length because the QRegExp was modified. However since its not modified anymore, it will not necessarily return the right value.
Thanks for this post. But what happens if you use “int QStringList::indexOf ( const QRegExp & rx, int from = 0 ) const”? Will this still work in Qt 4.5 or will it break similarly as QString::indexOf()?
I hope this insight means we’ll finally get a QRexExp::MatchResult object in Qt 5, instead of (as David put so nicely) the QRegExp “weird beastie” it currently is?
Comments on this entry are closed.