Ich schreibe einen Windows-Dienst in C++ (Visual Studio 2022, C++20) und der Dienst wurde im Feld unerwartet beendet. Es hing mit einem Update zusammen, das ich lange nach dem Schreiben des ursprünglichen Codes vorgenommen hatte, bei dem eine innere Funktion f für die Dauer ihrer Ausführung ein gesetztes Flag benötigte und dann beim Beenden der Funktion wieder auf nicht gesetzt gesetzt wurde. Um diese Änderung vorzunehmen, habe ich einfach ein paar Codezeilen hinzugefügt, wie unten gezeigt: SetFlag(true) und Scope_guard:
Code: Select all
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 .. */
}
soll einen Fehler auslösen, und in diesem Fall möchte ich, dass die Ausführung weit oben im Aufrufstapel bei einer äußeren Funktion fortgesetzt wird, wo ich bereinige und es dann erneut versuche, indem ich mit einer while-Schleife iteriere. Dies ist natürlich das klassische Problem, wenn Destruktoren auslösen, und ich habe eine Weile damit verbracht, in dieses Kaninchenloch einzutauchen, um die technischen Details und Best Practices zu verstehen.
In meinem Fall fand ich, dass die einfachste und sauberste Lösung darin bestand, f mit Code zu umschließen, der das Flag setzt und löscht:
Code: Select all
void fWithFlagTrue() {
/* complicated flow control, many return paths .. */
}
void f() {
SetFlag(true);
fWithFlagTrue();
SetFlag(false);
}
Das war ziemlich unbefriedigend, weil:
- Mir gefällt mein ursprünglicher zweizeiliger Patch wirklich gut. Ich setze das Flag oben und mit „scope_guard“ kann ich sicherstellen, dass das Flag beim Beenden der Funktion gelöscht wird.
- Mir gefällt meine Lösung wirklich nicht, da es sich um viel Standardwerk handelt, und es scheint, dass es eine weniger langwierige idiomatische Lösung geben sollte.
Ich verstehe, dass es problematisch ist, zwei aktive Ausnahmen gleichzeitig zu haben, und das Unterbrechen des Stapelabwickelns schon sehr problematisch. Wenn in meinem Fall f auslöst, möchte ich, dass die Ausführung in „outer“ endet, wie ich oben beschrieben habe, und der Status des Flags ist nicht relevant. Wenn SetFlag auslöst, gilt das Gleiche:
Ich möchte nur, dass die Ausführung im äußeren endet. Daher denke ich, dass meine Lösung langwierig ist, aber logischerweise ist es das, was ich will. Ich hoffe nur, dass es etwas gibt, das meinem ursprünglichen zweizeiligen Patch näher kommt und das erreichen kann.