When writing exception-safe code, should all private member functions guarantee at least basic exception safety? What would be best/good practice in this situation? Alternatives?
For example, say I have a class Foo
with public
function member DoSomething
, which calls private
function member DoSomeOfIt
. DoSomeOfIt
may fail due to some functions that I cannot influence, and when it does it may leave the Foo
object in a partially modified state which violates Foo
's invariants. So I could wrap DoSomeOfIt
in a try-catch
block and call another private
function member UndoThat
in the catch
block to undo what DoSomeOfIt
did. Individually, DoSomeOfIt
and UndoThat
may not be exception safe, but DoSomething
is. Would that be exception safe code? (In this case with strong guarantee)
class Foo {
public:
// I provide strong exception safety guarantee
void DoSomething() {
try {
DoSomeOfIt();
} catch (const std::exception& e) {
UndoThat();
throw;
}
}
private:
// I provide no exception safety guarantee.
DoSomeOfIt() {
// may throw and violate class invariants.
}
// I provide no exception safety guarantee.
UndoThat() {
// undoes everything most recent call of
// DoSomeOfIt did.
}
};
Of course I could simply include the code of DoSomeOfIt
and UndoThat
in DoSomething
, but that could lead to code bloating, and a long function body, whereas breaking up the functions modularizes the tasks and may make the code more readable(?)
DISCLAIMER: I understand this may be opinion-based. I am not sure if that makes this a bad post, but I'd appreciate any opinions, or experiences where this has lead to issues, or is common practice etc.
This is my opinion.
If there is a possibility that
DoSomeOfIt
will be used by more than one function, it'll be better to have the exception handling code reside in that function itself -- you don't want to have the exception handling code duplicated across multiple functions. If that is not a possibility, your posted code is fine.Having said that, if you move the exception handling code to
DoSomething
, you don't lose anything. In fact, the function becomes better. If it is used in the future by another function, the exception handling is already taken care of. Looked at from that point of view, it will be better to move the exception handling code toDoSomething
.