1. A trivial bug
It started with a pretty simple piece of code:
QDomElement e = n.toElement(); // try to convert the node to an element.
if(!e.isNull())
{
TRACE(e.tagName());
....
where
TRACE() sends an object to
cout. The simplest of tasks you'd say, but it didn't compile. What? What year is it now? Are we in the early nineties? Doesn't library writers know about the standard library? It turned out, they know, so I used a slightly modified code:
TRACE(e.tagName().toStdString());
but it always crashed with Qt 4.3, Windows XP and VisualStudio 2005! Why? No time to check. After some reading of Qt docs, I settled with:
TRACE(e.tagName().toAscii().data());
It worked, but the code looked
extremely ugly! After copy-pasting it for
n times (no,
n wasn't equal to 3, as it should be according to the Agile gospel, sorry!!!) I longed for something more elegant and explicit, something like:
TRACE(qstr_cast<const char*>(e.tagName()))
The code was quickly written and worked instantly:
// helper for QString conversions
// -- cute and explicit syntax: qstr_cast<const char*>()!
template <class T>
inline T qstr_cast(const QString& s)
{
return T(s.toAscii().data());
}
inline const char* qstr_cast(const QString& s)
{
return s.toAscii().data();
}
Well, it worked on my machine but not in the target environment, and only in one (different) case. Why? Of course I blamed the Qt
runtime support, which already let me down with the
toStdString() function. Then I saw the same effect in the debugger on my
developemnt machine, and I blamed it on
multithreading: there must be something wrong with locking when accessing this particular
QString instance. But at last I found time to remove this bug, and looked at the
sychronisation, and it was 100% correct. The bug was hidden elsewhere. The new (correct) code is:
// helper for QString conversions
// -- cute and explicit syntax: qstr_cast<const char*>()!
template <class T>
inline QByteArray qstr_cast(const QString& s, T* t = 0)
{
// the QByteArray's const char* conversion op. will be applied
// by the compiler in the const char* context!
return s.toAscii();
}
As you can see, the old code returned a pointer to the data of a temporary instance of an
QByteArray object, and of course it pointed into the void. End of story!
2. Discussion of the trivial bug
Why am I writing this? Isn't it just a banal and stupid bug? I don't think so. I think there are some points to be made.
The first one is that Qt doesn't follow the
"Principle of Least Surprise"*. This code should work out of the box:
cout << qtStringObj;! Why? Because C++ programmers wrote code like this for centuries! Well, almost. In the "freedom languages" ;-)** you are accustomed to writing just:
print someObj; and it always works. Mind I didn't use the word "modern languages" but in modern times we expect it just to be working!
The second one is that Qt doesn't want me to use the standard library! There is no shift operator for
std::ostream instead they want me to use their
QStream class, which I don't know and have no desire to learn. As for
QString class the Qt partisans maintain that it is vastly superior to the
std::string class, but what about
cout? It is somehow an C++ keyword by now. And besides, why aren't I allowed to make my own choices, even if I choose to use an inferior alternative? Maybe I don't have to be that fast in my prototype implementation? You somehow feel trapped in the proprietary Qt world, and somehow cast back in time. Is that the backwards
compatibility with the original 80-ties or 90-ties design? It feels somehow frumpy.
The third one is that once I decided on the syntactic appearance of the function (i.e.
qstr_cast<>() and not for example
cstr_ptr() or
CSTR()), I subconsciously settled on a implementation: just get the internal string data and export the pointer to the char buffer. Alas, in case of the
QString this doesn't work that way! Moreover, it's not only the name choice alone, this code would work with all the string classes I knew in my long C++ life. So maybe I chose the name because
subconsciously I already knew how to implement it? We pride
ourself on being rational beings,
but we don't know how much we depend on subconscious shortcuts, which will normally work in a familiar territory, but fail when trying something new. Just like me, as I'm relatively new to Qt.
And what's
the moral? It's elementary dear Watson: RTFM first! Or perhaps: don't mix Qt and
STL???
---
* for example
"Applying the Rule of Least Surprise" from
"The Art of Unix Programming" by E.S. Raymond:
http://www.faqs.org/docs/artu/ch11s01.html or
"Principle of Least Astonishment" at Portland Pattern Repository:
http://c2.com/cgi/wiki?PrincipleOfLeastAstonishment** I thought it was a joke, but not, it's an essay by Kevin Barnes:
http://codecraft.info/index.php/archives/20/