Wo sollte man assert benutzen?
-
Hi,
ich frag mich grade wo man assert benutzen sollte? ich seh darin irgendwie keinen großartigen sinn?
Kann jemand ein beispiel geben wo sich assert lohnen würde?
-
Ein Assert sollte man benutzen, wenn man etwas sicherstellen möchte, was eigentlich laut Programmlogik sicher ist -> ein assert sollte niemals "zünden"
Wenn du solche Sicherstellungen regelmäßig einbaust, kannst du schon frühzeitig Bugs verhindern.
-
Bei Logikfehlern. zB mache ich Bounds-Checking bei Arrays immer mit assert. Oder Vorbedinungen -> da nehme ich auch gerne assert.
Im Prinzip alles wo ich mir denke: der Programmierer muss einen Logikfehler/Denkfehler machen, damit dieses assert Fehlschlägt.
Der Sinn: wenn wir einen doofen Fehler machen, zB
for(int i=0; i<=size; ++i) cout<<arr[i];dann bringts in der Debug Version einen Fehler - aber in der Release Version wäre es verschwendung, weil eben der Code bis dahin korrekt ist (zumindest sein sollte)
-
wo issn da ein fehler?!?!?!
-
333 schrieb:
wo issn da ein fehler?!?!?!
Beim Zugriff auf arr[size] gibts einen Segfault; das "<=" sollte "<" heißen.
-
Praktisch sind asserts auch, wenn man keine Fehlerüberprüfung machen will, weil die eigentlich schon passiert sein sollte.
Zum Beispiel
// p darf nicht 0 sein void myfunc(const char * p) { assert(p); //... }
Es ist die Verantwortung des Benutzers dieser Funktion, daß p nicht 0 ist. Dennoch kann ich ihn mit diesem assert darauf hinweisen, daß er da nen Fehler gemacht hat. Gleichzeitig habe ich aber im Release keinen Laufzeitverlust.
Da die Funktion nicht mit 0 aufgerufen werden darf (steht im Kommentar) spare ich mir auch das abprüfen von selbigem. Sonst passiert es nur, daß auf jeder Abstraktionsebene alle Sachen nochmal überprüft werden und das kann sich mit der Zeit schonmal aufsummieren.MfG Jester
-
@Jester
aaaaah also jetzt versteh ich wo man assert anwenden kannbig thx!
-
@Jester: Genau diese Vorgehensweise finde ich jetzt aber nicht gut. Wenn ich erst eine Benutzereingabe mach und dann den String deiner Funktion übergebe, kommt es zu einem Fehler im Debug Build und zu undefiniertem Verhalten (oder in diesem Fall wahrscheinlich Acces Violoation) im Release-Build.
Asserts sollte man für Situationen verwenden, die Kraft eigener Programmlogik gar nicht möglich sind, deshalb verschwinden sie ja auch im Release-Build.
Benutzereingaben, Lesefehler oder sonstiges kommen im Release-Build auch vor, sowas sollte man mit Exceptions behandeln.
-
Hi,
ja, der Meinung bin ich auch. Eine Lib sollte z.B. auch im Releasebuild (also Lib im Releasebuild, so wie sie ja normalerweise ausgeliefert wird) alle Eingaben die von außen kommen als ungültig behandeln, bis sie ausdrücklich validiert wurden. Dazu gehört strenggenommen sogar, dass man nicht nur p != 0 prüft, sondern auch, dass p auf einen gültigen Speicherbereich zeigt, was IIRC aber nur mit plattformspezifischen Funktionen geht.
ChrisM
-
edit: habt ihr meinen Beitrag überhaupt gelesen? Es geht da nicht um eine Lib und die dort angebotenen High-Level-Operationen, sondern um die verschiedenen Abstraktionsebenen. Nur die oberste muß wirklich die Konsistenz prüfen.
@Optimizer: Du bist also für folgende Variante:
// unterste Abstraktionsebene void myfunc(const char * p) { if(!p) { //Fehlerbehandlung } } // mittlere Abstraktionsebene void performOperation(const Part & part) { if(! part konsistent) { Fehlerbehandlung } myfunc(part.pointer); // ... } // höchste Abstraktionsebene void complexOperation(const Object & o) { if(! o konsistent) { Fehlerbehandlung } performOperation(o.part); //... }
Die schönen redundanten Tests machen den Code sicher sehr performant.
Ansonsten verweise ich an dieser Stelle einfach mal auf Large Scale C++ Software Design von John Lakos, an dessen Beispiel obiges (das aus dem vorigen Beitrag) angelehnt ist.MfG Jester
-
Optimizer: Du hast Jesters Posting missverstanden. Wenn du in einem Programm eine Benutzereingabe machst, dann sollte idealerweise nur an einer einzigen Stelle geprüft werden, ob die Eingabe gültig ist. Nicht in jeder Funktion, die irgendwann mal die Eingabe bearbeitet. Nimm z.b. das:
double wurzel(double x) { if (x < 0) throw "Wurzel aus negativer Zahl??!"; else return sqrt(x); } int main() { double x; do { cout << "x >= 0 eingeben: "; cin >> x; } while (x < 0); cout << "Wurzel: " << wurzel(x); }
Wozu die zusätzliche Prüfung in wurzel()? Ein assert würde es auch tun, denn wurzel hat eben die Vorbedingung, dass das übergebene Argument nicht-negativ ist, und die Eingaberoutine sorgt auch dafür, dass nur gültige Eingaben angenommen werden. Ein assert würde in aggressiver Weise dafür sorgen, dass die Prüfung der Eingabe an einer Stelle konzentriert ist.
-
Moment mal, ich kann nicht hellsehen, ich weiss nicht, wo das her ist.
Wenn das jetzt so gemeint war, dass das nur ne kleine, private Hilfsmethode ist, die garantiert kein anderer füttert als ich, dann werd ich da sicher auch keine Exceptions benutzen.
Aber dann ist ja auch Kraft eigener Programmlogik sichergestellt, dass nichts passiert. Exceptions sollte man benutzen, sobald man keinen Einfluss darauf hat, was man bekommt und für mich hat das Stück Code so ausgeschaut, als wird da irgendwo mal ein String eingelesen.
-
@Optimizer
Benutzereingaben, Lesefehler oder sonstiges kommen im Release-Build auch vor, sowas sollte man mit Exceptions behandeln.
dagegen hat auch niemand etwas gesagt. Nur wenn ich eine Library oder allgemeine Funktion schreibe, weiss ich ja nicht, ob es sich um die Benutzereingabe etc. handeln könnte, deswegen hat hier dann der Programmierer selber vorsorge zu treffen.
assert dient eben nur dazu, dass dem Programmierer die Fehlersuche erleichtert wird und nicht irgend wo das Programm zusammen bricht plötzlich.
-
@Bashar: Ok, darauf können wir uns einigen.
-
Hi,
mag sein, ich bleibe trotzdem bei meiner Meinung, dass eine Libs alle Eingaben validieren sollte, auch wenn sie natürlich nicht wissen kann, ob das nicht schon von der ansteuernden Anwendung geschehen ist. Für mich ist die Lib ein eigenes Modul, das in sich geschlossen stabil laufen muss, auch wenn es ungültig angesteuert wird.
ChrisM
-
@ChrisM: Ich glaube, wir meinen alle das selbe. (Und das beinhaltet auch, dass alle Eingaben geprüft werden).
Mich hat nur Jesters Beispiel etwas verwirrt.
-
Jo, so war's auch gemeint. Wichtige Voraussetzung ist natürlich, daß die Vorbedingungen spezifiziert und dokumentiert sind. Und dann stelle ich mich irgendwie schon auf den Standpunkt, daß der Benutzer (damit meine ich den, der meine Funktion benutzt) dafür verantwortlich ist, daß er meine Funktion korrekt benutzt. Und um ihm das Leben etwas leichter zu machen helfe ich mit dem assert nach, damit er auch gleich merkt, wenn er sich nicht dran hält.
MfG Jester
-
Hi,
nein, ich habe Jester schon verstanden.
Trotzdem bin ich der Meinung, dass die Funktion ein definierbares Verhalten (z.B. Exception) zeigen sollte, wenn z.B. 0 übergeben wird, obwohl in der Dokumentation bzw. einem Kommentar steht, dass p nicht 0 sein darf.
EDIT: Eine andere Möglichkeit wäre es natürlich, es so zu machen, wie Microsoft bei DirectX und gleich noch Debugversionen der DLLs im SDK mitauszuliefern, die dann natürlich viele zusätzliche Sicherheitschecks beinhalten.
ChrisM
-
Wenn ich bestimmte Bedingungen fordere, dann muß ich auch davon ausgehen können, daß sie eingehalten werden, sonst habe ich in jedem Fall das Problem, daß ich redundante Prüfungen habe. Und mit den asserts wird die Eingabe ja wunderbar validiert, wer's falsch benutzt kriegt richtig eins auf den Deckel und muß sich drum kümmern es richtig zu machen.
MfG Jester
-
Hi,
ja, aber das assert() ist im Auslieferungsbuild ("release") ja nicht drin.
Lies mal oben mein Edit, das kam nach deinem Post eben. Können wir uns darauf einigen?
ChrisM