4 ms·
This is one of those half-true claims that C++ aficionados make, probably because they can play a bit fast and loose with resource cleanup in their particular s
by moregrist 4y ago
This is one of those half-true claims that C++ aficionados make, probably because they can play a bit fast and loose with resource cleanup in their particular situations.
RAII simplifies some of the resource cleanup, but at a cost: if the resource cleanup fails, there’s essentially no way to convey this.
So yes, you can write a destructor that tries to clean up your resources regardless of how the code exits its current scope. But if that cleanup encounters a problem, the destructor can, at best, try to convey this indirectly, either by (commonly) logging or (rarely) setting a variable to a value. It certainly cannot throw another exception, or even directly manipulate the return value of the function.
This is fine for some purposes, and completely unacceptable for others. But it’s not equivalent to the explicit cleanup and error handling in C.
So some of us occasionally find ourselves in an RAII language and using goto.
- vlovich123 4y agoYou can totally throw an exception from RAII code if you declare the destructor noexcept(false). Goto + RAII isn’t generally compatible unless you structure things very carefully
- moregrist 4y agoYou can, and your program will call std::terminate if there’s already an exception being processed. Not exactly desirable if you’re trying to write code that ensures careful resource cleanup. Also why it’s widely regarded as _wrong_ to ever throw in a destructor.
- kentonv 4y agoIMO this is a design bug in C++. The authors couldn't agree on what to do in the exception-during-unwind scenario, so they chose the worst possible option: crash. In most cases, an second exception raised while another exception is already being thrown is merely a side-effect of the first exception, and can probably safely be ignored. If the idea of throwing away a secondary exception makes you uncomfortable, then another possible solution might have been to allow secondary exceptions to be "attached" to the primary exception, like `std::exception::secondary()` could return an array of secondary exceptions that were caught. Obviously there's some API design thought needed here but it's not an unsolvable problem. If we could just change C++ to work this way, then throwing destructors would be no problem, it seems? So this seems like a C++-specific problem, not fundamental to RAII. That said, there is another camp which argues that it fundamentally doesn't make sense for teardown of resources to raise errors. I don't think you're in this camp, since you were arguing the opposite up-thread. I'm not in that camp either.
- bregma 4y agoSomething like std::nested_exception [0]? [0] https://en.cppreference.com/w/cpp/error/nested_exception https://en.cppreference.com/w/cpp/error/nested_exception
- kentonv 4y agoSort of, but nested_exception covers a different scenario. With nested_exception, the "attachment" is an exception which caused the exception it is attached to. In the scenario I'm talking about, the "attachment" is an exception which was caused by the exception it is attached to. Anyway, the key missing thing is not so much the exception representation, but the ability to have custom handling of what to do when an exception is thrown during unwind. Today, it goes straight to std::terminate(). You can customize the terminate handler, but it is required to end the process.
- bhawks 4y agoEven if the standard consolidated on one way or another to pack up secondary exceptions (or discard them) how likely is the calling code going to be able to handle and recover from this case? I am personally on team crash - I would rather my program exited and restarts in a known state then being in some weird and hard to replicate configuration.
- einpoklum 4y ago> Even if the standard consolidated on one way or another to pack up secondary exceptions (or discard them) how likely is the calling code going to be able to handle and recover from this case? Not unlikely. Sometimes your unwind involves cleaning up things that throw for the same reason as the original failure - e.g. failure to communicate with some piece of hardware. But you still try going through that unwind, right? Eventually you leave the context of accessing your hardware device entirely and are back to just working with system memory, the standard streams and some files, which would probably work fine. I have recently experienced this writing wrappers for the CUDA API for GPU programming.
- kentonv 4y ago
- zvrba 4y agoProgram termination is _the_ _perfect_ resource cleanup </s> (at least when running on a decent OS.)
- spacechild1 4y ago> RAII simplifies some of the resource cleanup, but at a cost: if the resource cleanup fails, there’s essentially no way to convey this. RAII at least provides a decent default. After all, most resource cleanup cannot fail, or you cannot do much other than print an error. Now, if you need to catch errors during a particular cleanup, you can still do it manually, but RAII lets you focus on these few cases.
- dwattttt 4y agoIf you need fallible cleanup, but also to try cleanup via RAII, it isn't hard to have a "cleanup" method that signals whether it succeeded, and an "already cleaned up" boolean member the destructor checks.
- pjmlp 4y ago> RAII simplifies some of the resource cleanup, but at a cost: if the resource cleanup fails, there’s essentially no way to convey this. Exceptions.