Comment by patrick451

1 day ago

> Which is really no different from someone choosing to terminate.

If you std::abort(), you'll get a useful stack trace in the core dump. If you crash from an unhandled exception, you don't. That's a pretty huge difference and is one of the reasons exceptions suck.

That's nice but it's certainly not guaranteed by anything, just something provided by your toolchain or platform. ("Core dumps" aren't even a thing in C++.)

If you're looking for implementation-specific guarantees then you could make that happen with exceptions too. I think on GCC replacing a function like __cxa_throw might be sufficient to let you capture a stack trace?

If you're looking for source-level-only guarantees then another option is to just replace your throw <expr> statements with one that attaches whatever extra info you want. You could literally script this to patch your external repos automatically too. Or heck, maybe you could even just define throw to be a macro that shoves your stack trace into some global variable before actually throwing.

> If you std::abort(), you'll get a useful stack trace in the core dump. If you crash from an unhandled exception, you don't. That's a pretty huge difference and is one of the reasons exceptions suck.

All of this is up to the implementation in practice. The codebases I work on generally follow the pattern that exceptions may be thrown but may not be caught*, and thus they practically serve as terminate. And we absolutely get stack traces in our core dumps (Linux, both GCC and clang), and basically all of the complex debugging I do starts with a coredump stacktrace.

* We follow this pattern for a few reasons, (1) it is generally safer for us to assume that libraries we consume (STL included) will behave as expected with exceptions enabled, (2) "don't catch exceptions" (or if you must, catch the as close-to-throw as possible) is a simple way to avoid exceptions-for-nonexceptional-cases control flow, and (3) most of the C++ codebase is exposed through bindings in Python, and propagating errors as exceptions is the only Python-friendly way to handle it.

> If you crash from an unhandled exception, you don't.

.. you absolutely get a stack trace from unhandled exception in c++, that starts where the exception is thrown? At least with clang and GCC, maybe MSVC isn't able to.

foo.cpp:

    #include <stdexcept    
    void bar() { throw std::runtime_error("boo"); }
    void foo() { bar(); }
    int main() { foo(); }

running:

    $ g++ foo.cpp -std=c++23 -g
    $ ./a.out
    terminate called after throwing an instance of 'std::runtime_error'
      what():  boo

    $ coredumpctl gdb
    ...
    #7  0x00005555555551bb in bar () at foo.cpp:3
    #8  0x00005555555551da in foo () at foo.cpp:4
    #9  0x00005555555551e6 in main () at foo.cpp:5

it's a basic example, but it's how I've always done all my debugging in C++ since forever

  • Sure, in a desktop program you do. In embedded exceptions are a scourge because they crash your program until someone can get back out there to power cycle it. At least with return-codes you can continue execution even if you failed to effect the change you wanted. If that was ancillary to the system’s core function then the system keeps running.

    • Pretty sure esp32 just rebooted automatically when unhandled exception is thrown for instance