wieso gibt es in c++ kein finally?



  • groovemaster schrieb:

    exception-freak schrieb:

    naja, aber wenn man mit c++ exceptions noch nicht mal 'nen nullpointer-access oder eine miese rechenoperation wie 'division durch 0' abfangen kann, macht das auch nicht viel sinn.

    Genau das wäre imo brauchbarer für den neuen Standard als finally. Sowas lässt sich mit C++ bisher leider nur plattformspezifisch umsetzen.

    Hmm,
    Null-Pointer-Exceptions lassen sich simpel mit einer "Checked Access" Funtion durchführen, oder alternativ mit einer entsprechenden Template-Klasse die das etwas schöner macht:

    #include <iostream>
    #include <exception>
    
    template<typename T>
    T* checked_access(T* ptr)
    {
        if(!ptr)
        {
            throw std::runtime_error("Zero pointer exception");
        }
        return ptr;
    }
    
    int main()
    {
    
        int* a = NULL;
    
        try
        {
            *checked_access(a) = 3;
        }
        catch(...)
        {
            //...
        }
    
        return 0;
    };
    

    Bei einer Division ist es immer sinnvoller, den Divisor auf 0 zu prüfen, als solche Fehler über ein Laufzeitfehlersystem einmal quer durchs Betriebssystem zu schießen.
    Beides ist Plattformunabhängig...



  • Das Problem ist sowohl bei Null-Pointer wie Div durch 0, daß man das jeweils von Hand vor dem Zugriff überprüfen muß. Es wäre schöner, wenn die Laufzeitumgebung automatisch eine entsprechende Exception werfen würde.



  • ~john schrieb:

    Es wäre schöner, wenn die Laufzeitumgebung automatisch eine entsprechende Exception werfen würde.

    Bitte behaltet immer in Hinterkopf das C++ auch das Ziel hat effizient und schnell zu sein. Bei C++ wird nicht ohne grund einiges von Laufzeitentscheidungen in die Hand des Entwicklers gelegt. Wenn ich z.B. ausschließen kann das ich über die Indexe eines vectors zugreife, ist eine Prüfung eine unnötige Laufzeitbelastung. Ebenso wenn ich von vorne herein weiß das keine 0 in meinen Divisionen auftreten kann oder, oder, oder...

    Man muss abwägen zwischen Sicherheit und Performance, C++ hat halt auch letzteres im Auge. Wobei übertriebene Sicherheit wiederum dazu führt das Entwickler Fehler teilweise garnicht betrachten.

    cu André



  • asc schrieb:

    Bitte behaltet immer in Hinterkopf das C++ auch das Ziel hat effizient und schnell zu sein. Bei C++ wird nicht ohne grund einiges von Laufzeitentscheidungen in die Hand des Entwicklers gelegt. Wenn ich z.B. ausschließen kann das ich über die Indexe eines vectors zugreife, ist eine Prüfung eine unnötige Laufzeitbelastung.

    nö, es muss ja nix ständig überprüft werden. eine aktion ist nur gefragt, wenn etwas passiert ist. bisher wird x/0 vom OS ausgewertet, das eine hässliche messagebox aufmacht (falls die exception nicht behandelt wird). sie könnte aber genauso gut an die c++ runtime library weitergereicht werden und als c++ exception im programm wieder auftauchen. ich glaub' msvc kann sowas über diese TRY/EXCEPT macros.
    🙂



  • In C++ eine NullPointerException bereitzustellen ist u.U. nicht trivial. Prinzipiell ist für das OS erstmal alles eine access violation. Zwischen dem speziellen Fall des 0-Pointers und einer "normalen" access violation zu unterscheiden, ist aber nicht ganz einfach. Zum Beispiel, wenn ich das zweite Elemente in einem struct mit einem 0-Pointer referenziere, könnte das die Adresse sein: 0x00000004

    Klar, so ist es noch ganz einfach zu verstehen, dass die Ursache ein 0-Pointer ist, aber wo zieht man die Grenze? Wenn man nur die Adresse hat, ab welchem Wert ist es ein 0-Pointer und wann ist es was anderes? In Java ist die Frage leicht zu beantworten, weil es keine anderen Arten von access violations gibt (Arrayzugriff wird vorher vom Programm selber geprüft).

    Um in C++ 0-Pointer Fehler eindeutig identifizieren zu können, müsste wahrscheinlich vor jeder Dereferenzierung ein expliziter Test auf 0 stattfinden. Das kostet Laufzeit und was ist jetzt der Gewinn dabei? Ich sehe ihn nicht.



  • ausnahme-freak schrieb:

    bisher wird x/0 vom OS ausgewertet, das eine hässliche messagebox aufmacht (falls die exception nicht behandelt wird).

    Also ich hab bei meinem Taschenrechner noch nie eine Messagebox gesehen.



  • Optimizer schrieb:

    Zwischen dem speziellen Fall des 0-Pointers und einer "normalen" access violation zu unterscheiden, ist aber nicht ganz einfach.

    z.b. windoofs spuckt bei exceptions ziemlich gute infos aus: http://msdn2.microsoft.com/en-us/library/aa363082(VS.85).aspx

    LordJaxom schrieb:

    Also ich hab bei meinem Taschenrechner noch nie eine Messagebox gesehen.

    dann isser defekt. haste noch garantie?
    🙂



  • Also ich bin eigentlich ganz froh, dass Speicher-Zugriffs-Fehler eher wie ein assert und nicht wie eine Ausnahme wirken. Genauso bei der Division durch 0, sowas will ich definitiv so früh raushaben wie möglich und bisher ließ sich bei mir mit Tests beides immer schön rausfiltern.



  • ausnahme-freak schrieb:

    asc schrieb:

    Bitte behaltet immer in Hinterkopf das C++ auch das Ziel hat effizient und schnell zu sein. Bei C++ wird nicht ohne grund einiges von Laufzeitentscheidungen in die Hand des Entwicklers gelegt. Wenn ich z.B. ausschließen kann das ich über die Indexe eines vectors zugreife, ist eine Prüfung eine unnötige Laufzeitbelastung.

    nö, es muss ja nix ständig überprüft werden. eine aktion ist nur gefragt, wenn etwas passiert ist. bisher wird x/0 vom OS ausgewertet, das eine hässliche messagebox aufmacht (falls die exception nicht behandelt wird). sie könnte aber genauso gut an die c++ runtime library weitergereicht werden und als c++ exception im programm wieder auftauchen. ich glaub' msvc kann sowas über diese TRY/EXCEPT macros.
    🙂

    Wie soll das Funktionieren? Natürlich muss bei jedem Zugriff auf einen Zeiger geprüft werden, ob ein Nullpointer vorliegt. Selbiges gilt für das Überschreiten der Boundaries bei Arrays. Jeder Zugriff muss gecheckt werden.
    Wie soll er denn sonst wissen, dass was passiert? Kristallkugel?



  • Tachyon schrieb:

    Wie soll das Funktionieren? Natürlich muss bei jedem Zugriff auf einen Zeiger geprüft werden, ob ein Nullpointer vorliegt. Selbiges gilt für das Überschreiten der Boundaries bei Arrays. Jeder Zugriff muss gecheckt werden.
    Wie soll er denn sonst wissen, dass was passiert? Kristallkugel?

    nö, das läuft hardwaregestützt ab. CPU, MMU etc. erkennen bus und alignment errors, division durch 0, access violations usw. und lösen einen interrupt aus. das betriebssystem sagt dem programm dann 'schau mal, was du da angestellt hast' und gibt ihm gelegenheit, darauf zu reagieren, indem dessen exception-handler (falls vorhanden) aufgerufen wird. im fall eines c++ programms könnte der exception handler in der c++ runtime lib sitzen, die dann daraus eine c++ exception machen könnte und das c++ programm könnte sie, wie gewohnt, mit einem 'catch' verarbeiten. es geschieht alles automatisch, ohne dass man aktiv irgendwas überprüfen muss.
    🙂



  • os-freak schrieb:

    Tachyon schrieb:

    Wie soll das Funktionieren? Natürlich muss bei jedem Zugriff auf einen Zeiger geprüft werden, ob ein Nullpointer vorliegt. Selbiges gilt für das Überschreiten der Boundaries bei Arrays. Jeder Zugriff muss gecheckt werden.
    Wie soll er denn sonst wissen, dass was passiert? Kristallkugel?

    nö, das läuft hardwaregestützt ab. CPU, MMU etc. erkennen bus und alignment errors, division durch 0, access violations usw. und lösen einen interrupt aus. das betriebssystem sagt dem programm dann 'schau mal, was du da angestellt hast' und gibt ihm gelegenheit, darauf zu reagieren, indem dessen exception-handler (falls vorhanden) aufgerufen wird. im fall eines c++ programms könnte der exception handler in der c++ runtime lib sitzen, die dann daraus eine c++ exception machen könnte und das c++ programm könnte sie, wie gewohnt, mit einem 'catch' verarbeiten. es geschieht alles automatisch, ohne dass man aktiv irgendwas überprüfen muss.
    🙂

    Jo, das stimmt. Das könnte mal tatsächlich so machen. Allerdings bedeuten Interrupts im Protected-Mode einen ordentlichen Schlag ins Gesicht des Betriebssystems...



  • hier mal ein beispiel, compiled mit msvc2005 unter win:

    #include <stdio.h>
    #include <windows.h>
    
    int divide (int a, int b)
    {
      int res = 0;
      __try           
      {
        res = a/b;
      }
      __except (1)   // OS exception handler
      {
        throw "divison by zero";
      }
      return res;
    }
    
    int main()
    {
      try
      {
        int x = divide (5, 0);
      }
      catch (char *what)  // C++ exception handler
      {
        puts (what);
      }
    }
    

    🙂



  • Die Hardware ist keine magische Black Box. Sobald dein Code auf eine nicht erlaubte Adresse zugreift -> Interrupt -> OS handler -> Exception in deinem Programm: access violation @ 0x00000048 Du kriegst schon eine Exception, wenn du sie willst, aber woher soll klar sein, ob es wegen einem Nullpointer ist?

    Und, war das jetzt, weil du nen nullpointer benutzt hast? Ja, wahrscheinlich in diesem Fall. Aber wissen kannst du es schon nicht, du weißt ja nicht, wie dein Programm auf diese Adresse gekommen ist.

    Dazu müsste das OS jetzt den Programmzustand ein paar Takte vorher wissen und nachsehen, ob irgendwo 0 + offset gerechnet wird, um die böse Adresse zu bestimmen. Fast unmöglich wird es, wenn du den Zeiger aus irgendner Datenstruktur herholst, in die du ihn 2 Tage vorher reingelegt hast. Du kannst mit Adressen alles machen und ausrechnen. Woher will das OS wissen, dass du die Absicht hattest, dass ein bestimmter Wert nicht hätte 0 sein dürfen?

    Ein paar Buzzwords wie MMU und alignment errors helfen da nicht. 😉 Das OS hat keine Ahnung, was ein Programm tun will. Es kann nur dem Programm virtuellen Speicher zur Verfügung stellen und überwachen, dass es die Regeln für jede Speicherseite einhält.



  • Tachyon schrieb:

    Allerdings bedeuten Interrupts im Protected-Mode einen ordentlichen Schlag ins Gesicht des Betriebssystems...

    schon mal die maus bewegt? solche schläge steckt die kiste locker weg.

    Optimizer schrieb:

    Die Hardware ist keine magische Black Box. Sobald dein Code auf eine nicht erlaubte Adresse zugreift -> Interrupt -> OS handler -> Exception in deinem Programm: access violation @ 0x00000048 Du kriegst schon eine Exception, wenn du sie willst, aber woher soll klar sein, ob es wegen einem Nullpointer ist?

    man kriegt z.b. mitgeteilt, bei welcher adresse der zugriff scheiterte. und wenn die 0 ist, dann kann man sich schon einen reim drauf machen. was nicht geht, sind z.b. exceptions beim überschreiten von arraygrenzen. es kann ja sein, dass sich dahinter speicher befindet, auf den man auch zugriff hat. aber ich finde, man muss nicht so sehr differenzieren. immerhin wär's besser als nix^^

    Optimizer schrieb:

    Ein paar Buzzwords wie MMU und alignment errors helfen da nicht

    wo ist das buzzig? in 'nem coder-board wissen doch die meisten was das heisst.
    🙂



  • ~john schrieb:

    hustbaer schrieb:

    Finally wäre trotzdem nett.

    Finally ist in C++ total überflüssig.

    Ich kann mich nur wiederholen: viele Dinge sind in C++ total überflüssig, aber trotzdem da.



  • operatingsystem-freak schrieb:

    Tachyon schrieb:

    Allerdings bedeuten Interrupts im Protected-Mode einen ordentlichen Schlag ins Gesicht des Betriebssystems...

    schon mal die maus bewegt? solche schläge steckt die kiste locker weg.

    Optimizer schrieb:

    Die Hardware ist keine magische Black Box. Sobald dein Code auf eine nicht erlaubte Adresse zugreift -> Interrupt -> OS handler -> Exception in deinem Programm: access violation @ 0x00000048 Du kriegst schon eine Exception, wenn du sie willst, aber woher soll klar sein, ob es wegen einem Nullpointer ist?

    man kriegt z.b. mitgeteilt, bei welcher adresse der zugriff scheiterte. und wenn die 0 ist, dann kann man sich schon einen reim drauf machen.

    Die ist aber fast nie 0, weil du den Zeiger i.d.R. dereferenzierst um anschließend auf ein Member zuzugreifen (möglicherweise mehrmals). Deshalb wird zuerst die Adresse, die insgesamt rauskommt, ausgerechnet und dann in den Speicher geschaut, was dort steht. Wenn es in der Nähe von 0 ist, kannst du natürlich vermuten, was die Ursache ist, aber richtig Gewissheit wirst du so nicht kriegen.

    Wo siehst du jetzt btw. den Vorteil davon, wenn du es sicher unterscheiden könntest? Gerade null-Pointer sind IMHO die Art von Speicherzugriffsfehler, wo ich nicht lange suchen muss.



  • Tachyon schrieb:

    Null-Pointer-Exceptions lassen sich simpel mit einer "Checked Access" Funtion durchführen, oder alternativ mit einer entsprechenden Template-Klasse die das etwas schöner macht:

    Das ist eigentlich keine Option. Du kannst nicht einfach ein Projekt im Nachhinein mit solchen Funktion ausstatten. Ausserdem sieht das extrem hässlich aus und verschlechtert die Lesbarkeit. 🙂

    Tachyon schrieb:

    Bei einer Division ist es immer sinnvoller, den Divisor auf 0 zu prüfen, als solche Fehler über ein Laufzeitfehlersystem einmal quer durchs Betriebssystem zu schießen.

    Von Fall zu Fall kann man natürlich auf 0 prüfen, sofern dies zum Algorithmus passt. Du hast die eigentliche Thematik aber nicht verstanden. Sofern die Hardware entsprechende Exceptions unterstützt, kostet das nichts an Performance. Erst wenn eine Exception geworfen wird, kostet das Zeit. Und dann ist es sowieso egal. Werden solche Exceptions von der Hardware nicht unterstützt, wird das gleiche wie bei manueller Prüfung gemacht. Und das kostet dann immer Performance. Diese Abstraktion sollte möglichst auf Sprachebene gelöst sein, nicht auf den Anwender abgeschoben werden. Die Möglichkeit, generell solche Prüfungen zu unterbinden oder Hardware Exceptions vom System handeln zu lasen, sollte natürlich trotzdem noch gegeben sein.

    Optimizer schrieb:

    Die Hardware ist keine magische Black Box. Sobald dein Code auf eine nicht erlaubte Adresse zugreift -> Interrupt -> OS handler -> Exception in deinem Programm: access violation @ 0x00000048 Du kriegst schon eine Exception, wenn du sie willst, aber woher soll klar sein, ob es wegen einem Nullpointer ist?

    Betriebssysteme bieten dafür normalerweise eine entsprechende Funktionalität, um das herauszufinden. Unter Windows lässt sich zB mit SEH schon eine ganze Menge machen.



  • groovemaster schrieb:

    Betriebssysteme bieten dafür normalerweise eine entsprechende Funktionalität, um das herauszufinden. Unter Windows lässt sich zB mit SEH schon eine ganze Menge machen.

    Das ist genauso abstrakt wie alles davor. "Es geht schon" usw. Ich weiß, dass man die böse Adresse in Erfahrung bringen kann. Ich weiß, dass man schätzen kann, ob es auf Grund der Nähe zu 0 dadurch zu Stande gekommen sein könnte, dass man irgendwo einen 0-pointer verwendet hat. Das habe ich alles selber schon geschrieben.

    Aber das OS kann es nicht *wissen*. Woher denn? Mein Programm kann *irgendwie* auf die Idee gekommen sein, auf eine bestimmte Adresse zuzugreifen. Und wie gesagt, ich sehe auch kein praktisches Problem dabei, dass es nicht völlig eindeutig ist.


Anmelden zum Antworten