Your condescending tone is unwarranted.
I was exasperated/flabbergasted, not trying to be condescending but tone is hard to convey over the interwebs, so I apologize if it came across as condescending.
No, I'm pointing out the problemspace where exceptions (unwinding the call stack) and RAII meet.
That only exists if you choose it to. You can disable exceptions at the compiler level. Stating that RAII is broken because exceptions can double throw is not a valid argument. Not only are they separate features, but its also near impossible to accidentally throw from a destructor.
Signal handlers are documented as "software interrupts" or "OS interrupts" since they are implemented similarly (an address is registered as a place to call into when a given event occurs). You can write these as well as hardware interrupt tables in C, (or C++ if you like).
Similar, but not the same. Signal handlers can handle RAII without any problems (and exceptions with a little care).
RAII is not: "just a function call". RAII is pairing initialization of a resource with acquiring (often allocating) the resource, AND utilizing automatically generated code which calls destructors prior to leaving a function as your resource releasing method.
Exactly... a function call. From a theoretical perspective, sure it means a lot more, but on the practical end its just that, a simple function call (often inlined). A destructor call doesn't magically jump to other threads/contexts, it executes in the context of the function that the object was created in.
RAII dictates a lifecycle of "resources" which is tied to program execution flow, and is usually not easily compatible with heavy resources, like sockets or files or memory-mapped IPC, etc. Works fine for trivial RAM-only resources, but RAII is not a panacea for all your resource management ills. That the gist I'm getting at.
It works anywhere a _execute_on_scope_exit(...) type macro/function/intrinsic would.
If you're using RAII it means your clean up code happens prior to a function returning. This conflicts with the potential of calling cleanup code from a different context such as in a signal handler or other interrupt (such as the floating point exception handler). My point is that this is one instance where RAII will not work. If you delete some RAM or .close() a file, etc. due to SIG_HUP or SIG_TERM, etc, and then you used RAII elsewhere to delete or .close() or otherwise release the same resources, then you've got a problem. That problem is called: Manually managing resource life cycle, and you'll be writing code that obviates the need for RAII to solve that issue.
See, you're jumping all around conflating issues from multiple sources.
Ok lets go with this file class example. Its not uncommon to have a Close() function which closes an opened file. This would be an optional function. If you destroy an opened file object, the destructor would close the file, no exceptions thrown, no way to indicate an error occurred if it occurred. Close() on the other hand throws an exception if an error occurs, otherwise it closes successfully. Either way there's no risk of double destruction, resource leakage, or double exception throws.
Why does this work? Because its pretty trivial for the destructor to track the state of the object and react accordingly. In fact that's kinda the point of a destructor.
Now if you want to allocate resources in one thread/context and clean them up in another, just new/delete (or use a shared_ptr).
I'm not saying that naive RAII handles every resource clean-up situation ever, but it does handle the VAST MAJORITY.
That's the issue. Computers do more than one thread of execution and even a single core processor runs on an OS which can preempt execution to call hardware / software interrupts, notify of system shutdown, query for execution status, notify of IPC, mouse/keyboard event pumps, etc.
None of which affects RAII. If a thread is pre-empted to run an interrupt, irregardless of whether it was in a destructor or not, it still has to resume execution as if it hadn't been pre-empted. If it didn't, even without RAII, you'd have programs breaking all over the place. And if you're working with a shared resource, such that a pre-emption could cause corruption or unforeseen state change, then you better protect it with a mutex (or similar). But again, it has nothing to do with RAII.
If you use RAII with C++ you're probably going to have to contend with the fact that an exception can be the cause of executing RAII clean up code. This isn't hard to understand. RAII generated cleanup code can be called due to an exception being thrown. I never said RAII is an exception. This is a misunderstanding on your part. I'm explicitly talking about RAII clean up code being called in the context of an exception unwinding the stack.
You keep bringing up problems with exceptions, and conflating them with problems with RAII. You can double-throw even without RAII. In fact its actually near impossible to accidentally double throw from a destructor.
https://akrzemi1.wordpress.com/2011/09/21/destructors-that-throw/There's this weird paranoia surrounding exceptions for some reason. Repeatedly I read weak arguments against exceptions followed by the usual 'bang your dead' or 'game over' or something to that effect. Its not a big deal. If you're in a situation where your exception handling code failed (either due to a file error, lack of memory, or a device/driver broke) there's little you can do apart from std::terminate() anyways (and don't start with the 'but what about hardware interrupts'... no one's suggesting using exceptions in hardware interrupts).
It does exactly what one would expect... I don't understand the issue. If proper closing of the file matters (ie. you are writing to it not just reading, or its not a temp file, or is necessary in some manner) and you can/want to recover if it fails, you call close(). If you don't care, you don't. Either way, the destructor doesn't need to throw, and can clean up if necessary.
You're now aware that closing a C++ fstream can throw an exception.
Calling close() can, not destructing it.
You previously stated you'd put the file closing stuff in the destructor. If an exception is thrown and this unwinds your stack, calling fstream's destructor thanks to RAII, then your stated solution is at odds with not only "don't put code in destructor that can throw" but also with the rule "you can't really throw another exception while an exception is unwinding the stack". Let's say you put a try/catch in the destructor of a wrapper class to close the fstream. Now you can't report the failure to close the file. Point being: the RAII clean up code is throwing an exception. If not using RAII you could wrap the fstream.close() in a try/catch and report the failure to close even when your try/catch .close() is called due to an exception being thrown.
If you're going to talk in hypotheticals, then stick to that. If you want specifics about a particular implementation, then stick to that; but you can't switch back and forth at a whim in an attempt to point out 'contradictions'. My 'put file close code in the destructor' response was a simple/short response to a hypothetical scenario, it wasn't meant as an in-depth response on the intricacies and design decisions of ofstream.
I've already pointed out the obvious solution above, and that is exactly that ofstream does. There's no contradiction. IMO its logical, safe, and easier to use than other solutions.
I'm done with this discussion. Sorry for blaspheming your god, but my criticism was valid.
All I got was:
- You don't think exceptions should be used in hardware interrupts. Which I agree with, but I have no idea what it has to do with RAII in the context of a game development forum.
- You seem to imply that destructors can cause context/thread switching (somehow via software interrupts/signal handlers), but don't seem to want to state it explicitly. You keep bringing up this idea that handlers/interrupts or other types of ambiguously defined things can cause problems with RAII, but haven't stated how or why. I'm still unsure whether you think that signal/interrupts handler code should not have RAII, or that an interrupt/pre-emption from a signal/interrupts handler in a destructor call is the issue. This branched off into the 'what if I want to destroy resources in the context of a handler/interrupt' but again, did you mean allocate in one context, destroy in another? Or... I don't know really. These are such esoteric situations that you'll need to be much more precise.
- You don't like exceptions.
Was there something I missed?