HOWTO make your app work with Qt 4.5
With Qt 4.5-rc1 recently released, we've stumbled over a strange issue with Quassel. A binary compiled against Qt 4.4 would completely freeze when used with Qt 4.5. This problem mostly hit users on binary distros trying out Qt 4.5. As it turned out, recompiling Quassel against Qt 4.5 magically fixed the problem - but of course, this is not an option for distro packagers, as they will have to provide packages built against 4.4 at least until 4.5 hits their distros proper.
So we hunted for that bug, to no avail at first, but then EgS stumbled over dug into the strange dungeons of his memory, and there found lurking a blog entry by one of the Trolls:
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().
We found one location in our code where we did this, and changed that:
- matches[i] = str.indexOf(regExp[i], qMax(matchEnd[i], idx));
+ matches[i] = regExp[i].indexIn(str, qMax(matchEnd[i], idx));
This seems to fix the issue, and Quassel binaries built against Qt 4.4 now reportedly work with 4.5.
What I don't get: I can see how "not modifying the QRegExp object" causes our code to hang here, and why the above makes it work again. But why the fringle does a mere recompile against Qt 4.5 fix that issue as well? Does the regexp magically become modifiable again? Luck? Karma? Voodoo? Anybody can help me out there? ;-)
- Sputnick's blog
- Login or register to post comments

Hi wow another helpful tip in
Hi wow another helpful tip in Qt 4.5 thanks!
I had the same problem before and it surely is helpful.
I was looking for a proper solution for a few days.
I am a bit disappointed with the other sites.
They gave too much complexity and thanks for sharing this.
I am really having a great time reading tips from this site.
Cudahy Ca Foreclosures | Ca Foreclosures 3.5% FHA Ca Foreclosures | DANVILLE Ca Foreclosures 100% VA DUBLIN Ca Foreclosures | GARDEN GROVE Ca Foreclosures Zero Down USDA LA PUENTE Ca Foreclosures | LIVERMORE Ca Foreclosures Half Percent Down MILPITAS Ca Foreclosures | MISSION VIEJO Ca Foreclosures 203 K FHA ORANGE Ca Foreclosures | PETALUMA Ca Foreclosures Conventional Loans REDONDO BEACH Ca Foreclosures | SAN LEANDRO Ca Foreclosures Jumbo Loans South Pasadena Ca Foreclosures | TURLOCK Ca Foreclosures City Down Payment TUSTIN Ca Foreclosures | WEST HOLLYWOOD Ca Foreclosures 2yr BK Discharge OK Aliso Viejo Ca Foreclosures | VA Mortgage Loan <620 FICO with No 12 mo lates VA Mortgage Loan | ARCADIA Ca Foreclosures 620+ FICO Ok DOWNEY Ca Foreclosures | FAIRFIELD Ca Foreclosures 6% Seller Credit OK FOLSOM Ca Foreclosures | LAGUNA NIGUEL Ca Foreclosures 30 Yr Fixed LOS BANOS Ca Foreclosures | MANTECA Ca Foreclosures 15 Yr Fixed MENLO PARK Ca Foreclosures | MORGAN HILL Ca Foreclosures 20 Yr Fixed NOVATO Ca Foreclosures | REDLANDS Ca Foreclosures 5 Yr Adjustable RICHMOND Ca Foreclosures | SAN RAFAEL Ca Foreclosures 7 Yr Adjustable UNION CITY Ca Foreclosures | WEST SACRAMENTO Ca Foreclosures 10 Yr Adjustable
Explaining the luck :-)
Okay, I was curious and digged around a bit to find the reason of all of this.
Original problem/bug in Qt 4.4.x
================================
First, there is a bug in Qt 4.4.3 in that a parameter passed as a const reference is changed. Look at this contrived example:
-------- cut here --------------
#include <QString>
#include <QRegExp>
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
QString s("Hello world!");
const QRegExp re("world");
cout << "re.matchedLength(): " << re.matchedLength() << endl;
const int ml = re.matchedLength();
s.indexOf(re); // Note that re is declared as constant!!
cout << "re.matchedLength(): " << re.matchedLength() << endl;
if (ml != re.matchedLength()) {
cout << "Bug in QString::indexOf: modifies const parameter." << endl;
}
return 0;
}
------ cut here -------
Running this program on my system (Debian unstable, Qt 4.4.3-2), I get:
torsten@transit:~/qtbug-modifies-const$ ./demo
re.matchedLength(): -1
re.matchedLength(): 5
Bug in QString::indexOf: modifies const parameter.
Which is obviously a bug if you ask me - passing an object as const reference should not make any visible changes to that object. It seems like Qt 4.5 fixed that, and in fact, the documentation at http://doc.trolltech.com/4.5/qstring.html#indexOf-4 shows that there is now a const- and a non-const variant of indexOf.
Why this broke Quassel
======================
Now, let's look at Quassels code or rather the diff that fixed the hang. The bug of the original code is in the line marked with !!BUG!!:
X-Git-Url: http://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fqtui%...
diff --git a/src/qtui/chatitem.cpp b/src/qtui/chatitem.cpp
index de96b2d..934fadf 100644
--- a/src/qtui/chatitem.cpp
+++ b/src/qtui/chatitem.cpp
@@ -445,7 +445,7 @@ QList ContentsChatItem::findClickables() const {
for(int i = 0; i < regExpCount; i++) {
if(matches[i] < 0 || matchEnd[i] > str.length()) continue;
if(idx >= matchEnd[i]) {
- matches[i] = str.indexOf(regExp[i], qMax(matchEnd[i], idx));
+ matches[i] = regExp[i].indexIn(str, qMax(matchEnd[i], idx));
if(matches[i] >= 0) matchEnd[i] = matches[i] + regExp[i].cap(1).length(); // !!BUG!!
}
if(matches[i] >= 0 && matches[i] < minidx) {
The problem: The marked line of code uses the state of regExp[i] to access the result of the str.indexOf operation. However, str.indexOf takes the regExp[i] argument as a const reference, so regExp[i].cap(1) can't have that result. If it actually not documented what QRegExp::cap() will return when no match data was acquired, on my system it is the empty string.
If any match is found, this will lead to the loop to find the same match again and again. This is because it continues searching at
457 idx = matchEnd[type];
which unfortunately is assigned
449 if(matches[i] >= 0) matchEnd[i] = matches[i] + regExp[i].cap(1).length();
or, in other words: matchEnd[i] = matches[i]. So the next search will find the same match again, ...
Calling regExp[i].indexIn() looks just as bad, as it is a const method. But even the Qt 4.5 documentation states that this updates the match information anyway (yuck!).
Why recompiling helps
=====================
This problem magically goes away when recompiling with Qt 4.5. Why?
Actually it is quite simple. The relevant source line is
448 matches[i] = str.indexOf(regExp[i], qMax(matchEnd[i], idx));
Building against Qt 4.4.x, the method QString::indexOf in this call will get resolved to the overload
int QString::indexOf(const QRegExp & rx, int from = 0) const;
This method will change rx in Qt 4.4.x and got fixed in 4.5 to not mess with the state of rx. Now, rebuilding against Qt 4.5, there are two candidates of indexOf with a QRegExp as first argument:
(1) int QString::indexOf(const QRegExp & rx, int from = 0) const;
(2) int QString::indexOf(QRegExp & rx, int from = 0) const;
This time the compiler will chose the second implementation, as in the context of line 448 of chatitem.cpp, the QRegExp argument is writable. The only difference of overload (2) vs. (1) is (quoting the Qt 4.5 docs):
"If there is a match, the rx regular expression will contain the matched captures (see QRegExp::matchedLength, QRegExp::cap)."
So the code originally worked "by accident" due to a bug in Qt.
Thanks a lot for finding this
Thanks a lot for finding this out; I wasn't aware of the new non-const indexOf(). This indeed explains why a recompile helped.