"I.e. instead of manual close calls you let the destructor handle it. This is both exception-safe and spares you remembering to add a close call on every possible exit-path."
Unless closing the handle causes an exception to be thrown. Either you never see the exception or your program aborts, and neither one is particularly good if you want to write reliable code.
"Unless closing the handle causes an exception to be thrown."
That is a straw-man argument. If the underlying file-closing API can throw exceptions, they must be caught from the destructor. If that means that they get ignored, then that is what it will have to be (much the same as most C programs ignore the return call from `printf`). Also in this case, the RAII wrapper should provide a member function which can execute the underlying close call early and expose the failure, for users who may be interested in guaranteed reliability.
In any case, provide RAII _safety_ does not intrinsically reduce reliability. If your destructor is executing code which _must_ succeed or be handled at a higher level, then a good developer will not put it in the destructor. No language feature can solve the question of "where should the program stop caring about failure?"
> in this case, the RAII wrapper should provide a member function which can execute the underlying close call early and expose the failure, for users who may be interested in guaranteed reliability.
Indeed; this is a useful technique. However, it means that you now have a close() method which you have to call on every exit path, because if you miss a path, that's a path which could have an exception thrown from a destructor. And that basically means you're not doing RAII anymore.
So yes, RAII is intrinsically incompatible with this kind of reliability.
> However, it means that you now have a close() method which you have to call on every exit path, because if you miss a path, that's a path which could have an exception thrown from a destructor.
Not true at all. `close` and the destructor should be idempotent. In the main path, `close` will get called and failure will propogate upward. In any other failure path, the destructor will attempt to perform the underlying close and allow the stack to unwind without any further interruption.
Of course, anyone can come up with pathological cases where this is not acceptable behavior from a high-reliability sub-component. And in such a situation, RAII may not be the best answer (nor would lexical scoping i.e. constructor/destructor, for that matter!) But in my experience, this approach is just fine for most application-level code.
Unless closing the handle causes an exception to be thrown. Either you never see the exception or your program aborts, and neither one is particularly good if you want to write reliable code.