Fehler markieren; Exceptions, return-Codes oder Error-Attribut?



  • Hilft mir nichts, brauch ein ganzes Minibeispiel, ich habe nun schon 5 Stück abgetippt, immer ist es irgendwas anderes.



  • Du könntest ja mal die Fehlermeldungen zeigen.



  • #ifndef H2RERROR_H
    #define	H2RERROR_H
    
    #include <string>
    #include <iostream>
    #include <exception>
    
    class H2rError : public std::exception {
    public:
      virtual char const* what() throw();
    };
    
    char const* H2rError::what() throw(){
      return "My Error Message";
    }
    
    #endif	/* H2RERROR_H */
    

    Fehlermeldung:
    h2rerror.h: In member function ‘virtual const char* H2rError::what()’:
    In file included from facade.h:16,
    from main.h:9,
    from main.cpp:4:
    h2rerror.h:11: error: expected primary-expression before ‘,’ token
    h2rerror.h: At global scope:
    h2rerror.h:14: error: redefinition of ‘const char* H2rError::what()’
    h2rerror.h:11: error: ‘virtual const char* H2rError::what()’ previously defined here
    h2rerror.h:14: error: expected unqualified-id before ‘{’ token

    Das Beispiel kommt von hier http://www.rpg-studio.de/scientia/C%2B%2B/Tutorial:_16._Fehlerbehandlung#Eigene_Exceptionklassen und wenn ich das Beispiel via Copy und Paste in ein neues Projekt stelle, dann klappt das auch - jemand eine Idee?



  • Wieso zum Himmel überschreibst du what()? Finger davon!



  • Jay1980 schrieb:

    const char* H2rException::what() const throw()
    {
      static std::string s;
      s = "H2rException ist in ";
      s += txt;
      s += " aufgetreten.";
      return s.c_str();
    }
    

    😮

    static std::string s; ?
    Wirklich?



  • Bezogen auf den zuletzt von Jay1980 geposteten Code:

    Konkret fallen mir zwei Dinge auf: Erstens ist what() throw() nicht genug; die von std::exception verwendete Signatur ist

    virtual char const *what() const throw();
    

    Zweitens gehört die Definition der Methode nicht in den Header, sondern in eine eigene Übersetzungseinheit, sonst kriegst du damit später Linkerfehler. Beides wird deinen Compilerfehler aber nicht auslösen, sondern würde dir erst später Probleme verursachen. Die Fehlermeldung

    h2rerror.h:11: error: expected primary-expression before ‘,’ token

    gekoppelt mit der völligen Abwesenheit von Kommata in deinem Code macht mich ziemlich sicher, dass sich da ein Tippfehler eingeschlichen hat, den du nicht mit herauskopiert hast.


  • Mod

    Und von Exception-Spezifikationen lass lieber auch die Finger. Es gibt einen Grund, warum die seit neuestem deprecated sind.

    Und vor Internettutorials hat man dich doch hier bestimmt auch schon gewarnt.



  • Naja, bis ich da auf einem grünen Zweig bin, nehme ich assert-statements und exit mit Fehlercode - gibts halt jedesmal einen Abbruch, wenn irgendwo etwas schiefläuft. Das mit dem Error-Handling scheint mir echt mühsam und tricky zu sein, gerade wenn da wohl erst vor einiger Zeit Ansätze als deprecated eingestuft wurden.

    @SeppJ das heisst ich sollte nur die Exceptions der STL nutzen und keine 'eigenen' bauen sollen?



  • Jay1980 schrieb:

    Das mit dem Error-Handling scheint mir echt mühsam und tricky zu sein, gerade wenn da wohl erst vor einiger Zeit Ansätze als deprecated eingestuft wurden.

    Naja, die Community weiss eigentlich seit vielen Jahren dass die Exception-Specs in C++ nicht wirklich sinn machen.
    Da C++11 das erste echte Update für C++ ist (C++03 war bloss ne Revision des 98er Standards), konnten sie natürlich auch erst jetzt deprecated gemacht werden.



  • Nur kann man sie in diesem Fall ja nicht wirklich weglassen, weil std::exception::what() nun mal const throw() ist (bzw. const noexcept in C++11, aber throw() ist da ja ein Alias für noexcept).

    Es ist ja auch so, dass die Kritik an Exception-Spezifikationen sich vor allem gegen solche richtet, die nur bestimmte Exceptions erlauben. Die Garantie zu geben, dass überhaupt keine Exception geworfen werden kann, ist auch im neuen Standard nicht veraltet, und ob jetzt throw() oder noexcept da steht, ist erst mal von mäßiger Wichtigkeit.



  • Naja, gibt schon Unterschiede zwischen throw() und noexcept.

    Bei throw() war vorgeschrieben dass unexpected() aufgerufen wird wenn doch was geflogen kommt. Und unexpected() war es wiederum erlaubt selbst Exceptions zu werfen.
    Ein Programm konnte sich also nicht darauf verlassen, dass aus einer throw() Funktion niemals was rausfliegt.

    Bei noexcept ist es dagegen garantiert. Wenn sich das Programm nicht daran hält, dann wird terminate() aufgerufen. Und terminate() darf eben keine Exceptions werfen.



  • Jay1980 schrieb:

    Naja, bis ich da auf einem grünen Zweig bin, nehme ich assert-statements und exit mit Fehlercode - gibts halt jedesmal einen Abbruch, wenn irgendwo etwas schiefläuft.

    assert ist okay, um Logikfehler zu finden. Aber niemals für Laufzeitfehler. Und wieso exit() ?



  • hustbaer schrieb:

    Naja, gibt schon Unterschiede zwischen throw() und noexcept.

    Bei throw() war vorgeschrieben dass unexpected() aufgerufen wird wenn doch was geflogen kommt. Und unexpected() war es wiederum erlaubt selbst Exceptions zu werfen.
    Ein Programm konnte sich also nicht darauf verlassen, dass aus einer throw() Funktion niemals was rausfliegt.

    Bei noexcept ist es dagegen garantiert. Wenn sich das Programm nicht daran hält, dann wird terminate() aufgerufen. Und terminate() darf eben keine Exceptions werfen.

    Das ist so nicht ganz korrekt. std::unexpected darf zwar eine Exception werfen, diese wird aber nur aus der Funktion herausgereicht, wenn sie entweder eh auf die Spezifikation passt oder die Spezifikation std::bad_exception beinhaltet, in welchem Fall die geworfene Exception durch ein std::bad_exception-Objekt ersetzt wird. Andernfalls wird std::terminate aufgerufen. So oder so kommt nichts aus der Funktion, was nicht in der Exception-Spezifikation steht. (Vgl. 15.5.2 (2) in C++03)



  • Hm, das hatte ich dann falsch aufgeschnappt, sorry.
    Macht natürlich mehr Sinn so.

    Nochwas: bei noexcept ist glaube ich vorgeschrieben dass kein Stack-Unwinding vor dem Aufruf von terminate() stattfindet. Auch ein Unterschied zu (und IMO eine Verbesserung gegenüber) throw(), wo Stack-Unwinding vorgeschrieben ist.



  • Ich fürchte, da habe ich ebenfalls schlechte Neuigkeiten:

    C++11 15.5.1 (2) schrieb:

    In such cases, std::terminate() is called (18.8.3). In the situation where no matching handler is found, it is implementation-defined whether or not the stack is unwound before std::terminate() is called. In the situation where the search for a handler (15.3) encounters the outermost block of a function with a noexcept-specification that does not allow the exception (15.4), it is implementation-defined whether the stack is unwound, unwound partially, or not unwound at all before std::terminate() is called. In all other situations, the stack shall not be unwound before std::terminate() is called. An implementation is not permitted to finish stack unwinding prematurely based on a determination that the unwind process will eventually cause a call to std::terminate().



  • Nexus schrieb:

    Jay1980 schrieb:

    Naja, bis ich da auf einem grünen Zweig bin, nehme ich assert-statements und exit mit Fehlercode - gibts halt jedesmal einen Abbruch, wenn irgendwo etwas schiefläuft.

    assert ist okay, um Logikfehler zu finden. Aber niemals für Laufzeitfehler. Und wieso exit() ?

    Mir geht es nur darum, dass ich ein zu Ende laufen des Programms verhindern will, wenn ich merke, dass die Daten im Programm fehlerhaft sind - da dachte ich, wie ziehe ich denn am besten den Stecker und mir kam exit() und assert() - je nachdem, wie ich gerade pruefen kann. Ich habe aber nur den Thread da gesehen, http://www.c-plusplus.net/forum/210888 und exit() scheint doch nicht so der Bringer zu sein, es wäre schon besser das Programm 'sanft' aber in jedem Fall zu beenden. Bei assert muss ich aufpassen, dass mir in der Release-Version nicht automatisch die Prüfung wegfällt, daher auch keine optimale Lösung, normalerweise sollten die assert-Statements da ja draußen oder entwertet sein, wenn ich das richtig verstanden habe.

    Okay, mir scheint folgender Weg als geeignet: ich werfe einfach immer exceptions, aber lege dafür eben keine eigene Exception-Klasse an. Am Ende meines catch-Blocks beende ich das Programm in dem ich nach oben weiter werfen lasse, das heisst, das irgendwann terminate() kommt und ein sanftes Ende eintritt - soweit richtig?

    Code poste ich dann, wenn ich damit etwas rumprobiert habe.



  • Kann ja nicht sein, das du jetzt keine eigenen Exception-Typen definieren kannst? Das ist wirklich ganz einfach. Mach das so: leite von einer speziellen Std-Exception ab und übergebe im Ctor deinen Fehlertext. Es macht keinen Sinn what() zu überschreiben.

    class my_error : public std::exception
    {
         public:
             my_error(const std::string &err) : std::exception(err)
             {};
    };
    
    throw my_error("Da ist was schief gelaufen");
    

    Hier noch ein paar Infos:
    http://msdn.microsoft.com/en-us/library/t65b74ad.aspx



  • @Jay1980: Du brauchst keine Exceptions nach oben werfen. Das tut C++ für dich automatisch, wenn du sie nicht fängst. Und ne Exception fangen und danach weiterwerfen ergibt selten bis gar nicht Sinn.

    @Artchi: So sah ja auch mein Vorschlag aus.



  • Endlich hat es geklappt!

    Danke fuer die Tipps insbesondere Archi.

    Unten mal der kompilierende Code und da ich ja das Programm wirklich beenden will, muss ich doch weiterwerfen, sonst macht das Programm ja unterhalb des letzten catch-Blocks weiter - das will ich ja gerade nicht. Mir ist schon klar, dass man mit Exception-Handling die auftretenden Fehler wieder gerade zu biegen versucht, aber in meinem Fall will ich lieber einen Abbruch, daher meine Taktik mit dem 'Hochwerfen und auf terminate() vertrauen'.

    // Error-Klasse
    class H2rError : public std::runtime_error {
    public:
      H2rError() : std::runtime_error( "H2rError-Fehlermeldung!"){}
    };
    
    // Funktion bei der etwas anbrennen kann
    
    double Facade::computePairEntropyOfStrings(const std::string& stringToComputeOne, const std::string& stringToComputeTwo) {
    
      try {
        checkForSameStringLength(stringToComputeOne, stringToComputeTwo); // H2rError
      } catch( H2rError &e ){
        std::cout << e.what() << std::endl;
        std::cerr << e.what() << std::endl;
        throw; // fuer Abbruch weiterwerfen
      }
      seqan::CharString columnStringOne(stringToComputeOne);
      seqan::CharString columnStringTwo(stringToComputeTwo);
      double res = getPairEntropyOfColumnStrings(columnStringOne, columnStringTwo);
      return res;
    }
    
    // Funktion die wirft
    void Facade::checkForSameStringLength(const std::string& stringToComputeOne, const std::string& stringToComputeTwo){  
    
      if ( stringToComputeOne.size() != stringToComputeTwo.size() ){
        throw H2rError();
      }
    
    }
    

    Okay, und die Zeichenkette im Konstruktor übergeben ist geschickter, das passe ich noch an.



  • Erstens sah meine Klasse genau so aus, bis auf dass ich struct verwendet habe und mir damit public erspart habe. Zweitens macht das, was du tust, keinen Sinn. Du gibst Fehlermeldungen aus und wirfst weiter. Das ist so, als würdest du es 2 mal sagen. Und dass du hier eine Exception werfen willst, lässt darauf schließen, dass die Ausgabe keinen Sinn ergibt. Mach den try-catch Block also Weg und arbeite ganz normal weiter nach dem Check.


Anmelden zum Antworten