3

So, I'm looking at the C++ reference for the try/catch block.

I see there are a few ways to capture an exception like so:

try {
    f();
} catch (const std::overflow_error& e) {
    // this executes if f() throws std::overflow_error (same type rule)
} catch (const std::runtime_error& e) {
    // this executes if f() throws std::underflow_error (base class rule)
} catch (const std::exception& e) {
    // this executes if f() throws std::logic_error (base class rule)
} catch (...) {
    // this executes if f() throws std::string or int or any other unrelated type
}

I see in the following examples that you can capture the "e" data like so:

std::cout << e.what();

So my question boils down to this:

How do I get the exception data on the catch(...)?

(Side question: is it even wise to use the catch(...)?)

erenon
  • 18,838
  • 2
  • 61
  • 93
CodeLikeBeaker
  • 20,682
  • 14
  • 79
  • 108
  • 3
    You can't. If you are catching `...` this could be anything from an integer to std::string. The only thing you should normally do with `...` is log an unknown exception in some logging system and re-throw (so the application terminates). Then debug your code to understand why something so strange was thrown. – Martin York Oct 31 '16 at 13:33
  • @LokiAstari Okay, well that makes sense. Thanks :) – CodeLikeBeaker Oct 31 '16 at 13:42

2 Answers2

8

In general, you can not. C++ allows pretty much anything to be thrown. For example throw 42; is perfectly well-defined C++ code, and the exception's type is int.

As for it being wise to use it - there are valid uses:

  • If an exception is thrown and there is no catch block for it all the way up, std::terminate is called and there is no guarantee of stack unwinding. catch(...) guarantees that (because it catches any exception).

int main()
{
    super_important_resource r;
    may_throw();
    // r's destructor is *not* guaranteed to execute if an exception is thrown
}

int main()
try {
    super_important_resource r;
    may_throw();
    // r's destructor is guaranteed to execute during stack unwinding
} catch(...) {
}
  • it's a valid use case to log that an exception was thrown, before rethrowing it.

try {
//...
} catch(...) {
    log() << "Unknown exception!";
    throw;
}
krzaq
  • 16,240
  • 4
  • 46
  • 61
  • Thank you krzaq for your answer. This makes sense. – CodeLikeBeaker Oct 31 '16 at 13:50
  • Am I right thinking that problem with stack unwinding with uncaught exception may happens just with resources that were allocated on stack in main() function? While moving all way up from exception point, stack have to be unwinded, at least up to main() function? – Arkady Oct 31 '16 at 13:54
3

How do I get the exception data on the catch(...)?

In general case, you cannot get an arbitrary exception. Still, if the exception type is one of known types, you can re-throw current exception and catch it.

Side question: is it even wise to use the catch(...)?)

It makes sense to use at as a fallback option, to handle unexpected exceptions. And one could consider catch-rethrow technique to avoid copy-pasting of catch series in several places.

void Catcher()
{
    try
    {
        throw;
    }
    catch (const std::overflow_error& e) {
        // this executes if f() throws std::overflow_error (same type rule)
    }
    catch (const std::runtime_error& e) {
        // this executes if f() throws std::underflow_error (base class rule)
    }
    catch (const std::exception& e) {
        // this executes if f() throws std::logic_error (base class rule)
    }
    catch (...) {
        // oops!
    }
}

int main()
{
    try {
        f();
    }
    catch (...) {
        Catcher();
    }
}
AlexD
  • 32,156
  • 3
  • 71
  • 65
  • How does this address the OPs question? – erenon Oct 31 '16 at 13:35
  • @erenon If we are within the `catch(...)` block, we still can get the exceptions of known types. – AlexD Oct 31 '16 at 13:39
  • @AlexD I like your Catcher class to avoid repetition. Also, thanks for editing your answer to include the additional information. +1 – CodeLikeBeaker Oct 31 '16 at 13:49
  • I didn't know you could rethrow from a different context and it'd throw the active exception. This is interesting – krzaq Oct 31 '16 at 13:49
  • 1
    @krzaq .It is how I understood the standard. "_The exception with the most recently activated handler that is still active is called the currently handled exception._" and "_A throw-expression with no operand rethrows the currently handled exception_". – AlexD Oct 31 '16 at 13:57
  • 2
    @AlexD sure, I just always though that you had to `throw` it right there in the catch block. So thank you, I learned something new today :) (I also verified it with cppreference, gcc and clang, so you're very unlikely to be wrong on this) – krzaq Oct 31 '16 at 13:58