【问题标题】:Qt/C++ Error handlingQt/C++ 错误处理
【发布时间】:2011-06-07 09:46:51
【问题描述】:

我一直在做很多关于使用 Qt/C++ 处理错误的研究,但我仍然像刚开始时一样迷茫。也许我正在寻找一种简单的出路(就像其他语言提供的那样)。一个,特别是,提供了一个我虔诚地使用的未处理的异常。当程序遇到问题时,它会抛出未处理的异常,以便我可以创建自己的错误报告。该报告从我的客户机器发送到在线服务器,然后我稍后再阅读。

我在使用 C++ 时遇到的问题是,必须先考虑已完成的任何错误处理(想想 try/catch 或大量条件)。根据我的经验,代码中的问题是事先没有想到的,否则一开始就不会有问题。

在没有跨平台错误处理/报告/跟踪机制的情况下编写跨平台应用程序对我来说有点可怕。

我的问题是:我可以在我的应用程序中使用任何类型的 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 操作系统上的默认崩溃窗口的情况下退出应用程序。我只是希望它在将断言消息写入文件等后很好地退出。

【问题讨论】:

    标签: c++ qt error-handling


    【解决方案1】:

    覆盖QCoreApplication::notify() 并在那里添加try-catch。根据我的经验,main() 中的内容涵盖了大多数情况。

    这是我的做法。请注意,我在这里使用的是 C++ 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;
    }
    

    此外,我们在 Windows 上使用 __try、__except 来捕获异步异常(访问冲突)。 Google Breakpad 可能会成为它的跨平台替代品。

    【讨论】:

    • 我认为我做错了什么,因为除了我自己使用 throw 命令外,我所做的任何 try-catch 都没有得到任何爱。如此混乱。即便如此,在这种情况下,应用程序仍然会崩溃并尝试向 Apple 发送错误报告。我正在尝试让应用程序正常退出。
    • 我在原帖中添加了一些说明和代码。
    • @Shigon:对于 Mac,您需要一种不同的机制来捕获崩溃。在 Google 或 SO 中搜索它,或者在 SO 上提出更集中的问题。如何做到这一点主要与 Qt 无关,因为这个问题回答了在 Qt 框架中放置这些崩溃处理程序的位置。
    • 这种方法安全吗? Qt 声称它无法处理通过信号和槽传播的异常:qt-project.org/doc/qt-5/exceptionsafety.html#signals-and-slots
    • @Mikhail:并不是说您应该依赖它来管理应用程序中的错误。然而,作为一个调试工具,在错误发生的地方捕捉和报告错误是非常有用的。信号/槽仅在队列模式下使用事件(跨线程)
    【解决方案2】:

    您可以在 main() 中或周围放置一个 catch (...) 如下:

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

    【讨论】:

    • 此外,添加 c 样式的异常处理会捕获访问冲突、堆栈溢出和一些更有用的异常。
    • @Marcus,据我所知,您只能使用 MSVC 在 Windows 上通过异常处理来捕获访问冲突和堆栈溢出……它不可移植。
    • @Jerkface:是的。我不知道为什么在这种情况下我假设是 Windows。 :-|
    【解决方案3】:

    Google Breakpad 是一个跨平台的应用错误报告框架。也许有帮助?

    (我还没有在我们的 c++/qt 应用程序中尝试过它,但我很想有朝一日可以使用它......)

    【讨论】:

      【解决方案4】:

      Qt 通常不使用,也不完全支持异常抛出(如果你相信的话!)

      查看这些链接:

      Why doesn't Qt use exception handling?

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

      也就是说,@Crazy Eddie 和@Macke 的答案非常好,但并不总是有效。特别是,我发现你不能从你从 QML 调用的槽函数中使用它们中的任何一个。因此,我为这个问题创建了一个 hacky 解决方法。 *将此与他们的结合使用 - 而不是代替它。

      首先,我创建了从 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;
        }
        

        【讨论】:

          猜你喜欢
          • 2017-07-04
          • 2010-12-07
          • 1970-01-01
          • 1970-01-01
          • 2016-01-03
          • 2014-05-11
          • 2011-11-19
          • 2013-08-29
          • 1970-01-01
          相关资源
          最近更新 更多