Qt/C++错误处理

40

我一直在研究如何使用Qt/C++处理错误,但始终感到迷茫。也许我想找到一个简单的方法(就像其他编程语言提供的那样)。有一种特殊的方式可以提供未处理异常,我非常频繁地使用它。当程序遇到问题时,会抛出未处理异常,以便我可以创建自己的错误报告。该报告会从客户机器发送到在线服务器,我稍后再查看。

我在C++中遇到的问题是必须事先考虑好任何错误处理(例如尝试/捕获或大量条件判断)。在我的经验中,代码中的问题通常是没有事先考虑到的,否则就不会有问题了。

没有跨平台的错误处理/报告/追踪机制给我带来了一点恐惧感。

我的问题是:是否有任何Qt或C++具体的“全局”错误捕获机制,我可以在应用程序中使用,以便如果发生故障,至少可以在崩溃之前写入报告?

示例:


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操作系统上退出应用程序而不显示默认的崩溃窗口。 我只想在将断言消息写入文件等操作后优雅地退出。

5个回答

36

重写 QCoreApplication::notify() 并在其中添加 try-catch。在我的经验中,这样做以及在 main() 中做一些处理就可以覆盖大多数情况。

以下是我通常的做法。请注意,我在此处使用的是 C++ RTTI,而不是 Qt 的版本,但这只是为了方便我们的应用程序。此外,我们会弹出一个 QMessageBox,显示相关信息和指向日志文件的链接。您应该根据自己的需求进行扩展。

bool QMyApplication::notify(QObject* receiver, QEvent* event)
{
    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;
}

此外,我们在Windows中使用__try__except来捕获异步异常(访问冲突)。Google Breakpad可能可以作为跨平台替代品。

我觉得我做错了什么,因为无论我做任何try-catch,除非我自己使用throw命令,否则都没有起到作用。太令人困惑了。即使这样,应用程序仍然会崩溃,并尝试向苹果发送错误报告。我正在尝试使应用程序优雅地退出。 - user336063
我已经在原文中添加了一些解释和代码。 - user336063
@Shigon:对于Mac,你需要不同的机制来捕获崩溃。在Google或SO上搜索它,或者在此处在SO上提出更专注的问题。如何做到这一点并不是Qt相关的主要问题,因为这个问题回答了在Qt框架中放置这些崩溃处理程序的位置。 - Macke
这种方法安全吗?Qt声称它无法处理通过信号和槽传播的异常:http://qt-project.org/doc/qt-5/exceptionsafety.html#signals-and-slots - Mikhail
@Mikhail:并不是说你应该依赖这个来管理你的应用程序中的错误。然而,作为调试工具,它非常有用,可以捕获和报告错误发生的位置。Signal/slot仅在排队模式(跨线程)中使用事件。 - Macke

12

你可以在main()函数中或其周围放置catch(...)语句。这是周围的方式:

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

此外,添加C风格的异常处理可以捕获访问冲突、堆栈溢出和其他一些有用的异常。 - Macke
2
@Marcus,据我所知,在Windows上使用MSVC,你只能通过异常处理来捕获访问冲突和堆栈溢出...这是不可移植的。 - rohanpm
1
@Jerkface:没错。我不知道为什么在这种情况下我会假设是Windows。 :-| - Macke

6

Google Breakpad是一个跨平台的应用程序错误报告框架。也许它能帮到你?

(我还没有在我们的c++/qt应用程序中尝试过,但我希望有一天可以尝试一下...)


2
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

然后,是C++:
#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!!";
}

请仅从您的插槽直接使用宏!

0

我更喜欢使用异常处理错误。 请查看以下示例代码:

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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接