assert einsetzen?



  • Hallo,

    ich habe bis jetzt noch nie asserts eingesetzt. Ich habe mich versucht zu informieren (SuFu und google) aber:

    1. Warum brauchst man asserst. Ich könnte doch genaugenommen selbst ein makro schreiben

    2. wie kann man asserst abschalten wenn man dann seinen release baut?

    3. benutzt ihr asserst überall? ich habe gelesen je mehr desto besser aber der code wird dann auch unübersichtlicher oder?

    Danke für kurze Hilfe



  • 1. Ja, warum schreibst du nicht deinen eigenen Compiler? Warum benutzt du ein fertiges Betriebssystem? Fragen über Fragen! Man benutzt etwas existierendes, um nicht das Rad neu zu erfinden und somit Fehler zu bauen. Alles was man neu schreibt, bedeutet Zeit, Geld und potenzielle Fehler.

    Weiterhin sagt dir Assert, an welcher Stelle der Fehler auftrat.

    2. Wenn man ein Release baut, wird das Assert autom. abgeschaltet. Das ist dessen Sinn.

    3. Nein, benutze ich praktisch nie. Und wenn, dann gaaaanz selten.



    1. Komische Frage, wenn du meinst, das selbst schreiben zu müssen, weißt du doch schon, wozu man es braucht. Davon ab spricht überhaupt nichts dagegen sich sein eigenes Assert zu programmieren, insbesondere wenn einem die Programmbeendigung durch abort() nicht so liegt.

    2. Das Makro NDEBUG definieren. Die Definition von assert ist in #ifndef NDEBUG gekapselt.

    3. In meinem Privatzeug ständig, ich finde auch nicht, dass es den Code unübersichtlich macht, im Gegenteil. Es macht implizite Annahmen explizit.



  • Zu 3.) Manche Bücher gehen dabei so weit, daß sie assert's als code-seitige Dokumentation der Preconditions ( Vorbedingungen ) ansehen.
    Aber keine Frage, man kann es immer übertreiben.

    Gruß Kimmi



  • hmm....also so richtig überzeugt bin ich nicht. ich lese ständig dass asserts wichtig sind. ich wollte halt wissen wie sinnvoll sowas ist...im grunde dient es ja nur debug-zwecken...



  • Na, mit assert stellst du halt sicher, das etwas bestimmtes erfüllt wird.

    Wenn das nicht für einen sauberen "Betrieb" sinnvoll ist, dann weiß ich auch nicht.

    Aber keiner zwingt dich, es zu benutzen.



  • Fragender2 schrieb:

    hmm....also so richtig überzeugt bin ich nicht. ich lese ständig dass asserts wichtig sind. ich wollte halt wissen wie sinnvoll sowas ist...im grunde dient es ja nur debug-zwecken...

    Was heisst hier "nur"? Dir ist wohl nicht klar, wie wichtig das Debuggen als Teil des Programmierens ist? 😉

    Assertions sind sehr praktisch, da sie Logikfehler (zum Beispiel die erwähnten nicht erfüllten Preconditions) unmittelbar aufdecken und nicht das Programm mit Fehlern oder sogar undefiniertem Verhalten weiterlaufen lassen. Und wenn du schon einmal Fehler hattest, die sich quer über das ganze Programm erstrecken und in völlig anderem Zusammenhang nicht nachvollziehbare Probleme erzeugen, wirst du Sicherheitsmechanismen wie Assert dankbar sein. 🙂
    (Natürlich hilft ein ordentliches Design auch enorm, was Wartbarkeit und Debugmöglichkeiten angeht.)

    Dass man Annahmen im Code sieht, ist zwar für den Bibliotheksentwickler praktisch, aber hilft dem Anwender nur begrenzt und ersetzt schon gar keine Dokumentation.



  • danke euch. Dennoch gehts nicht ganz in meinen kopf rein wozu ein assert denn zwingend notwendig sein könnte...bzw. wo es sehr sinnvoll wäre.

    Ich finde immer nur beispiele für logik-fehler. Aber logik-fehler muss ich doch irgendwo sowieso manuell über ein if abfangen.

    wenn ich die üblihce beispielfunktion

    double dividiere (double a, double b)
    {
       assert(b!=0);
       return a/b;
    }
    

    habe - wäre es nicht blödsinn hier ein assert zu setzen sondern da wo der user-input ist auf alle fälle ein if-test zu setzten der prüfte ob b ungleich 0 ist?

    Für dieses beispiel leuchtets mir nicht ein.

    Gibts andere sinnvollere ?

    Danke



  • Mit einem assert definierst du eine Vorbedingung. In dem Fall, dass b eben nicht 0 ist. Es liegt nun - genau wie in einer Version ohne assert - an dem Aufrufer sicherzustellen, dass diese Vorbedingung erfüllt ist. Ein assert dient eben nur dazu, dass man Vorbedingungen explizit klar macht (=> im Debugmode bricht das Programm ab). Das ist also was anderes, als das validieren von irgend einer Eingabe. Solltest du a und b zB vom Nutzer einlesen, dann musst du vor dem Aufruf der Funktion sicherstellen, dass die Vorbedingung erfüllt ist!

    Ich benutze ständig assert und denke, dass dies der richtige Weg ist. Sag explizit was du erwartest. Macht den Umgang mit Code wesentlich leichter.



  • Fragender2 schrieb:

    Aber logik-fehler muss ich doch irgendwo sowieso manuell über ein if abfangen.

    Wieso meinst du, bräuchte man dazu eine If-Abfrage?

    Fragender2 schrieb:

    Für dieses beispiel leuchtets mir nicht ein.

    Logikfehler kann natürlich breit aufgefasst werden. Stell dir vor, du hast eine Containerklasse, die mehrere Elemente verwaltet und einen operator[] für indizierten Zugriff anbietet (z.B. std::vector ). In sehr vielen Fällen kann der Benutzer für gültige Werte garantieren, zum Beispiel, wenn er über alle Elemente iteriert.

    Es wäre eine Verschwendung von Performance, eine If-Abfrage für die paar wenigen Fälle zu implementieren. Man müsste sich auch überlegen, wie man den Fehler behandeln sollte. Einfach nichts tun geht nicht, man muss schliesslich ein Objekt zurückgeben. Soll man ein Dummy-Objekt zurückgeben? Dann muss der Benutzer immer zuerst selbst schauen, ob er das Objekt brauchen kann. Und diese Aufgabe sollte eigentlich von der Klasse abgenommen werden. Oder eine Exception? Kann in vereinzelten Fällen sinnvoll sein, allerdings will man nicht für ein paar Ausnahmefälle ständig unnötige Abfragen drin haben; ausserdem gibt es Fälle, wo eine Behandlung nicht sinnvoll ist und das Programm besser gleich abgebrochen wird (für die Fälle, wo eine Exception erwünscht ist, gibt es bei std::vector eine Funktion at() ).

    Man löst das Dilemma durch Assertions: Man sagt dem Benutzer: "Setze operator[] dann ein, wenn du dir über korrekte Indizes im Klaren bist". Somit hat er keinen Laufzeitoverhead im Release-Modus. Allerdings kann es trotzdem immer zu Fehlern kommen, sei das zum Beispiel, wenn der Index als Resultat einer Berechnung zustande kommt. In so einem Fall ist es sehr nützlich, wenn das Programm gleich mitteilt: "Du hast die Assertion verletzt! An dieser Codestelle hast du einen falschen Aufruf, repariere das". Viel nützlicher, als wenn monatelang nichts passiert und beim Kunde plötzlich merkwürdige Abstürze auftreten. Viel nützlicher, als wenn man als Entwickler die Möglichkeit in Erwägung ziehen muss, einen Grossteil Code nochmals neu zu schreiben, weil die Wahrscheinlichkeit auf Finden des Fehlers derart gering ist.



  • asserts sind direkt nach dem Debugger das mächtigste Mittel, Fehler ausfindig zu machen. Immer dann, wenn du von einer Variablen (z.B. Funktionsparameter) oder einem Rückgabewert erwartest, dass er einen bestimmten Wert hat und der folgende Code nur unter dieser Annahme korrekt funktioniert, baut man ein assert ein... das geschieht bei mir schon lange weitgehend unbewusst.
    Damit findet man auch Fehler bei komplexen Aufrufstrukturen, bei dem der Debugger kaum weiterhelfen kann und bei Fehlern, die nie zum Programmabsturz, sondern nur zu falschem Verhalten führen und dadurch z.B. schleichend Daten vernichten. In einem Serverprogramm hat mich ein assert schon vor einer kleinen Katastrophe bewahrt und mich überhaupt erst darüber informiert, dass ein Problem vorlag. Und natürlich dabei geholfen, kleinere Bugs zu eliminieren. Ich lasse asserts inzwischen auch oft in Releasebuilds drin... es macht die Sache eben einfacher, wenn der Benutzer mit einer konkreten Fehlermeldung aufwarten kann bzw. die Logs einsenden kann, anstatt nur sagen zu können: das Programm ist abgestürzt.

    Du wirst assert zu schätzen lernen, nachdem sie dir die ersten paar Male weitere graue Haare erspart haben.



  • Ich kann es nicht lassen hier noch einen Querverweis auf die Programmiersprache Eiffel zu machen. 🙂
    Anhand dieser Sprache sieht man sehr gut, dass Assertions (dort unter anderem auf einer höheren Ebene Contracts genannt) einen sehr hohen Stellenwert haben und das ist (meiner Meinung nach) schon auch ein wenig Zukunftsweisend, wenn auch ein wenig nervig in Eiffel, aber dennoch auch sehr hilfreich.

    Einen kleinen Vergleich, wie das in C++ aussehn könnte habe ich auf meinem Blog beschrieben:
    http://www.drakon.ch/?id=&offset=&mobile=0&show_entry=79

    Die genannten Vorteile, wie bequemeres Debugging und Fehler zu finden, die man sich gar nicht bewusst ist, sind wichtige Argumente, aber was mit diesen Contracts auch noch möglich ist, habe ich Heute anhand von automatisiertem Testing gesehen. Das heisst, dass unser Assistent eine Klasse geschrieben (ACCOUNT) und die ohne einen Testcase testen konnte dadurch, dass das System die Klasse und Funktionen angeschaut hat und geschaut, welche Werte übergeben werden dürfen (also überhaupt korrekte Werte geben können) und dann wurden die Postconditions automatisch überprüft und allfällige Fehlschläge gemeldet. Diese Demonstration fand ich doch recht beeindruckend, weil die Korrektheit des Programmes nicht nur dazu benutzt werden kann, um Fehler zu finden, sondern wirklich auch um das ganze sinnvoll zu testen. Mir ist keine andere Sprache bekannt, wo ein solches automatisiertes Testing möglich ist.

    Das finde ich noch interessant an dem Ansatz, wie unsere Vorlesung aufgebaut ist und mit Eiffel durchgeführt wird. Bei uns wird kaum jemand mal noch fragen, für was Pre/Postconditions oder Invariants nötig sind, weil einem das von der ersten Stunde an beinahe eingehämmert wird, wie wichtig die sind und man bekommt beinahe das Gefühl, dass man keine korrekte Software ohne schreiben kann. 🙂



  • drakon schrieb:

    Ich kann es nicht lassen hier noch einen Querverweis auf die Programmiersprache Eiffel zu machen. 🙂

    Finde ich gut.

    Anhand dieser Sprache sieht man sehr gut, dass Assertions (dort unter anderem auf einer höheren Ebene Contracts genannt) einen sehr hohen Stellenwert haben und das ist (meiner Meinung nach) schon auch ein wenig Zukunftsweisend, wenn auch ein wenig nervig in Eiffel, aber dennoch auch sehr hilfreich.

    Zukunftsweisend ist es vermutlich nicht. Dazu komme ich gleich.

    Einen kleinen Vergleich, wie das in C++ aussehn könnte habe ich auf meinem Blog beschrieben:
    http://www.drakon.ch/?id=&offset=&mobile=0&show_entry=79

    Du betreibst da evtl overengeneering. Es ist richtig, daß das Konzept/concept/Begriff um require und ensure (MS und SUN würden es technology nennen) recht schlau ist. Und C++ als die Hure aller Sprachen könnte und sollte davon profitieren. Aber Komplexität ohne Not bringt Dir den Tod. Mir scheint, ein schlichtes

    #define require assert
    #define ensure assert
    

    würde schon alles erledigen.

    recht beeindruckend, weil die Korrektheit des Programmes nicht nur dazu benutzt werden kann, um Fehler zu finden, sondern wirklich auch um das ganze sinnvoll zu testen. Mir ist keine andere Sprache bekannt, wo ein solches automatisiertes Testing möglich ist.

    Ja, das ist supi.

    und man bekommt beinahe das Gefühl, dass man keine korrekte Software ohne schreiben kann. 🙂

    Leider könen nur erstaunlich wenige Programmierer von "mit" profitierten.



  • Zukunftsweisend meinte ich im Sinne von, dass man sich mehr Gedanken macht, was die Anforderungen sind für ein gewisses Modul und worauf man sich verlassen kann. Es wird wahrscheinlich noch massig Zeit vergehen, bis es wirklich Standard ist, aber die Tendenz ist ja imo da.

    Ja, klar betreibe ich da Overengeneering. Sonst würde es ja kein Spass machen. 🙂

    Das Problem an dem define (und natürlich auch an meinem Beispiel) ist ja, dass man da hinsichtlich der automatischen Testmöglichkeit nichts gewonnen hat, weil es sich wahrscheinlich kaum gscheit analysieren lassen würde, da nicht standardisiert.

    Leider könen nur erstaunlich wenige Programmierer von "mit" profitierten.

    Jup. Das hat sich wahrscheinlich auch Bertrand Meyer gedacht, als er die Sprache entwickelt hat. Programmierer muss man zu bravem Code zwingen. :p



  • Für C gibt es durchaus Tools die automatisch zur compile-time prüfen können, ob das Programm ein assert verletzten könnte oder ob dies ausgeschlossen ist. So z.B. BLAST:

    http://mtc.epfl.ch/software-tools/blast/index-epfl.php

    Disclamer: Das klappt (terminiert) natürlich nicht für tatsächlich _alle_ möglichen Programme (Halteproblem)...



  • drakon schrieb:

    Zukunftsweisend meinte ich im Sinne von, dass man sich mehr Gedanken macht, was die Anforderungen sind für ein gewisses Modul und worauf man sich verlassen kann. Es wird wahrscheinlich noch massig Zeit vergehen, bis es wirklich Standard ist, aber die Tendenz ist ja imo da.

    Ja, klar betreibe ich da Overengeneering. Sonst würde es ja kein Spass machen. 🙂

    Das Problem an dem define (und natürlich auch an meinem Beispiel) ist ja, dass man da hinsichtlich der automatischen Testmöglichkeit nichts gewonnen hat, weil es sich wahrscheinlich kaum gscheit analysieren lassen würde, da nicht standardisiert.

    Leider könen nur erstaunlich wenige Programmierer von "mit" profitierten.

    Jup. Das hat sich wahrscheinlich auch Bertrand Meyer gedacht, als er die Sprache entwickelt hat. Programmierer muss man zu bravem Code zwingen. :p

    Volle Zustimmung, bis auf

    Programmierer muss man zu bravem Code zwingen.

    Level    | ohne Zwang                 | mit Zwang          | Lernkurve
    --------------------------------------------------------------------------------
    kackboon | kriegt erste Programme hin | kriegt nix hin     | lernt sehr langsam
    nube     | baut dauernd kacke         | baut weniger kacke | lernt, sich zu beugen, aber begreift es noch nicht
    profi    | baut dauernd kacke         | baut kaum kacke    | lernt gut
    provi    | baut kaum kacke            | baut kaum kacke    | irrelevant
    guru     | praktisch fehlerfrei       | praktisch fehlerfrei, aber täglich eine erzwungene suboptimale Lösung | irrelevant
    

    Also ich kann eine Sprache ohne goto nicht ernst nehmen (außer sie ist lispoid, natürlich).



  • volkard schrieb:

    Also ich kann eine Sprache ohne goto nicht ernst nehmen (außer sie ist lispoid, natürlich).

    Hehe. Dann würdest du Eiffel recht verabscheuen. Da ist bereits ein break böse. (respektive nicht vorhanden) 😉

    Die Tabelle kann ich kaum beurteilen. Bis auf das, dass jetzt wahrscheinlich die meisten bei uns (teils auch völlige Anfänger) die Konzepte von OOP und contracts verstehen, aber dass sie eigenständig ein Programm entwickeln können, wage ich zu bezweifeln. (Was aber wahrscheinlich auch bis zu einem gewissen Grade an der Eigenverantwortung einige liegt).



  • drakon schrieb:

    volkard schrieb:

    Also ich kann eine Sprache ohne goto nicht ernst nehmen (außer sie ist lispoid, natürlich).

    Hehe. Dann würdest du Eiffel recht verabscheuen. Da ist bereits ein break böse. (respektive nicht vorhanden) 😉

    Ich mußte auch zwei Semester mit Eiffel spielen. Ich kannte schon C++ aber war noch nube, von echter Objektorientierung keine Ahnung. Nein, Eiffel hat nicht weh getan; ganz im Gegenteil: es hat Begehrlichkeiten geweckt.

    drakon schrieb:

    Die Tabelle kann ich kaum beurteilen. Bis auf das, dass jetzt wahrscheinlich die meisten bei uns (teils auch völlige Anfänger) die Konzepte von OOP und contracts verstehen, aber dass sie eigenständig ein Programm entwickeln können, wage ich zu bezweifeln. (Was aber wahrscheinlich auch bis zu einem gewissen Grade an der Eigenverantwortung einige liegt).

    Ist auch mehr nur eine Handskizze. Und sich über sie zu unterhalten wäre müßig. Vielleicht hat sie mir eröffnet, folgendes zu sagen:

    Also diese Zwänge haben Vor- und Nachteile. Vorteile: Code wird allgemein besser. Nachteile: Manchmal, ganz selten, wird Code dadurch schlechter.
    Ich hoffe, das kannst Du so stehen lassen.
    Mein lustiger Gedankengang geht so: In dem Semester sind recht viele unterschiedliche Leute und schätzungsweise 10% davon werden gute Softwareentwickler werden. Diese 10% sind empfänglich für die fehlervermeidenden Konzepte und würden sie auch in C oder PHP anwenden. Der Prof muß die Konzepte nur erzählen, weil ganz von alleine kommt man normalerweise nicht drauf. Sie würden die Konzepte optimalerweise aus purer Faulheit anwenden, denn das Debuggen dauert im ersten Semester zehnmal so lang wie das Schreiben selber und wenn man durch Einbau von voll tollen Fehlervermeidungsstrategien, die meinetwegen die Programmierzeit verdoppeln, die Debugzeit halbieren kann, hat man viel Zeit gewonnen, sich zu besaufen.
    Für die programmiertechnisch guten 10% der Studenten ist gar kein Sprachzwang nötig. Natürlich ist ein Prof nötig, der einen führt.
    Für die programmiertechnisch schlechten 90% ist es nicht wichtig, da sie ihren Lebensweg in einer Weise gestalten werden, der es nicht notwendig macht, daß sie viel programmieren. Deswegen ist für sie auch kein Sprachzwang nötig. Sie sollen ja nur ein Grundverständnis haben, mal ein paar kleine Sachen gemacht haben, damit sie sich bei Aufwands- und Machbarkeitsabschätzungen nicht im luftleeren Raum bewegen müssen.
    Damit brauchen beide Gruppen eher keinen Sprachenzwang. Gelegentlich stößt ein Profi gegen einen Zwang und ärgert sich ein wenig, weil er genau weiß, daß in einer anderen Sprache das ganz direkt ginge. Gelegentlich ärgert er sich auch ein wenig mehr und bricht vielleicht ein Projekt ab (mir passiert bei einem Berichtsgenerator-Framework in VB.net) oder braucht fünf Monate für was, was in einer anderen Sprache geschätzt in einem Monat geht (mir passiert, ich habe nur einen Monat in Rechnung gestellt und meide seitdem Java)(später sollte ich den Kern dessen zu einem anderen Zweck machen und war frei und es dauerte nur drei Tage).
    Zusammengenommen führt das dann dazu, daß Eiffel eine "Lehrsprache" ist und jeder nach dem Lernen von Eiffel dann entweder auf eine richtige Sprache umsteigt oder mit dem Programmieren ganz aufhört. Vielleicht wäre es geschickter, in der Sprache viel mehr zu erlauben, und zusätzlich die geilen Sachen anzubieten. Wertemengenüberprüfungen zur Compilezeit an den aufrufenden Code zu propagieren, sowohl Eingabewerte (require) als auch Ausgabewerte (ensure), und dem Compiler damit die Ablehnung zu erlauben, ist schrecklich lecker. Oder ihr Eiffler habt nicht unsere (gemeint: alle Sprachen außer Eiffel und natürlich einige lispoide) Optimierungsmauer bei virtuellen Funktionen, bei Euch kann der Compiler auch die inlinen und gegebenenfalls zum Beispiel wegoptimieren. *sabber*
    edit: Die anderen Sprachen, insbesodere die mit Jittern, erlauben normalerweise auch das wegoptimieren, tuns aber nicht. Eiffel-Compiler hatten schon sehr früh statt Tabellennachguckerchen lieber Entscheidungsbäume genommen. Vielleicht wegen der contracts, darüber müßte ich mal nachdenken.



  • Ja. Damit kann ich mich gut identifizieren.

    Ich empfinde Eiffel in dem Sinne auch als eine gute Lernsprache. Es nervt Teilweise gewaltig, wenn man sich andere Sprachen gewohnt ist, aber wirklich die eingebauten Möglichkeiten sichereren Code zu schreiben finde ich toll. Z.b finde ich auch die geringe Gefahr von Syntaxfehlern. )"semicolons are so 20th century", wie Meyer pfelgt zu sagen. :))
    Allerdings ist sie teilweise weniger Ausdrucksstark, was die Möglichkeiten angeht, aber naja.. Hat auch wieder Vor/Nachteile.

    Aber was mit an Eiffel am meisten stört ist Eiffel Studio. Die IDE ist einfach nur mühselig. Unintuitiv, langsam und völlig Überladen. Immerhin ist der Debugger recht nett, aber ansonsten ist es eher eine Qual damit zu entwickeln.


Anmelden zum Antworten