I'm writing a Windows service in C++ (Visual Studio 2022, C++20), and the service was unexpectedly terminating in the field. It was related to an update I made long after writing the original code, where an inner function f needed a flag set for the duration of its execution, and then returned to unset at function exit. To make this change I just added a couple of lines of code, as shown below - the SetFlag(true) and scope_guard:
void outer() {
while(true) {
try {
many_nested_functions_leading_to_f();
}
catch (const MyException&) {
/* handle exception and then retry with the outer while loop */
}
}
}
void SetFlag(bool) {
/* can throw MyException */
}
void f() {
/* We need flag to be true for the duration of this function */
SetFlag(true);
auto guardClearFlag = sg::make_scope_guard([] { SetFlag(false); });
/* complicated flow control, many return paths .. */
}
SetFlag is designed to throw on error, and in that case I want execution to resume way up the call stack at an outer function, where I clean up and then retry by iterating with a while loop. This is of course the classic issue when destructors throw, and I spent a while descending into that rabbit hole to understand the technical details and best practices.
In my case I found that the simplest and cleanest solution was to wrap f with code that sets and clears the flag:
void fWithFlagTrue() {
/* complicated flow control, many return paths .. */
}
void f() {
SetFlag(true);
fWithFlagTrue();
SetFlag(false);
}
This was rather unsatisfying because:
I really like my original two-line patch. I set the flag at the top and with scope_guard I can ensure the flag is cleared on function exit.
I really don't like my solution since it's a lot of boilerplate, and it seems that there should be a less tedious idiomatic solution.
I understand that having two active exceptions at once is problematic, and interrupting stack unwinding is very problematic. In my case if f throws then I want execution to end up in outer as I described above, and the state of the flag isn't relevant. If SetFlag throws the same applies: I just want execution to end up in outer. So I think my solution is long-winded but it's what I want logically. I'm just hoping there is something closer to my original two-line patch that can achieve this.