How to catch exceptions in Qt?

31,262

Solution 1

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.

Solution 2

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;
} 
Share:
31,262

Related videos on Youtube

smallB
Author by

smallB

Updated on July 09, 2022

Comments

  • smallB
    smallB almost 2 years
    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?

    • Adrian
      Adrian about 12 years
      Maybe you should put the try-catch block in the count() function...
    • Adrian
      Adrian about 12 years
      Then your solution provided in the question is good
  • smallB
    smallB about 12 years
    #vBx you don't use any signals/slots connection, am I missing something?
  • Alok Save
    Alok Save about 12 years
    @smallB: Signals and slots are QT specific constructs, what vBx gives you here is the standard C++ solution.
  • smallB
    smallB about 12 years
    @Als I see that but my question is qt specific. I thought the notion of signal slot would be a good give away. Also if you would note I don't ask how to use try catch block.
  • Nicol Bolas
    Nicol Bolas about 12 years
    @smallB: Your question is in no way Qt specific. Qt is C++; it may have special macros and it's own special little pre-compiler and build projects, but it's still C++. The fact that you happen to use signals changes nothing about exception handling.
  • alexisdm
    alexisdm about 12 years
    @NicolBolas the question is very Qt specific: count() will be called asynchronously, so knowning where the exception is handled is not as easy as it seems.
  • Mark Ransom
    Mark Ransom about 12 years
    @alexisdm, can you explain that a little? Generally parameters to a function are fully evaluated before the function is called. How does the SLOT macro make it asynchronous?
  • smallB
    smallB about 12 years
    @vBx son where did I cry? I've just tried to explained to you that you didn't understand what I'm asking about.
  • smallB
    smallB about 12 years
    @NicolBolas 9 out of ten persons (who know C++ and Qt), if you tell them words signal/slot will immediately associate this with typical Qt pattern. One will not. I feel sorry for you.
  • smallB
    smallB about 12 years
    yes, I believe I'll have to rewrite this and don't throw. Thanks.
  • alexisdm
    alexisdm about 12 years
    This is a string, not a function call: SLOT(count()) == "1slot()" (or maybe "2slot()" I'm not sure).
  • smallB
    smallB about 12 years
    @NicolBolas also there are persons who by reading tags (c++, qt) would immediately associate this question as a qt specific. As I've said, I feel sorry for you.
  • Max Lybbert
    Max Lybbert about 12 years
    It's entirely possible to call count(), save the result, and then pass the result to SLOT if SLOT's not allowed to throw an exception.
  • cgmb
    cgmb about 12 years
    SLOT does not take the result of count(). This is the SLOT macro: # define SLOT(a) "1"#a
  • Petr
    Petr over 10 years
    -1 for not providing any example how to reimplement this... all possible combinations I tried only throw some compiler errors and this information in this answer is basically something what we can see from qt debug log itself
  • Petr
    Petr over 10 years
    What is MyApplication? Is that a dialog?
  • jichi
    jichi over 10 years
    class MyApplication : public QApplication
  • BЈовић
    BЈовић over 10 years
    When it catches an exception, it returns true. Why? What does the return value mean? What if you return false?
  • jichi
    jichi over 10 years
    @BЈовић I am not sure about the return value. According to Qt's documentation, it will determine whether propagate events (keyboard and mouse) to the parent. But for QApplication, I think it usually does not have a parent object.
  • cgmb
    cgmb about 10 years
    Catching it in the notify loop is a terrible way of handling exceptions because it results in an unintuitive control flow. If you really must, it's pretty simple and jichi's answer shows exactly how.
  • antred
    antred almost 9 years
    In my opinion reimplementing QApplication::notify is by far the superior solution because it plugs all holes. After you've done this, you can be sure that any exception (at least any your catch-block mentions) will be caught and dealt with (perhaps log it to a file before exiting or something).
  • L__
    L__ almost 9 years
    this doesn't really answer the question - in case you are using someone else's code you might want to use QApplication::notify to make sure you don't miss the exception
  • cgmb
    cgmb almost 9 years
    @antred QApplication already catches and logs the exception with a qWarning before exiting. That's the error message I quoted is. You probably want to save warnings to a log, and you can do so via qInstallMessageHandler. Though, yes, if you want to have a custom message, you'll need to reimplement QApplication::notify,
  • cgmb
    cgmb almost 9 years
    It's clear to me now that people finding this page are dealing with a variety of situations. The original answer was good for the asker, but I've updated my answer to try to address other situations that people may be facing.
  • Thomas
    Thomas about 3 years
    But receiver might have a parent. It is the receivers hierarchy that matters for event propagation.