I’ve been in computers too long, whenever someone talks to me about error codes I think about the very old MacOs releases, or even longer ago the Amiga crash codes.
But I’m afraid you see them still, a dialog giving a very helpful “sorry, error 1234 happened”.
Now, anyone that is a coder knows how difficult it is to handle each and every error with a properly written error message. Preferrably one that actually makes sense to the user. And realistically speaking this tends not to happen. But before now the effect was typically to have useless messages like the above, or use some hacks that might get out of date on refactorings.
If you look at Qt there are several classes that have an error state; QNetworkReply is one of them. This error state is an enum and in C++ enums really are just integers.
In Qt we allow an enum to be more than just a set of integers using the Q_ENUMS macro. The direct benefit for you is that you can use introspection (/reflection) to get the string value of an enum.
So, no more excuses for just printing an integer error message, you can at least print the more readable enum value. Which means that technical people that don’t have the API docs for Qt handy can still figure out what went wrong.
Here is the code to do this;
QNetworkReply::NetworkError error;
error = fetchStuff();
if (error != QNetworkReply::NoError) {
QString errorValue;
QMetaObject meta = QNetworkReply::staticMetaObject;
for (int i=0; i < meta.enumeratorCount(); ++i) {
QMetaEnum m = meta.enumerator(i);
if (m.name() == QLatin1String("NetworkError")) {
errorValue = QLatin1String(m.valueToKey(error));
break;
}
}
QMessageBox box(QMessageBox::Information, "Failed to fetch",
"Fetching stuff failed with error '%1`").arg(errorValue),
QMessageBox::Ok);
box.exec();
return 1;
}
In Qt every class that has the Q_OBJECT macro will automatically have a static member “staticMetaObject” of the type QMetaObject. You can then find all sorts of cool things like the properties, signals, slots and indeed enums.
Happy hacking
16 Responses to “Coding tip; pretty-printing enum values.”
Looks like a templated general purpose function is possible to me.
qErrorMessage( code );
Would be splendid.
Nice. Now, if you used exceptions… ![]()
This might still get out of date on refactorings, e.g. if the metadata isnt called “NetworkError” anymore. Perhaps it could be more general by giving objects an error state in which case an error message meta data are attached.
Small improvement suggestion: http://doc.trolltech.com/4.4/qmetaobject.html#indexOfEnumerator
![]()
“In Qt every class that has the Q_OBJECT macro will automatically have a static member “staticMetaObject” of the type QMetaObject.”
More precisely, every _derived_ class that has the Q_OBJECT macro (plus the classes Qt and QObject) will have staticMetaObject. moc aborts with an error if a base class has the Q_OBJECT macro. But for this case one can use the still undocumented (even though something else was promised at last year’s Akademy *sigh*) Q_GADGET macro. Unfortunately, there is a bug in moc which makes it impossible to have a base class with Q_GADGET macro but without enum and a derived class with Q_GADGET macro but with enum because moc will not generate the staticMetaObject for the base class which will cause a linker error (missing symbol BaseClass::staticMetaObject). (Yes, I have submitted a bug report for this a long time ago.)
The rather trivial patch to fix this bug in moc is:
Index: src/tools/moc/moc.cpp
===================================================================
— src/tools/moc/moc.cpp (revision 869687)
+++ src/tools/moc/moc.cpp (working copy)
@@ -683,7 +683,7 @@
next(RBRACE);
- if (!def.hasQObject && def.signalList.isEmpty() && def.slotList.isEmpty()
+ if (!def.hasQObject && !def.hasQGadget && def.signalList.isEmpty() && def.slotList.isEmpty()
&& def.propertyList.isEmpty() && def.enumDeclarations.isEmpty())
continue; // no meta object code required
Maybe it will be applied now. ![]()
Mmh, I’d like to use something similar to sender() in my slotLogMessage(const QString &message) to prefix each message with its source. Is it somehow possible to get a string of the ORIGIN of a signal that was passed thorugh multiple classes? Like, the name of the class that first called emit?
Of course a simple pragmatic solution to the problem would be to return the error codes as a string instead of an integer in the first place.
too bad this can’t be done for QEvent::Type.
This simple macro should rid you of the copy&paste:
#define ENUM_NAME(o,e,v) (o::staticMetaObject.enumerator(o::staticMetaObject.indexOfEnumerator(#e)).valueToKey((v)))
Use as follows:
QNetworkReply::NetworkError error;
error = fetchStuff();
if (error != QNetworkReply::NoError) {
QString errorValue = QLatin1String(ENUM_VALUE(QNetworkReply, NetworkError, error));
QMessageBox box(QMessageBox::Information, “Failed to fetch”,
“Fetching stuff failed with error ‘%1`”).arg(errorValue),
QMessageBox::Ok);
box.exec();
return 1;
}
Oops, of course I mean QLatin1String(ENUM_NAME(QNetworkReply, NetworkError, error)); there. And it ate my indentation. ![]()
This was part of Simons presentation on “Secrets Of Qt” during the 2007 developer days. You can find the transcript here: http://labs.trolltech.com/page/Projects/DevDays/DevDays2007
\Ralf
@Cornelius: I can think of a few problems with that.
1) It would cause the string to be copied whenever the value is transfered between pieces of code. It’s faster to copy an integer.
2) It’s faster to compare an integer with an integer than a string with a string. Useful in error-handling code.
3) Enumerators get their own type, so you get more compile-time checking.
@Cornelius, Michael: Don’t forget the fact that human-visible strings could be localized!
This won’t help with displaying localized messages to the user, unless enum strings are marked for translation.
@Felix, et al: Marking the enum strings for translation is actually a really good idea; even if the enum strings themselves are in English you can use Linguist to fill in “localized” English “translations” of those strings for display purposes. (Similarly you can use Linguist to intelligently deal with plurals in the code’s “native” language. It’s a nice trick.)