web-dev-qa-db-ja.com

Qtで例外をキャッチする方法は?

_try
{  // `count()` throws exception
  connect(thread, SIGNAL(started()), engine, SLOT(count()));  
}
catch(const X& e)
{}
_

Qt-5の時点で、次のエラーが発生します。

Qtは、イベントハンドラーからスローされた例外をキャッチしました。イベントハンドラからの例外のスローは、Qtではサポートされていません。 Qtコードを介して例外を伝播させてはなりません。それが不可能な場合は、Qt 5で、少なくともQCoreApplication::notify()を再実装し、そこですべての例外をキャッチする必要があります。

上記のように従来の方法で例外をキャッチできない場合、どこでそれらをキャッチする必要がありますか?

19
smallB

どこで捕まえるの?

これが、Qtがシグナル/スロット接続間での例外のスローをサポートしていない理由です。試してみると、次のメッセージが表示されます。

Qtは、イベントハンドラーからスローされた例外をキャッチしました。イベントハンドラからの例外のスローは、Qtではサポートされていません。 QApplication :: notify()を再実装し、そこですべての例外をキャッチする必要があります。

すでに述べたように、QApplicationをサブクラス化してそこで例外をキャッチすることは可能ですが、それは物事を処理する非常に厄介な方法になります。

可能であれば、カウントをスローしないように書き換えることをお勧めします。


count()を書き換えられない場合はどうなりますか?

たとえば、count()が、使用しているサードパーティライブラリの関数の一部である場合はどうなりますか?

公式のQtライブラリにはスロットがありません。したがって、スローするスロットがあるサードパーティのライブラリを使用している場合は、それが適切なライブラリではないことを示している可能性があります。とにかく使用したい場合は、QApplication::notifyでキャッチするのではなく、アダプターを作成することをお勧めします。

どういう意味ですか?まず、コンストラクターで大ざっぱなサードパーティオブジェクトを取り込むオブジェクトを作成します。その中に、try/catchブロックでスロースロットへの呼び出しをラップするスロットを記述します。大ざっぱなサードパーティオブジェクトのスロットに接続する代わりに、新しく作成したオブジェクトのスロットに接続します。

この方法で例外キャッチを実行すると、関連するコードがまとめられ、これらの問題のある関数が複数発生した場合に、QApplication::notifyが無関係なtry/catchブロックでいっぱいになるのを防ぎます。

例えば:

class BadCounter {
Q_OBJECT
public slots:
  void count() { throw CounterError("unable to count"); }
};

class CounterAdaptor {
Q_OBJECT
  BadCounter* counter_;
public:
  CounterAdaptor(BadCounter* counter) {
    counter_ = counter;
  }
public slots:
  void count() {
    try {
      counter_->count();
    } catch (const CounterError& e) {
      std::cerr << e.what() << std::endl;
    }
  }
};

int main() {
  BadCounter engine;
  CounterAdaptor adaptor(&engine);
  QThread* thread = new QThread();
  connect(thread,SIGNAL(started()),&adaptor,SLOT(count())); 
  thread.start();
  ... // etc...
  delete thread;
}

どこからでも投げられる可能性のあるものを処理したい場合はどうなりますか?

この種の世界的な懸念の明らかな例は、予期しない例外です。間違いはどこでも起こり得ます。原因を特定して修正できるように、イベントに関する詳細をできるだけ多くログに記録することが望ましいでしょう。この場合、 jichiの回答 に示すように、wouldQApplication::notifyを独自のサブクラスに再実装する必要があります。グローバルな懸念にグローバルハンドラーを使用することは非常に合理的です。

16
cgmb

QApplication :: notifyをオーバーライドするためのサンプルコードが必要な場合は、ここから入手しました: http://www.02.246.ne.jp/~torutk/cxx/qt/QtMemo.html

#include "MyApplication.h"
#include <exception>

MyApplication::MyApplication(int& argc, char** argv) :
  QApplication(argc, argv) {}

bool MyApplication::notify(QObject* receiver, QEvent* event) {
  bool done = true;
  try {
    done = QApplication::notify(receiver, event);
  } catch (const std::exception& ex) {
    // ログや何らかの回復処理
  } catch (...) {
    // ログや何らかの回復処理
  }
  return done;
} 
12
jichi