Wo sollte man assert benutzen?
-
Optimizer schrieb:
@Mastah: Klar, ich hab auch immer unreferenzierte Variablen in meinem Code.
Aus deiner Ironie schließe ich, dass du gemerkt hast wo dein Fehler lag?
-
Optimizer schrieb:
@kingruedi: Nein, das ist nichts anderes. Wenn NULL keinen Sinn hier machen würde, würde die Methode fehlschlagen, aber sie würde keinen Mist machen.
Interessant, du schließt also aus der Tatsache, dass es Funktionen gibt bei denen man NULL als Parameter übergibt, wenn man etwas nicht genauer eingrenzen möchte (ähnlich wie default-Parameter), dass man immer die Parameter überprüfen sollte? Irgendwie vergleichst du gerade Äpfel mit Birnen.
-
MaSTaH schrieb:
Optimizer schrieb:
@Mastah: Klar, ich hab auch immer unreferenzierte Variablen in meinem Code.
Aus deiner Ironie schließe ich, dass du gemerkt hast wo dein Fehler lag?
Mein Fehler?? Wenn ich sage, es führt zu ner Acces Violation, dann kannst du davon ausgehen, dass ich die Variable benutzt habe. Oder glaubst du, dass ich dich anlüge? Ich weiss doch selber, dass ein NULL-Pointer an sich noch nichts böses ist.
-
Optimizer schrieb:
MaSTaH schrieb:
Optimizer schrieb:
@Mastah: Klar, ich hab auch immer unreferenzierte Variablen in meinem Code.
Aus deiner Ironie schließe ich, dass du gemerkt hast wo dein Fehler lag?
Mein Fehler?? Wenn ich sage, es führt zu ner Acces Violation, dann kannst du davon ausgehen, dass ich die Variable benutzt habe.
Dass du die Variable benutzt hast deckt sich aber nicht mit der Aussage, dass die Nullreferenz alleine durch ihre Existenz eine Access-Violation ausgelöst haben soll. Ist ja auch egal.
-
es ist doch eigentlich so (etwas schwarz/weiss): assert ist zur fehler(bug)suche und ausnahmebehandlung zur laufzeit(benutzer)problembehandlung, und dies sind zwei ganz unterschiedliche sachen
programmierte fehler können beim kompilieren und zur laufzeit mit assert erkannt werden, dies sind bugs, die eigentlich nie hätten auftreten dürfen und müssen behoben werden, dafür wird auch die debugversion des programms benutzt, in der releaseversion sind diese programmfehler nicht mehr enthalten, da sie vorher schon durch die debugversion und die asserts gefunden sind, wenn ein assert nicht anschlägt, dürfte ja auch ein anderer test, z.b. in der releaseversion, an gleicher stelle nicht anschlagen, höchstens die stelle wo das assert sich befindet wurde vorher nicht richtig mit der debugversion getestet, aber das sollte wohl erfolgt sein (oder?), der test in der releaseversion z.b. mit ausnahmebehandlung ist bei dieser art von fehler damit ja wohl überflüssig
(wenn man diese tests doch in der releaseversion macht, gibt es aber bei der ausnahmebehandlung unschöne bespiele in einigen programmen, z.b. bei den unrealengine spielen finde ich es sehr sinnlos, wenn irgendein programmierfehler auftritt, welchen der benutzer garnicht beeinflussen kann, und sich dann das spiel mit einer nicht hilfreichen ausnahmemeldung beendet, besser wäre es, wenn sie den fehler vorher mit assert bei der entwicklung, statt beim benutzer mit einer ausnahme, gefunden hätten,
da wäre wohl eine defensivere art der tests und behandlung sinnvoller gewesen, das spiel sollte trotzdem weiterlaufen und diesen fehler zwar loggen aber sonst stillscheigend behandelt/nicht beachtet/defensiv handhaben und nicht sich einfach sinnlos beenden,
die quakeengine spiele sind in dieser beziehung stabiler)fehler die bei der benutzung des programms auftreten, z.b. fehlerhafte benutzereingaben, defekte dateien, ....., müssen immer in beiden versionen (debug und release) behandelt werden, dies sind keine programmierfehler, also nicht mit assert sondern durch "normale" fehlerbehandlung oder mittels durch ausnahmebehandlung impl. fehlerbehandlung,
die behandlung sollte aber ein zuverlässiges verhalten des programms weiterermöglichen, und nicht mit einem absturz oder eine ausnahme beendet werden, so das der benutzer z.b. nicht seine daten verliert
-
MaSTaH schrieb:
Dass du die Variable benutzt hast deckt sich aber nicht mit der Aussage, dass die Nullreferenz alleine durch ihre Existenz eine Access-Violation ausgelöst haben soll. Ist ja auch egal.
Das habe ich auch nicht behauptet. Ich habe die Variable selbstverständlich benutzt, weil eine Referenz (zwar idR über Zeiger implementiert) nichts anderes ist, als ein anderer Name. Die Variable (in dem Fall der Pointer) und die Referenz darauf müssen jederzeit austauschbar sein.
Das war doch von Anfang an klar, dass die bloße Existenz keinen Fehler auslöst, genauso wie der NULL-Zeiger keinen Fehler auslöst.
-
Optimizer schrieb:
@kingruedi: Nein, das ist nichts anderes. Wenn NULL keinen Sinn hier machen würde, würde die Methode fehlschlagen, aber sie würde keinen Mist machen.
Nein, sie schlägt nicht fehl. Wie bereits gesagt, ist das so etwas wie Default Parameter
If this parameter is NULL, then the entire source image is used for the sprite.
If this argument is NULL, the point (0,0,0) is used, which is the upper-left corner.
If this argument is NULL, the point (0,0,0) is used, which is the upper-left corner.
(Quelle: MSDN)
niergend wo steht etwas von Access Violation.
Das ist das gleiche wie zB. in C++
void draw_point(const color &c=black);
-
Es gibt ja auch Parameter, die nicht NULL sein dürfen, das haben wir ja vorhin ausprobiert. Da schlägt die Funktion auch fehl, sie liefert einen Fehlerwert zurück.
-
@Optimizer: Wie schade, daß Du meinen Beitrag ignorierst.
-
Ich habe doch den Test gemacht (gleich der nächste Post).
Die Methode liefert zurück, dass sie fehlgeschlagen ist.
-
Um es kurz zu machen zitiere ich mich mal eben selbst.
Jester schrieb:
Komm mal weg von Deinem DX-Beispiel, das ist aus zwei Gründen nämlich nicht repräsentativ:
-
Man mag von MS halten was man will, aber im gute Schnittstellen definieren sind sie wahrlich keine Meister. Wenn ich sehen will, wie man ne gute Lib schreibt, dann werf ich nen Blick auf die STL.
Nur weil die MFC von CObject erbt ist das nicht gleich gutes Design. -
DX ist eine Bibliothek, die direkt auf die Hardware zugreift. Sozusagen am OS vorbei. Daher ist es wichtig, daß sie falsche Aufrufe erkennt, denn damit kann man nicht nur die eigene Applikation zum Absturz bringen, sondern die Integrität des gesamten Systems beeinflussen. Und das darf ein Betriebssystem niemals zulassen.
- ist zwar etwas schwächer, aber 2) kannst Du nicht ernsthaft ignorieren, insofern verstehe ich nicht, warum Du bei dem Beispiel bleibst.
-
-
2.) Stimmt so nicht. Auf die Hardware zugreifen tun nur Treiber, das sind die einzigen Programme, die wirklich ein System zum Absturz bringen können. Wenn eine DX-Funktion auf NULL zugreift, wird sie vermutlich genauso gekickt wie jedes andere Programm.
Aber darum geht es nicht. Ich will, dass meine Libs, Klassen, whatever sicher sind, IMMER. Und ob ich jetzt dazu - wie vorgeschlagen - asserts auch im Release verwende oder eine if-Abfrage tut da nicht wirklich was zur Sache.---------
Wir drehen uns ständig im Kreis, ich habe doch bereits ausführlich dargelegt, für was ich asserts verwende. Für Situationen, die ich voll und ganz kontrollieren kann, wo meine Programmlogik sicherstellt, dass irgendetwas niemals passiert. -> asserts sind völlig "unnötig", sie erfüllen keine Funktion, sondern sind nur eine Bestätigung, dass meine Programmlogik korrekt ist.Du verwendest asserts auch, um Situationen zu kontrollieren, die du nicht vollständig unter Kontrolle hast und gehst davon aus, dass spätestens beim Release deine Lib richtig benutzt wird. Vermutlich (ein anderer Grund für diese Annahme fällt mir nicht ein) aus Performancegründen.
---------Aus Performancegründen würde ich eine Prüfung nur entfernen, wenn sie zweifelsfrei bei einer Methode der Flaschenhals ist. Ich möchte noch einmal klarstellen, dass ich die Prüfungen auch nur einmal mache. Wenn meine Methode danach noch andere, interne Methoden aufruft, gibt es keine Prüfungen mehr mit Exceptions.
1.) Zu MS und DX: Ich finde die Vorgehensweise von MS gut, sie entspricht der meinen, außer dass die wahrscheinlich wegen C mit Rückgabewerten statt Exceptions arbeiten (In Managed DX wird mit Exceptions gearbeitet).
Im Grunde sind es nur unterschiedliche Ansichten, ich will immer die Sicherheit, dass meine Lib keinen Murks baut, und verzichte nur im Zweifelsfall auf eine Prüfung. Es kommt ja gar nicht darauf an, ob ich das mit Exceptions oder sonstwie mache. Du sagst, es reicht, im Debug Build zu prüfen, dann bieten sich asserts wohl an.
-
Optimizer schrieb:
Aber darum geht es nicht. Ich will, dass meine Libs, Klassen, whatever sicher sind, IMMER.
Aus reinem Interesse, wie gehst du mit Thread-Sicherheit um?
-
Ich denke die Diskussion macht eh wenig Sinn. Wenn Optimizer es für nötig hält ständig alles überall zu prüfen, dann bringt es auch nichts jetzt auf ihn einzureden. Auch wenn es für den Rest von uns logisch scheint, dass man Eingaben lieber am Ort der Eingabe prüft und auf den Stupiden-Prüf-Overhead im Release verzichtet.
-
kingruedi schrieb:
Ich denke die Diskussion macht eh wenig Sinn. Wenn Optimizer es für nötig hält ständig alles überall zu prüfen, dann bringt es auch nichts jetzt auf ihn einzureden. Auch wenn es für den Rest von uns logisch scheint, dass man Eingaben lieber am Ort der Eingabe prüft und auf den Stupiden-Prüf-Overhead im Release verzichtet.
Ein wahres Wort.
Wie gesagt, wenn eine Prüfung mal wirklich der Flaschenhals sein sollte, dann entferne ich sie schon auch.@void: Ich hab mich mit Threads noch nicht auseinandergesetzt (hey ich lern grad 3 Sprachen gleichzeitig
).
-
Optimizer schrieb:
wie gesagt, wenn eine Prüfung mal wirklich der Flaschenhals sein sollte, dann entferne ich sie schon auch.
Und wie machst Du das, wenn zwei Projekt die Lib benutzen, die eine verläßt sich drauf, daß die Checks durchgeführt werden, für die andere ist es zu lahm... machst Du dann ne Kopie und modifizierst eine? Wie erklärst Du dann Deinem Chef, daß in Zukunft zwei Libs gewartet werden müssen? Aber auch das hatte ich oben schonmal geschrieben.
Und jetzt geb ich es glaub ich auf.
MfG Jester
-
333 schrieb:
Wo sollte man assert benutzen?
in c.
in c++ hat assert nix mehr zu suchen.
a) die netten meldungen "fehler in datei sowieso.cpp in zeile 42" schicke man sich im debug-code auch seinem debugger und im release-code kommen assertions nicht vor.
b) der harte abbruch macht ja jede fehlersuche schwer. warum nicht mit "__asm int 3" oder ähnlichem den debugger auf die kritische zeile setzen. dann kann man sofort die variablen prüfen und muß nicht erst rausfinden, wie man den fehler nochmal rekonstruieren kann.
c) es fliegen keine destruktoren. und was ist, wenn die konsistenz meiner datenbank auch von destruktoren abhängt? also werfe man tunlichst nach dem debuggeraufruf noch ne exception und fange main grundsätzlich spätestens in der main.falls wir von nem neuen makro ASSERT ausgehen (schrieb man in c nicht bereits makros GROSS?), das obiges tut, dann wird die frage langsam interessant.
dann ist ASSERT bei fehlern zu setzen, die gar nicht vorkommen können, falls der programmierer die eingaben (auch von der platte und so) korrekt abtestet. die bibliotheken werden einfach zu lahm, wenn sie idiotenfest sein sollen. idiotenfest ist zum beispiel, wenn ein stack ne exception wirft, weil er leer war und gepoppt wird. man gehe immer davon aus, daß der bibliotheksanwender programmieren kann und lasse schliche den potenziellen absturz da. in debugversion gerne ne dialogbox, ein logfile, ne exception, ein debuggeraufruf, ein signalton und ne mail an microsoft (alles in MSASSERT drin). aber im releasecode bitte keinen unfug. "zero cost" muß devise bleiben, c++ ist nicht lahmer als assembler. wer damit nicht leben kann, nehme einfach j*v*.
-
jo, dass __asm__("int $0x03"); ist schon recht sinnvoll (auch unter C :)). Deswegen kann man ja folgendes machen (für gcc/g++)
#ifndef NDEBUG #undef assert #define assert(cond) ((cond)?(void)0:my_assert_failed()) #ifdef IA32 inline void my_assert_failed() { __asm__("int $0x03"); } #else inline void my_assert_failed() { raise(SIGTRAP); } //sollte unter Unix-like systemen gehen (falls kein IA32) #endif #endif
-
Was macht asm int 3? Warum ruft man einen Interrupt auf wenn man an eine Codestelle springen will? Kann mich jemand aufklären?
-
mit int 3 wird der interrupt 3 aufgerufen, welcher den Debugger startet.