Wo sollte man assert benutzen?
-
Dann bau dir doch ein eigenes abschaltbares assert, was im Fehlerfall Exceptions wirft, anstatt das Programm abzubrechen. Und schalte ASSERTIONS_ON auch im Release nicht ab.
# ifdef ASSERTIONS_ON # define my_assert(COND) ((COND)?(void)0:throw AssertionViolation(__FILE__, __LINE__, #COND)) # else # define my_assert(COND) # endif class AssertionViolation : public std::logic_error { public: AssertionViolation(const char * file, unsigned int line, const char * condition); };
-
davie schrieb:
Dann prüfst du sicherlich auch alle Referenzen, die du bekommst, ob sie nicht verschleierte null Zeiger sind?
Nein, das prüfen die Betriebssysteme für die ich programmiere.
Ihr glaubt immer, ich geb mich mit der halben Performance zufrieden, nur damit ich die Prüfungen habe, aber ihr überschätzt die paar Tests ganz extrem. Das sind ein paar Methoden, die man von außen benutzen kann und die rechnen jetzt irgendein kompliziertes Zeug aus und rufen weitere interne Methoden auf - da macht doch eine simple if-Abfrage nichts mehr aus (sagt zumindest auch mein Profiler und QueryPerformanceCounter()).
Also, was zum Geier spricht jetzt gegen ein paar Abfragen?@Bashar, das hat nichts mit Exceptions an sich zu tun, sondern ich verwende sie deshalb, weil die Bedingung immer geprüft werden soll. Ich könnte auch mit Rückgabewerten arbeiten, aber das mach ich aus verständlichen Gründen nicht. Es geht darum, dass die Prüfung gemacht wird, nicht um die Art der Fehlerbehandlung.
davie schrieb:
Wenn jemand undefiniertes Verhalten will, dann kriegt er es auch, egal wie gut du deine lib absicherst.
Nein, kriegt er nicht. Außer wir bleiben jetzt wirklich bei so einfachen Sachen wie eine Wurzelfunktion, wo ich dann auch keine Prüfung machen würde.
-
Hmm. Hier wird immer gesagt, assert ist im Release-Modus deaktiviert. Das ist natürlich nicht ganz richtig. Ob Assertions geprüft werden, geht danach ob NDEBUG definiert ist. Lass doch NDEBUG einfach aus, auch im Release.
-
Optimizer schrieb:
Also, was zum Geier spricht jetzt gegen ein paar Abfragen?
Nichts, aber es spricht etwas gegen Redundanz. Wenn eine Funktion einen falschen Wert bekommt, weil der User diesen nicht überprüft dann reicht ein assert. Wenn ein Programmierer so blöd ist und logische Fehler macht (wie z.B. divide mit 0 oder sqrt mit einem negativen double-Wert) dann ist er schuld. Netterweise fliegt ihm ein assert um die Ohren das auch noch nichts kostet, weil der Compiler es im Release-Build ignoriert (wenn NDEBUG deaktiviert ist). Exceptions sind für andere Sachen gedacht.
-
Um jetzt nochmal bei der Funktion, die den const char * erwartet zu bleiben.
Wenn ich spezifiziere, daß der nicht 0 sein darf, dann ist es ein Programmierfehler, wenn er 0 ist, oder?
Und daran ändert es auch nichts, wenn der string eine Eingabe des Benutzers ist. Wenn er da was blödes eingibt (oder nix eben), dann kommt ein Pointer auf "" rein und nicht 0, das ist ein kleiner aber feiner Unterschied.Btw.: Wir überprüft Dein OS denn, das eine Referenz gültig ist:
int * x=0;
int & y = *x;
// und los gehtsMfG Jester
-
Jester schrieb:
Btw.: Wir überprüft Dein OS denn, das eine Referenz gültig ist:
int * x=0;
int & y = *x;
// und los gehtsMfG Jester
Da geht gar nichts los. Das führt zu einer Acces Violation unter Windows und Linux. Ich habe es jetzt sogar extra ausprobiert.
@Bashar: Natürlich kann ich auch asserts im Release haben. Finde ich auch voll ok. Mir geht es ja nur darum, dass die Prüfung stattfindet.
Und ich nehme halt jetzt Exceptions, weil ich ein anderes Verständnis von asserts habe (siehe oben) und mische mich nicht in die #defines ein, die sich schlaue Leute so ausgedacht haben.
-
Optimizer schrieb:
Jester schrieb:
Btw.: Wir überprüft Dein OS denn, das eine Referenz gültig ist:
int * x=0;
int & y = *x;
// und los gehtsMfG Jester
Da geht gar nichts los. Das führt zu einer Acces Violation unter Windows und Linux. Ich habe es jetzt sogar extra ausprobiert.
Bei mir weder unter Windows noch unter Linux
.
int main() { int* x = 0; int& y = *x; }
-
int * x=0; int & y = *x;
dass das alleine schon eine access violation auslöst?
vielmehr ein späteres y = ...
Allerdings ist das *x schon undefiniert.was ist mit deinen klassen?
überprüfst du jedesmal ob nicht vielleicht this == 0?
das wäre nämlich auch denkbar. (wenn auch vom standard undefiniert)warum sagst du dann nicht selbst einfach:
das verhalten der funktion ist undefiniert, falls argument1 == 0.
ebenso wie du vermutest, dass das verhalten einer memberfunktion undefiniert ist,
falls this==0 (bzw. ein nullzeiger sich eben nicht dereferenzieren lässt) - ich nehme nämlich nicht an, dass du in jeder memberfunktion this==0 prüfst.
-
@Mastah: Klar, ich hab auch immer unreferenzierte Variablen in meinem Code.
davie schrieb:
warum sagst du dann nicht selbst einfach:
das verhalten der funktion ist undefiniert, falls argument1 == 0.Gegenfrage: Und warum nicht "wirft eine Exception, falls ... " ?!
Schau mal diese Methode hier an.If this parameter is NULL ...
If this argument is NULL ...
If this argument is NULL ...Das ist übrigens eine Methode, die man _sehr_ häufig aufruft, wenn man diese Klasse nutzt. Diese Methode erstellt Polygone, legt Texturen darüber, verrechnet ein paar Sachen mit der Transformationsmatrix, rechnet Bildschirmkoordinaten in Weltkoordinaten um - und macht ein paar if-Abfragen. Das wird bestimmt die ganze Performance killen.
Es gibt einfach keinen Grund, da rum zu geizen, wenn es nicht grad was simples wie sqrt ist.
EDIT: Link war falsch.
-
da ist es etwas anderes, da hier NULL als Option benutzt wird und nicht als Fehler.
-
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.
Wobei ich es dennoch mal auf einen Versuch ankommen lassen würde, die von Dir genannte Methode mit 0 als erstem Parameter aufzurufen... und bitte mal im Release, bin gespannt, was da passiert.
MfG Jester
-
-
Sie liefert einen FAILED-Wert zurück. Ich hab auch die Release Version vom SDK installiert.
@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.
-
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.