Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to catch exceptions in Qt?

Tags:

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

As of Qt-5, I get following error:

Qt has caught an exception thrown from an event handler. Throwing exceptions from an event handler is not supported in Qt. You must not let any exception whatsoever propagate through Qt code. If that is not possible, in Qt 5 you must at least re-implement QCoreApplication::notify() and catch all exceptions there.

If I can't catch the exceptions in conventional way as shown above, then where are we supposed to catch those?

like image 252
smallB Avatar asked Apr 09 '12 15:04

smallB


2 Answers

where am I supposed to catch it?

This is exactly why Qt does not support throwing exceptions across signal/slot connections. If you try it, you'll see this message:

Qt has caught an exception thrown from an event handler. Throwing exceptions from an event handler is not supported in Qt. You must reimplement QApplication::notify() and catch all exceptions there.

As it mentions, it is possible to subclass QApplication and catch your exception there, but that will be a very annoying way of handling things.

If possible, I would recommend rewriting count such that it does not throw.


What if you can't rewrite count()?

For example, what if count() is part of a function in a 3rd-party library that you're using?

No slot in any official Qt library throws, so if you're using a third-party library with a slot that throws, it's probably a sign that it's not a good library. If you want to use it anyway, I recommend that rather than catching it in QApplication::notify, that you instead create an adapter.

What does that mean? First create an object that takes in your sketchy third-party object in the constructor. In it, write a slot that wraps a call to the throwing slot with a try/catch block. Now instead of connecting to the sketchy third-party object's slot, connect to your newly create object's slot.

Doing the exception catching this way keeps related code together, and prevents QApplication::notify from filling up with a bunch of unrelated try/catch blocks if you encounter more than one of these problematic functions.

For example:

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

What if you want to handle something that could be thrown from anywhere?

The obvious example of this sort of global concern is an unexpected exception. Mistakes can happen anywhere. It would be desirable to log as many details about the event as possible so the cause could be identified and corrected. In this case, you would want to reimplement QApplication::notify in your own subclass as shown in jichi's answer. Using a global handler for global concerns is quite reasonable.

like image 128
cgmb Avatar answered Oct 23 '22 03:10

cgmb


If someone needs an example code to override QApplication::notify, I got one from here (in Japanese): 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; }  
like image 44
jichi Avatar answered Oct 23 '22 03:10

jichi