Home > c/c++, qt, software > Displaying Enum-Names instead of Values in Errors

Displaying Enum-Names instead of Values in Errors

The usual lazy developer likes to print out plain error messages, which contain pure numbers and don’t mean anything. The most prominent example is Windows itself, back in the old days.

Some time ago I was once again faced with the decision how to handle errors in our plug-able class design. While trying to find a good solution I remembered an old blog post – Qt is able to create a mapping from enum integer values to their respective names. So I fiddled around a bit and came up with a pretty neat solution, which requires virtually no extra code to gain a little more meaningful error messages than plain integer values.

Here is what I came up with:

You need to sub-class an error base class, insert your error enumeration, and tell Qt that you need the value-to-name mapping for this enum. All the extras come with the base class.
If you don’t like the rest of the class, you might just want to check out BaseError::identifierToName(). That’s where the magic is happening.

A usage example:

 // --- header file ---
 class HorseError : public BaseError
 {
     Q_OBJECT
     Q_ENUMS(x)
 public:
     enum x { DOES_NOT_WANT, IS_INJURED };
 };
 
 class Horse
 {
 public:
     HorseError error;
 
     void ride();
 };
 
 // --- source file ---
 void Horse::ride()
 {
     error.activate(HorseError::DOES_NOT_WANT, "cannot happen anyways");
 }
 
 // --- using ---
 
 Horse mybeautifulhorse;
 mybeautifulhorse.ride()
 
 // operator() returns a bool
 if (mybeautifulhorse.error()) {
    // operator QString makes the error readable
    qDebug() < < mybeautifulhorse.error;
 
    exit(1);
 }

The Header File – BaseError.hpp

#ifndef BASE_ERROR_HPP
#define BASE_ERROR_HPP
 
class BaseError : private QObject                                                                                                         
{                                                                                                                                         
    Q_OBJECT
public:
 
    BaseError();
    virtual ~BaseError();
 
    /** @returns whether the error is active */
    bool active() const;                       
    /** @returns the identifier (an enum) */   
    int identifier() const;                    
    /** @returns the name of the identifier enum e.g. "MyErrorClass::MyReturnValue" */
    QString name() const;                                                             
    /** @returns the description given by activate's second parameter */              
    const QString& description() const;                                               
 
    /** throw the error */
    void activate(int identifier, const QString& description = QString());
    /** clear the state / remove the error */                             
    void deactivate();                                                    
 
 
 
    /** alias for active() */
    bool operator()() const; 
 
    /** @returns 'name() "description()"' e.g.
     * 'MyErrorClass::MyReturnValue "something meaningful"'
     */                                                    
    operator QString() const;                              
 
signals:
 
    /** emitted when the error has been activated */
    void activated();                               
 
protected:
 
    /**
     * Default implementation generates a string using the
     * "DerivedErrorClassName::EnumeratorValueName"       
     *                                                    
     * @note                                              
     * reimplement this functions to apply an own         
     * identifier-to-error-name-mapping                   
     */                                                   
    virtual QString identifierToName(int identifier) const;
 
private:
 
    bool m_active;
    int m_identifier;
    QString m_description;
};
 
 
#endif

The Source File - BaseError.cpp

#include "BaseError.hpp"
#include <qmetaenum>
 
BaseError::BaseError() : QObject(), m_active(false)
{                                                  
}                                                  
 
 
BaseError::~BaseError()
{                      
}                      
 
 
bool BaseError::operator()() const
{                                 
    return m_active;              
}                                 
 
 
BaseError::operator QString() const
{                                  
    return QString("%1 \"%2\"").arg(name()).arg(description());
}                                                              
 
 
int BaseError::identifier() const
{                                
    return m_identifier;         
}                                
 
 
QString BaseError::name() const
{
    return identifierToName(m_identifier);
}
 
 
const QString& BaseError::description() const
{
    return m_description;
}
 
 
void BaseError::activate(int identifier, const QString& description)
{
    m_identifier = identifier;
    m_description = description;
    m_active = true;
 
    emit activated();
}
 
 
void BaseError::deactivate()
{
    m_active = false;
}
 
 
QString BaseError::identifierToName(int identifier) const
{
    QStringList ret;
 
    const QMetaObject* meta = metaObject();
 
    for (int i=0; i < meta->enumeratorCount(); ++i) {
 
        QMetaEnum enumerator = meta->enumerator(i);
 
        enumerator.key(identifier);
 
        ret < < QString("%1::%2").arg(enumerator.scope()).arg(enumerator.key(identifier));
    }
 
    return ret.join(", ");
}


Categories: c/c++, qt, software Tags: , , , , ,
  1. Willi
    June 5th, 2009 at 18:46 | #1

    I sometimes do something similar in c using an enum + stringarray to have that key:value relation. But your version is neat *thumbs up* ;)

  1. No trackbacks yet.