Обработка ошибок Qt/С++
Я проводил много исследований по обработке ошибок с Qt/С++, и я все еще теряюсь, когда начинаю. Может быть, я ищу легкий выход (как и другие языки). Один, в частности, предусматривает необработанное исключение, которое я использую религиозно. Когда программа сталкивается с проблемой, она выдает необработанное исключение, поэтому я могу создать свой собственный отчет об ошибках. Этот отчет отправляется с моей машины клиентов на сервер, который я потом читаю позже.
Проблема, с которой я сталкиваюсь в С++, заключается в том, что любая обработка ошибок, о которой идет речь, должна быть рассмотрена перед рукой (думаю, try/catch или массивные условные выражения). По моему опыту, проблемы с кодом не считаются ранее, иначе не было бы проблем.
Написание кросс-платформенного приложения без кросс-платформенного механизма обработки ошибок/отчетности/трассировки для меня немного страшно.
Мой вопрос: есть ли какой-либо Qt или С++ специфический механизм захвата ошибок "все-все", который я могу использовать в своем приложении, чтобы, если что-то пошло не так, я могу, по крайней мере, написать отчет перед ним аварии?
Пример:
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))
{
[...]
}
}
}
Я хочу, чтобы эта ошибка была поймана как необработанное исключение. И приложение прекратилось, не показывая пользователю окно сбоя по умолчанию в операционной системе Windows/Mac. Я просто хочу, чтобы это прекратилось после написания сообщения об утверждении в файл и т.д.
Ответы
Ответ 1
Переопределите QCoreApplication:: notify() и добавьте там try-catch. Это, и что-то в основном() охватывает большинство случаев в моем опыте.
Вот как я это делаю. Обратите внимание, что я использую С++ RTTI здесь, а не версию Qt, но это просто для удобства в наших приложениях. Кроме того, мы размещаем QMessageBox с информацией и ссылкой на наш лог файл. Вы должны расширяться в соответствии с вашими потребностями.
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;
}
Кроме того, мы используем __try, __except в Windows, чтобы поймать асинхронные исключения (нарушения доступа). Возможно, Google Breakpad может стать кросс-платформенной заменой.
Ответ 2
Вы можете поместить catch (...) в или вокруг main() Здесь:
int main() try
{
...
}
catch (std::exception & e)
{
// do something with what...
}
catch (...)
{
// someone threw something undecypherable
}
Ответ 3
Google Breakpad - это кросс-платформенная среда отчетов об ошибках приложений. Может быть, это помогает?
(Я еще не пробовал это в наших приложениях С++/qt, но мне бы хотелось когда-нибудь обойти его...)
Ответ 4
Qt обычно не использует или полностью поддерживает бросок исключения (если вы можете это поверить!)
Ознакомьтесь с этими ссылками:
Почему Qt не использует обработку исключений?
http://doc.qt.io/qt-5/exceptionsafety.html
Тем не менее, ответы от @Crazy Eddie и @Macke довольно хороши, но не всегда работают. В частности, я обнаружил, что вы не можете использовать любой из них из функции слота, которую вы вызывали из QML. Итак, я создал хакерскую работу для этой проблемы. * Используйте это в сочетании с их - не вместо него.
Сначала я создал класс, полученный из QException, который я пропущу здесь, но это то, что вы, вероятно, захотите сделать. В этом сообщении я просто называю это "MyQException".
В любом случае добавьте этот заголовок для класса с именем 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
Тогда, 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 ); }
Наконец, вот пример реализации:
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!!";
}
ТОЛЬКО ИСПОЛЬЗУЙТЕ МАКРО ПРЯМО ОТ ВАШЕГО СЛОТА!
Ответ 5
Я предпочитаю обработку ошибок с использованием исключений. Пожалуйста, найдите приведенный ниже пример кода:
ErrorStatus ExplodeToLine()
{
var errorStatus = new ErrorStatus();
try
{
errorStatus = fun();
if (!errorStatus.ok())
{
throw new VicException(L"fun failed");
}
errorStatus = fun1();
if (!errorStatus.ok())
{
throw new VicException(L"fun1 failed");
}
errorStatus = fun2();
if (!errorStatus.ok())
{
throw new VicException(L"fun2 failed");
}
errorStatus.setError(ErrorType.OK);
}
catch (VicException vicExp)
{
Log(vicExp.errorMsg());
}
catch (Exception exp)
{
Log(exp.errorMsg());
}
return error_status;
}