web-dev-qa-db-ja.com

Qt / C ++エラー処理

Qt/C++でのエラー処理について多くの調査を行ってきましたが、まだ始めたときと同じくらい迷っています。多分私は(他の言語が提供するように)簡単な方法を探しています。特に1つは、私が宗教的に使用する未処理の例外を提供します。プログラムで問題が発生すると、未処理の例外がスローされるため、独自のエラーレポートを作成できます。そのレポートは顧客のマシンからオンラインのサーバーに送信され、後で読みます。

私がC++で抱えている問題は、行われるすべてのエラー処理をBEFOREハンドの前に考える必要があることです(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オペレーティングシステムでデフォルトのクラッシュウィンドウをユーザーに表示せずにアプリケーションを終了したい。アサーションメッセージをファイルに書き込んだ後、それをうまく終了させたいだけです。

35
user336063

QCoreApplication :: notify() をオーバーライドして、そこにtry-catchを追加します。それと、main()の何かが私の経験のほとんどのケースをカバーしています。

これが私のやり方です。ここではQtのバージョンではなくC++ RTTIを使用していますが、これはアプリの便宜のためです。また、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は、おそらくクロスプラットフォームの代替として機能するでしょう。

34
Macke

キャッチ(...)をmain()内または周りに配置できます。

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

Google Breakpad は、クロスプラットフォームのアプリケーションエラー報告フレームワークです。多分それは役立ちますか?

(私はまだc ++/qtアプリで試していませんが、いつかやってみたいと思います...)

5
Macke

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

スロットから直接マクロを使用してください!

3
BuvinJ

例外を使用したエラー処理を好みます。以下のサンプルコードを見つけてください:

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