Sauberer Code trotz goto ???
-
volkard schrieb:
Janjan schrieb:
Lösung = if + bool
Sauberer Code trotz goto geht nicht.Ganz schlecht.
Aber sowas von nicht schlecht du goto-Liebhaber!
-
Bitte ein Bit schrieb:
Frage: Gibt es eine Möglichkeit das Ganze in einem sauberen Stil zu schreiben ?
Das *ist* guter Stil, soger allerbester Stil in C. Besser als lokal mit Exceptions herumzuwerfen oder künstliche Flags oder state machines oder deleteMitZeigerNullen oder ummantelnde Ressourcenhaltefunktionen. Bin mir sicher, mit genügend Zeit kann man Dutzende von Pseudolösungen finden, die aus dem Dilemma nicht wirklich herausführen. In C++ mit RAII haben wir genau für dieses Problem einen eleganten Ausweg und brauchen goto nicht mehr.
-
@Fellhuhn
Ups, hast Recht. Ich hatte vergessen dass ein #define nichts anderes als ein Wortersetzungssystem ist.
-
In C++ gibt es das erwähnt RAII und zur Fehlerbehanldung Exceptions.
void DoSomething(unsigned int a) { array<int, 100> m; array<double, 100> n; // oder vector<int> m(100); vector<double> n(100); //... if (a > 5) { try{ MyFunction(m, n, a); // throws for (unsigned int i = 0; i < a; i++) { MyFunction2(i); // throws if (a*a > 100) { MyFunction3(m, i)) // throws else if (a * a > 1000) throw logical_error("Error XYZ"); } } catch(exception & e) { ShowMessage(e.what()); } } // cleanup done in dtors }
-
brotbernd schrieb:
try{ //... else if (a * a > 1000) throw logical_error("Error XYZ"); } } catch(exception & e) { ShowMessage(e.what()); }
Exceptions zu benutzen um innerhalb der Funktion rumzuhüpfen ist nicht wirklich besser als goto. Exceptions sind dazu da um Fehler über Funktionsgrenzen hinweg weiterzugeben. Im besagten Fall kannst du an Stelle des throw gleich dein ShowMessage einbauen und mit return aus der Funktion verduften.
volkard schrieb:
Das *ist* guter Stil, soger allerbester Stil in C.
In C ist das guter Stil, ja. Aber es geht ja um C++, und auch in der Beziehung ist C++ eben nicht mit C zu vergleichen, du sagst es ja selbst:
In C++ mit RAII haben wir genau für dieses Problem einen eleganten Ausweg und brauchen goto nicht mehr.
RAII ist die sauberere und wartbarere Alternative zu goto in C++, und in Punkto Exceptionsicherheit ist goto völlig daneben und deswegen in C++ ganz anders als in C schlechter Stil.
Und bevor jetzt jemand sagt "aber wenn ich garkeine Exceptions hab ist es auch kein schlechter Stil.", dem sei erwidert, dass Exceptions ein allgegenwärtiges Sprachfeature von C++ sind. Wenn man selbst sich plötzlich entscheidet, Exceptions einzusetzen oder eine Bibliothek einzusetzen, die Exceptions verwendet, dann muss man plötzlich sein goto-Gefrickel doch umstellen. => goto ist auch in Abwesenheit von Exceptions schlechter Stil weil es die Erweiterbarkeit unnötig einschränkt.Nachtrag: wie alle Diskussionen die das Wort "Stil" enthalten ist auch diese zu mindestens 50% subjektiver Natur
-
pumuckl schrieb:
Exceptions zu benutzen um innerhalb der Funktion rumzuhüpfen ist nicht wirklich besser als goto. Exceptions sind dazu da um Fehler über Funktionsgrenzen hinweg weiterzugeben.
Das Programm bestand aber nur aus einer Funktion.
pumuckl schrieb:
Im besagten Fall kannst du an Stelle des throw gleich dein ShowMessage einbauen und mit return aus der Funktion verduften.
Welchen Vorteil habe ich davon, diesen Fehler an anderer Stelle zu behandeln als die anderen. Wenn ich mich später entscheide z.B. einen Fehlerbericht zu schreiben, führe ich im catch Block entsprechende Funktionalität hinzu. Fehler die sich irgendwo anders verabschieden, schummeln sich dann daran vorbei. Ich kann das natürlich auch in eine HandleError Funktion oder was weiß ich kapseln, aber einen wirklichen Nachteil der 'lokalen' Ausnahme seh ich jetzt nicht (ich mach das selber aber auch fast nie, weil meine Programme meistens umfangreicher als eine Funktion sind). Bezüglich eventueller Probleme lass ich mich aber gerne belehren.
-
pumuckl schrieb:
volkard schrieb:
Das *ist* guter Stil, soger allerbester Stil in C.
Aber
Ups. Die Satzzeichen waren total falsch.
Wollte sagen, daß es in C bester Stil ist. In C++ gar nicht.
-
Eine noch ungenannte Lösung (zumindest sehe ich sie auf Anhieb nicht) ist auch einen Teil der Funktion in eine zweite auszulagern, und diese bei Bedarf mit return zu beenden (und dieser als Parameter die nötigen Daten mitzugeben).
Spricht goto durch return in einer Funktion auslagern.
-
asc schrieb:
Eine noch ungenannte Lösung (zumindest sehe ich sie auf Anhieb nicht) ist auch einen Teil der Funktion in eine zweite auszulagern, und diese bei Bedarf mit return zu beenden (und dieser als Parameter die nötigen Daten mitzugeben).
Spricht goto durch return in einer Funktion auslagern.
Hab ich als "ummantelnde Ressourcenhaltefunktionen" erwähnt und als nicht so gut wie RAII abgetan.
-
volkard schrieb:
asc schrieb:
Eine noch ungenannte Lösung (zumindest sehe ich sie auf Anhieb nicht) ist auch einen Teil der Funktion in eine zweite auszulagern, und diese bei Bedarf mit return zu beenden (und dieser als Parameter die nötigen Daten mitzugeben).
Spricht goto durch return in einer Funktion auslagern.
Hab ich als "ummantelnde Ressourcenhaltefunktionen" erwähnt und als nicht so gut wie RAII abgetan.
Für den Punkt, dass es hier um die Ressourcen geht stimme ich dir zu. Allerdings kann man bei Tiefen wie hier schon mal dran denken das in Funktionen auszulagern.
if{for{if{if{..}}}}
ist imo schon recht tief.
Noch nicht schreklich, aber man sollte sich da schon mal überlegen, ob es besser geht.
-
Dravere schrieb:
Das sind aber ATL/MFC Klassen. Die bekommt man nicht gratis, dazu muss man sich eine Visual Studio Version Standard oder besser kaufen. Sowas kann man sich aber relativ leicht auch selber nachbauen, auch wenn es am Ende nur ein simpler Guard ohne Funktionen ist. Allerdings denke ich, dass es im Internet bereits fertige Klassen gäbe, wobei ich keine direkt kenne.
Grüssli
Dann bastelt man sich eben nen schönen kleinen RAII-Wrapper mit ein wenig spicken bei boost::any:
class RAIIClose { class placeholder { public: virtual ~placeholder(){} }; template< typename ValTy, typename Func > class holder : public placeholder { ValTy &held; Func func_; public: holder( ValTy & value, Func func ) : held( value ), func_( func ) {} ~holder() { func_( held ); } }; placeholder * content; public: template< typename ValTy, typename Func > RAIIClose( ValTy & value, Func func ) : content( new holder< ValTy, Func >( value, func ) ) {} ~RAIIClose() { delete content; } }; //Usage: { HKEY hkey; HANDLE file; RAIIClose x1( hkey, RegCloseKey ); RAIIClose x2( file, CloseHandle ); //... }//RAII räumt für uns auf
Bei häufiger Verwendung würd ich aber, wie Dravere schon gesagt hat, selbst was schreiben oder sowas verwenden:
#define HANDLE_WRAPPER( HW_HANDLE, HW_FUNC, HW_NAME ) \ class HW_NAME \ { \ HW_HANDLE handle_; \ public: \ operator HW_HANDLE&(){ return handle_; } \ \ void operator=( const HW_HANDLE& handle ) \ { handle_ = handle; } \ \ ~HW_NAME() \ { HW_FUNC( handle_ ); } \ };; HANDLE_WRAPPER( HKEY, RegCloseKey, wRegKey ); HANDLE_WRAPPER( HANDLE, CloseHandle, wMutex ); //Usage { wRegKey hkey = HKEY_USERS; wMutex m = CreateMutex( NULL, TRUE, NULL ); //... }//RAII räumt für uns auf
Vielleicht kann man das zweite noch irgendwie in ein Template packen, bin grad nur zu müde zum rumprobieren.
-
volkard schrieb:
pumuckl schrieb:
volkard schrieb:
Das *ist* guter Stil, soger allerbester Stil in C.
Aber
Ups. Die Satzzeichen waren total falsch.
Wollte sagen, daß es in C bester Stil ist. In C++ gar nicht.volkard schrieb:
SATZZEICHEN KÖNNEN LEBEN RETTEN
Sauberer Code trotz goto ???
wenn einer die frage stellt, dann ist sein code nicht sauber.