2011-01-11 38 views
28

He estado investigando mucho sobre el manejo de errores con Qt/C++ y todavía estoy tan perdido como cuando comencé. Tal vez estoy buscando una salida fácil (como otros idiomas ofrecen). Uno, en particular, proporciona una excepción no controlada que uso religiosamente. Cuando el programa encuentra un problema, arroja la excepción no controlada para que pueda crear mi propio informe de error. Ese informe se envía desde la máquina de mis clientes a un servidor en línea, y luego lo leo.Qt/C++ Manejo de errores

El problema que estoy teniendo con C++ es que cualquier manejo de error que se haya hecho tiene que pensarse ANTES de la mano (piense intente/capture o condicionales masivos). En mi experiencia, los problemas en el código no se consideran de antemano, de lo contrario no habría un problema para empezar.

Escribir una aplicación multiplataforma sin un mecanismo de manejo/informe/rastreo de errores multiplataforma me da un poco de miedo.

Mi pregunta es: ¿Hay algún tipo de Qt o C++ mecanismo de captura de errores "catch-all" específico que pueda usar en mi aplicación para que, si algo sale mal, pueda, al menos, escribir un informe antes se bloquea?

Ejemplo:


class MainWindow: public QMainWindow 
{ 
[...] 

public slots: 
void add_clicked(); 
} 

void MainWindow::add_clicked() 
{ 
    QFileDialog dlg(this, Qt::Sheet); 
    QString filename = dlg.getOpenFileName(this); 

    if(!filename.isEmpty()) 
    { 
     QStringList path = filename.split(QDir::separator()); 
     QString file = path.at(path.count()); // Index out of range assertion. 

     if(!lst_tables->openDatabase(filename)) 
     { 
      [...] 
     } 
    } 
} 

Quiero que este error para ser capturado como una excepción no controlada y la aplicación de dejar de fumar sin mostrar al usuario la ventana de accidente por defecto en el sistema operativo Windows/Mac. Solo quiero que salga bien después de escribir el mensaje de afirmación en un archivo, etc.

Respuesta

28

Anule QCoreApplication::notify() y añada try-catch allí. Eso, y algo en main() cubre la mayoría de los casos en mi experiencia.

Aquí hay una especie de cómo lo hago. Tenga en cuenta que estoy usando C++ RTTI aquí, no la versión de Qt, pero eso es solo por comodidad en nuestras aplicaciones. Además, colocamos un QMessageBox con la información y un enlace a nuestro archivo de registro. Debes expandir de acuerdo a tus propias necesidades.

bool QMyApplication::notify(QObject* receiver, QEvent* even) 
{ 
    try { 
     return QApplication::notify(receiver, event); 
    } catch (std::exception &e) { 
     qFatal("Error %s sending event %s to object %s (%s)", 
      e.what(), typeid(*event).name(), qPrintable(receiver->objectName()), 
      typeid(*receiver).name()); 
    } catch (...) { 
     qFatal("Error <unknown> sending event %s to object %s (%s)", 
      typeid(*event).name(), qPrintable(receiver->objectName()), 
      typeid(*receiver).name()); 
    }   

    // qFatal aborts, so this isn't really necessary 
    // but you might continue if you use a different logging lib 
    return false; 
} 

Además usamos el __try, __except en Windows para capturar las excepciones asíncronos (violaciónes de acceso). Google Breakpad probablemente podría servir como un sustituto multiplataforma para eso.

+0

¿Puede mostrarme un ejemplo de su pesca de prueba allí? –

+0

@ShiGon: Hecho ... – Macke

+0

Creo que estoy haciendo algo mal porque NO recibo amor por ningún try-catch que haga, excepto cuando utilizo el comando throw por mi cuenta. Muy confuso. E incluso entonces, la aplicación aún se bloquea e intenta enviar un informe de error a Apple en este caso. Estoy intentando que la aplicación salga con gracia. –

10

Se puede poner una trampa (...) en o alrededor de main() Aquí es todo:

int main() try 
{ 
    ... 
} 
catch (std::exception & e) 
{ 
    // do something with what... 
} 
catch (...) 
{ 
    // someone threw something undecypherable 
} 
+0

Además, la adición de manejo de excepciones de estilo c detecta Violaciones de acceso, Desbordamiento de pila y algunas excepciones más útiles. – Macke

+1

@Marcus solo se pueden detectar violaciones de acceso y desbordamiento de pila mediante el manejo de excepciones en Windows usando MSVC, hasta donde yo sé ... no es portátil. – rohanpm

+1

@Jerkface: cierto. No sé por qué estaba asumiendo Windows en este caso. : - | – Macke

5

Google Breakpad es un marco de informe de errores de aplicaciones multiplataforma. Tal vez ayuda?

(no he probado en nuestra C++/aplicaciones qt todavía, pero me gustaría moverse a la misma algún día ...)

2

Qt no suele usar, o totalmente el apoyo de lanzar una excepción (si se puede creer eso!)

visita estos links:

Why doesn't Qt use exception handling?

http://doc.qt.io/qt-5/exceptionsafety.html

Dicho esto, las respuestas de @Crazy Eddie y @Macke son bastante buenas, pero no siempre funcionan. En particular, descubrí que no puede usar ninguno de ellos desde una función de ranura que ha invocado desde QML. Por lo tanto, he creado un trabajo malicioso para este problema. * Úselo junto con el suyo, no en lugar de él.

Primero, creé la clase derivada de QException, que omitiré aquí, pero es algo que probablemente quiera hacer. En esta publicación, solo me refiero a ella como "MyQException".

De todos modos, añadir esta cabecera para una clase llamada QmlSlotThrower:

#ifndef QMLSLOTTHROWER_H 
#define QMLSLOTTHROWER_H 

#include "MyQException.h" 

class QmlSlotThrower 
{ 
public: 
    static QmlSlotThrower *get() 
    { 
     static QmlSlotThrower instance; 
     return &instance; 
    } 
    QmlSlotThrower(QmlSlotThrower const&) = delete; 
    void operator=(QmlSlotThrower const&) = delete; 

    void throwToTop(const MyQException &exception); 

private: 
    QmlSlotThrower(){} 
}; 
static QmlSlotThrower *qmlSlotThrower = QmlSlotThrower::get(); 

#define throwFromQmlSlot(exc) qmlSlotThrower->throwToTop(exc); return; 

#endif // QMLSLOTTHROWER_H 

Entonces, es CPP:

#include "QmlSlotThrower.h" 
#include <QTimer> 

class AsynchronousThrower: public QObject 
{ 
Q_OBJECT 
public: 
    void throwThis(const MyQException &exception) 
    { 
     exception_ = exception; 
     QTimer::singleShot(0, this, SLOT(throwIt())); 
    } 
private slots: 
    void throwIt(){ throw exception_; } 
private: 
    MyQException exception_; 
}; 
static AsynchronousThrower asycnThrower; 

// This is needed to allow the Q_OBJECT macro 
// to work in the private classes 
#include "QmlSlotThrower.moc" 

// -------------------------------- 

void QmlSlotThrower::throwToTop(const MyQException &exception) 
{ asycnThrower.throwThis(exception); } 

Por último, aquí está un ejemplo de implementación:

void someQMLSlot() 
{ 
    // Qt has been progressively adding exception handling 
    // support, but you still cannot throw from a QML 
    // triggered slot. It causes an uncatchable fatal error! 

    // As a general rule, don't throw in Qt unless you are 
    // certain something is there to catch it. You cannot 
    // count on an uncaught exception handler at a top level 
    // to always work. This QML problem is a perfect example. 

    // So this is not an option here! 
    //throw MyQException("Something terrible occured!"); 

    // This work around, however, can be used instead! 
    //throwFromQmlSlot(MyQException("Something terrible occured!")) 

    // Or, to be more robust in illustrating how you can still use 
    // normal throws from nested functions even, you can do this: 
    try{ throw MyQException("Something terrible occured!"); } 
    catch(const MyQException &e) { throwFromQmlSlot(e) } 

    qDebug() << "YOU SHOULD NEVER SEE THIS!!"; 
} 

USE ONLY ¡LA MACRO DIRECTAMENTE DESDE SU RANURA!