2

EDIT: Another way to ask this question, in perspective, is to ask: Should Lippincott functions "catch all"?

Should Lippincott functions be declared noexcept?, does it matter?

After all, a version of this function in which all exceptions are captured, by definition cannot produce an exception.

However, in all the examples I see online, the noexcept is never there and I was wondering if I was missing something.

Example case:

foo_Result lippincott() noexcept???
{
    try {
        throw;
    } catch (const MyException1&) {
        return FOO_ERROR1;
    } catch (const MyException2&) {
        return FOO_ERROR2;
    } catch (...) {
        return FOO_UNKNOWN;
    }
}

foo_Result foo_dothing() {
    try {
        foo::DoThing();
        return FOO_OK;
    }
    catch (...) {
        return lippincott();
    }
}
alfC
  • 14,261
  • 4
  • 67
  • 118
  • As the intention of the Lippincott function is to catch all exceptions... Isn't it reasonable to remark it as `noexcept` to emphasize this? – Scheff's Cat Jan 28 '22 at 06:37
  • yes, that is my observation, but it is never shown with `noexcept` in the examples around. – alfC Jan 28 '22 at 07:13
  • You are right. :-) Hmm... A sloppiness of the author? Maybe, you have to ask the author about that. (There is a comment section in the article.) - However, thanks for the question. I never heard about Lippincott functions before. I wasn't aware that it can be done that simple. Instead I used a `tryCatch()` function where the body has to be given with `std::function` (to pass the body to try as a lambda)... ;-) – Scheff's Cat Jan 28 '22 at 07:21
  • When I noticed `noexcept` I had big expectations but I was quite disappointed what the `noexcept` can provide. I expected that I would get diagnostics if I call something with possible exceptions inside a function declared `noexcept`. But nope - no diagnostics. Instead I get a guarantee for a `std::terminate` in this case. My actual intention was to ensure that exceptions may not slip out (e.g. into the event loop of Qt) which would terminate the application - for the annoyance of the user. For that, "your" Lippincott function (or my poorer replacement) are the better tools. – Scheff's Cat Jan 28 '22 at 07:28
  • ... But there are definitely situations where the `noexcept` can provide added value. I forgot the exact details but I remember roughly it was about containers and move semantics... ([SO: Are move constructors required to be noexcept?](https://stackoverflow.com/a/57314621/7478597)) – Scheff's Cat Jan 28 '22 at 07:29
  • while fulfilling exception guarantees (e.g. weak) algorithms can switch to fast implementations if some operations are noexcept, specially move or swap. for example during vector resize. about your other comment, some static analysis tools can detect inconsistencies between noexcept declarations and throws in your code. noexcept is like const, it helps you program better but almost never helps the compiler do optimizations. – alfC Jan 28 '22 at 08:09
  • _noexcept is like const, it helps you program better but almost never helps the compiler do optimizations._ This is exactly where I don't agree. The attempt to write into a `const` will cause a compiler diagnostic (i.e. a compile error). Throwing into a `noexcept` function will just give the guarantee to call `std::terminate()`. Actually, I would like more if the compiler would provide a diagnostic instead to force me to add a proper `try`/`catch` handling but that's what it doesn't provide. – Scheff's Cat Jan 28 '22 at 08:36
  • When I was investigating into this I already looked how `noexcept`/`try`/`catch` condensed into code. That part looked more promising: Doing a `try`/`catch` for a `noexcept` function seems to be optimized away by all major compilers. This resulted to me into a thumb of rule: Use `try`/`catch` in any case (to be on the safe side). If the tried function is `noexcept` the compiler will care about this. Considering that you may change the `noexcept` guarantee in a function declaration (without being aware where it's called) this is at least somehow maintenance friendly. – Scheff's Cat Jan 28 '22 at 08:40
  • Thanks for the hint with static analysis tools... (I really should investigate into that also.) ;-) – Scheff's Cat Jan 28 '22 at 08:57

1 Answers1

1

Not all catch (...) executions come from a C++ exception. It is typically advisable to rethrow the exception in any catch-all block. This would imply that lippincott should not be noexcept and also just not have the catch-all block.

Specifically, in the ABI commonly used for C++ outside of Windows, forced unwinding may execute catch-all blocks:

A catch-all block may be executed during forced unwinding. For instance, a longjmp may execute code in a catch(...) during stack unwinding. However, if this happens, unwinding will proceed at the end of the catch-all block, whether or not there is an explicit rethrow.

In particular, with GCC on Linux, the blocks are executed, and if not rethrown, the application is terminated. Forced unwinding can happen even in code you completely control on POSIX if you ever call cancellation points (e.g. read).

Aside from this, it's not uncommon for a library to execute user code (e.g. think qsort). You also usually don't want to suppress those exceptions.

Therefore, the best generic option is to be transparent in catch-all blocks. Perform the cleanup you need. Then always rethrow.

So your function would look something like:

foo_Result lippincott()
{
    try {
        throw;
    } catch (const MyException1&) {
        return FOO_ERROR1;
    } catch (const MyException2&) {
        return FOO_ERROR2;
    } catch (const FooBaseException&) {
        return FOO_UNKNOWN;
    }
}

GCC does allow catching forced unwinds, so if you really wanted a catch-all and the other consideration is discarded (e.g. no user callbacks), you can first catch abi::__forced_unwind and rethrow.

Jeff Garrett
  • 5,863
  • 1
  • 13
  • 12
  • i didn’t know that there is such thing as forced unwinding. but i have to say that catch all in the function sounded dissonant, because it pretends to know about the error category of a wildly unknown exception. if you exclude catch(…) then it is clear that the function shouldn’t be noexcept. Which answers my question. – alfC Jan 30 '22 at 20:54