Wird assert () für die Produktion nicht bevorzugt gegenüber if..else .. blocks?

7

Ich finde, dass die Verwendung von assert(...) meinen Code kürzer und einfacher zu lesen macht, im Gegensatz zu langen if..else.. Blöcken. Gibt es jedoch gute technische Gründe, assert(...) im Versandcode nicht zu verwenden, wenn dasselbe den Wert return testet, während weniger Code verwendet wird?

    
Alex Reynolds 30.01.2012, 05:08
quelle

9 Antworten

4

Nachdem ich diesen Artikel gelesen habe, werde ich meine Ansichten über assert :

teilen
  1. Ja, es ist in Ordnung, assert zu verwenden, wenn etwas absolut die Bedingung erfüllen sollte, die Sie geltend machen.

  2. In vielen Sprachen können Sie benutzerdefinierte Fehler bei der Assertion auslösen. C ohne "Ausnahmen" kann zu Fehlern führen, die ein wenig schwieriger zu diagnostizieren sind, ohne direkt auf die betreffende Quelle zu schauen.

Ben DeMott 30.01.2012, 05:14
quelle
4

Wenn es ein Programmierfehler ist (möglicherweise vom Aufrufer), verwenden Sie assert .

Wenn nicht ein Programmierfehler ist, verwenden Sie if / else und behandeln Sie die Situation entsprechend.

    
Mehrdad 30.01.2012 05:18
quelle
2

Während die anderen Antworten einige gute Informationen enthalten, scheint keine (mir) direkt auf die von Ihnen gestellte Frage eingegangen zu sein.

IMO, ja, es gibt einen ziemlich bedeutenden Mangel, assert s im (meisten) Produktionscode zu verlassen: Sie haben keine Kontrolle über die angezeigte Fehlermeldung, die oft ziemlich unheimlich aussieht.

Anstelle von etwas wie:

%Vor%

Ich gebe dem Kunden lieber etwas wie:

%Vor%     
Jerry Coffin 30.01.2012 05:27
quelle
2

assert() ist für Dinge gedacht, die unter normalen Umständen niemals falsch sein sollten.

if - else ist für Dinge gedacht, die unter normalen Umständen manchmal falsch sein können, weil der else -Anteil dafür ausgelegt ist, solche Situationen korrekt zu behandeln.

Kurz gesagt, if -Anweisungen sind dafür vorgesehen, Abstürze zu vermeiden; assert() wurde entwickelt, um sie zu verursachen .

    
Amber 30.01.2012 05:10
quelle
2

Ich denke, dass die Verwendung von assert für Laufzeitfehler eine schlechte Form ist, insbesondere weil sie der Konvention widerspricht:

  • assert wird normalerweise aus Nicht-Debug-Builds kompiliert. Sie möchten möglicherweise alle Ihre Builds mit der Erwartung kompilieren, dass assert immer aktiviert ist, aber jeder, der später Ihren Code erbt, versteht das möglicherweise nicht. Es wird noch schlimmer, wenn Sie Code schreiben, bei dem Ihre assert s Nebenwirkungen haben (wie assert((fp = fopen(filename, "r")) != NULL) ).

  • Ebenso ist assert für logische Fehler gedacht, nicht für Laufzeitfehler. Wenn Sie assert verwenden, um Laufzeitfehler zu überprüfen, beginnen Sie falsch darzustellen, wie sich Ihr Code für jeden verhalten soll, der versucht, ihn zu lesen. Betreuer werden nicht in der Lage sein, leicht zwischen logischen Fehlern und möglichen Laufzeitfehlern zu unterscheiden, und die Unterscheidung ist nützlich, wenn über das Codeverhalten nachgedacht wird.

Und möchten Sie wirklich, dass Ihr Programm abrupt beendet wird und einen Kern ausgibt, wenn ein Benutzer versehentlich ein Eingabefeld leer lässt? Das ist unglaublich benutzerfeindlich.

Wenn Sie glauben, dass die Verwendung von assert weniger Code ist, könnten Sie versuchen, Ihr eigenes, separates Makro zu schreiben. Das würde vermeiden, die erwartete Semantik von assert und die Feindseligkeit zu ändern. Zum Beispiel etwas wie:

%Vor%     
jamesdlin 30.01.2012 06:12
quelle
2

Behauptungen sind gut. Kompilierzeit-Behauptungen sind noch besser. Hinweis:

Wenn Ihre Umgebung noch keine statische Assertion hat, hier ein Vorschlag.

Das unten angegebene Makro ASSERT() kann an einer beliebigen Stelle im Code platziert werden, außer:

  • In einer doppelt enthaltenen Header-Datei ohne #ifndef...#endif wrapper.
  • In der Mitte einer Strukturdefinition (oder Enum-Definition).
  • In strengen C89 oder C90, nach einer Aussage. (Aber Sie können es in Klammern einwickeln!)

Wenn Sie etwas in die Mitte einer Strukturdefinition stecken wollen, müssen Sie das langweilige, hässliche, dreizeilige Konstrukt #if...#error...#endif verwenden. Und wenn Sie dies tun, hat der Preprozessor eine viel begrenztere Vorstellung davon, was ein "konstanter Ausdruck" ist.

Dies ist eine Verfeinerung von Ideen aus dem Web, vor allem von Ссылка . Diese Definition ist kürzer als BOOST_STATIC_ASSERT() . Und ich glaube, ist besser als Linus Vorschlag für eine verbesserte BUILD_BUG_ON() . Und der do{...}while(0) -Wrapper, den Sie normalerweise sehen, ist hier völlig unzutreffend, da er zulässige Speicherorte begrenzt.

Dies ist auch einfacher als Google Compile_ASSERT / CompileAssert. Der 'sizeof bitfield'-Trick scheint auch gut zu sein, von Linux BUILD_BUG_ON_ZERO() , aber nicht sein nutzloses Geschwister BUILD_BUG_ON() .

Es gibt viele Vorschläge für die Verwendung von Arrays mit einem negativen Index. Aber mit GCC erkennen die meisten von ihnen kein nicht-konstantes arg (was leicht genug ist, um irrtümlich zu tun), mit Ausnahme des 'extern int foo [expression]', das auch ein '' gibt. unbenutzte Variable 'Warnung. Aber typedef int array[expression] scheint auch gut zu sein: siehe unten.

Die Definition:

%Vor%

Ebenso gut, glaube ich, ist die folgende Variante, aber sie ist um fünf Zeichen länger:

%Vor%

Es gibt auch das do{switch(0){case 0:case(e):;}}while(0) -Konstrukt, das ich nicht untersucht habe.

Manchmal braucht man eine Variante, um den Fall zu behandeln, dass zufällig zwei verschiedene Header-Dateien vorkommen, um zwei ASSERT () in derselben Zeile oder ebenso für eine Quelldatei und eine Header-Datei zu haben. Sie könnten dies über __COUNTER__ behandeln, aber dies wird von einigen Compilern nicht unterstützt (und ist hässlicher). Und wir können __FILE__ nicht verwenden, weil es normalerweise nicht zu einem gültigen C-Token expandiert (z.B. hat es einen Punkt c oder Punkt h). Die Mozilla-Version Ссылка stellt fest, dass solche Konflikte "selten sein sollten", aber sie werden es tun sehr ärgern Sie Ihre Teamkollegen, wenn es passiert. Dies kann auch verwendet werden, um mehrere ASSERTS in einem mehrzeiligen Makro zu behandeln, wobei sich __LINE__ nicht ändert.

%Vor%

Die nächste Variante, ASSERT_zero(), , ähnelt BUILD_BUG_ON_ZERO(), mit dem 'sizeof bitfield'-Trick. Dies ergibt entweder:

  • ein Kompilierfehler, wenn e falsch oder
  • ist
  • der Wert Null.

So kann es an Stellen verwendet werden, an denen eine Anweisung nicht möglich ist, etwa in der Mitte eines Ausdrucks.

%Vor%     
Joseph Quinsey 30.01.2012 05:49
quelle
2

IMO keine der Antworten hier sagt der wichtigste Teil: Assert Staaten Programmierer Annahmen. assert(x) sagt etwas wie "x ist zu diesem Zeitpunkt immer wahr, du kannst das überprüfen, wenn du" willst. Wenn der Benutzer einen Assertionsfehler sieht, bedeutet dies, dass Sie etwas falsch gemacht haben.

Was Sie wahrscheinlich brauchen, ist eine Funktion, die fast dasselbe macht wie Assert, aber sowohl im Release- als auch im Debug-Modus. Die Verwendung von check(x) würde bedeuten, dass "Überprüfen Sie, ob x wahr ist. Wenn dies nicht der Fall ist, informieren Sie den Benutzer und beenden Sie"

    
cube 30.03.2014 20:53
quelle
1

Ich teile nicht den Punkt von denen, die sagen, dass es gültig ist. Sobald ich durch die Tatsache geschwankt habe, dass Qt's Behauptungen wie folgt definiert wurden:

%Vor%

und ich habe so etwas wie

geschrieben %Vor%

Wenn ich es im Veröffentlichungsmodus befolgt habe, war ich ziemlich verwirrt von a wurde nie initialisiert.

    
vines 30.01.2012 06:13
quelle
0

Wie Java Ausnahmen assert gibt ein fail-fast . Das ist eine sehr gute Methode, die viel Entwicklungszeit und -aufwand spart. Aber der Versandcode sollte schnelle Releases als Web-Anwendung haben, und nicht so etwas wie eine CD-Distribution. Außerdem können Ausnahmen in Java abgefangen, protokolliert und gut dargestellt werden.

    
Joop Eggen 30.01.2012 05:16
quelle