Designfrage: Err-Code --> Exceptions



  • Hallo zusammen,

    ich arbeite an einer Netzwerkprotokoll-Umsetzung, bei der ich im Fehlerfall ein Datenpaket mit einem Fehlercode als Antwort erhalte.

    Erhalte ich solch ein Fehlercode, möchte ich eine Exception auslösen. Dafür habe ich eine Exception-Klasse erstellt, die den Fehlercode im Konstruktur übergeben bekommt und über Description() oder what() eine entsprechende Fehlermeldung ausgibt:

    //MyException.h:
    #include <exception>
    
    namespace Exceptions{
       class MyException : public std::exception
       {
       public:
          MyException(unsigned int errCode);
          virtual ~MyException();
       public:
          const char* Description() const { return m_errMsg; }
       private:
          const char* m_errMsg;
       };
    };
    

    Nun möchte ich die Fehler zur Laufzeit unterschiedlich behandeln können. Zwei Möglichkeiten sind mir dazu eingefallen:

    Entweder ich füge der Exception-Klasse ein Methode zur Typidentifizierung hinzu oder ich erstelle für jeden Fehler eine eigene Exception-Klasse. Die zweite Variante gefällt mir besser, jedoch möchte ich nicht auf den Komfort verzichten, dass ich für einen Fehler immer den gleichen Aufruf ( throw MyException(errCode) ) nutzen kann und die Auswertung der Fehlercodes und die Zuordnung der richtigen Fehlermeldung zentral geschieht.

    //MyExceptions_A.h:
    #include <exception>
    
    namespace Exceptions{
       class MyException : public std::exception
       {
       public:
          enum ErrType{ /*...*/ };
       public:
          MyException(unsigned int errCode);
          virtual ~MyException();
       public:
          const char*  Description() const { return m_errMsg;  )
          ErrType      Type()        const { return m_errType; }
       private:
          const char*  m_errMsg;
          ErrType      m_errType;
       };
    };
    
    //MyExceptions_B.h:
    #include <exception>
    
    namespace Exceptions{
       class MyException : public std::exception
       {
       public:
          MyException(unsigned int errCode);
          virtual ~MyException();
       public:
          const char*  Description() const { return m_errMsg;  )
       private:
          const char*  m_errMsg;
       };
    
       // Für jeden Fehlercode eine eigene Exception-Klasse:
       class MyException_1 : public MyException {};
       class MyException_2 : public MyException {};
       class MyException_3 : public MyException {};
       //...
    
       //Fehlercode auswerten und entsprechende Exception werfen
       void ThrowException(unsigned int errCode);
    };
    

    Bei der letzten Variante müsste man dann ThrowException(errCode) aufrufen und hätte nicht das gewohnte throw MyException(errCode) .

    Was würdet Ihr sagen? Ist Variante B OK? Oder habt ihr andere Ideen?

    Es gibt zehn unterschiedliche Fehlercodes. Eine Erweitung der Fehlercodes ist nicht vorgesehen.



  • Ich habe das Gefühl, dass das hier nicht der richtige Anwendungsfall für Exceptions ist. Wenn ich z.B. an HTTP denke, ist eine 404 Antwort kein Ausnahmezustand für das Progamm. Das ist normal, damit rechnet man bei einer Verbindung. Exceptions sind nicht zu Steuerung von Verzweigungen im Code gedacht.



  • Hallo brotbernd,

    da hast Du natürlich vollkommen recht. Vielleicht habe ich mich auf die Exception so festgelegt, da sie in der Protokoll-Spezifikation "Exceptions" heißen.

    Es gibt jedenfalls ein paar Fehler, bei denen z.B. eine Wiederholung der Anfrage möglich wäre ("Busy-Exception"). Andere Fehler (Anfrage ungültig, Adressierung ungültig, Datenwerte ungültig,...) sind in meinen Augen dann schon Ausnahmen.

    Mir fehlt da aber zugegebenermaßen noch die Erfahrung das richtige Gespür.



  • Eine mögliche Lösung wäre eine entsprechende "Converter"-Funktion:

    void convertErrorToException(unsigned int error_code) {
      switch(error_core) {
        case WRONG_ADDRESS: throw WrongAddress();
        case WRONG_DATA: throw WrongData();
        // usw.
      }
    }
    

    Einfach die Funktion aufrufen und du bekommst deine Exception. Allerdings bin ich wirklich nicht sicher, ob das mit Exceptions so eine gute Lösung ist. Werfen nur um gleich wieder gefangen zu werden? Das geht eigentlich genausogut mit Weiterreichen des Error-codes. Fängt der Nutzer eine Exception nicht ab, wird das Programm beendet! Wäre ja verdammt toll, wenn das Programm "abstürzt" nur weil der User was falsches eingibt. Am besten geschieht dies direkt in der Config und der User heult den Support voll, dass das Programm kaputt ist, weil es gar nimmer startet 😉



  • l'abra d'or schrieb:

    Eine mögliche Lösung wäre eine entsprechende "Converter"-Funktion:

    void convertErrorToException(unsigned int error_code) {
      switch(error_core) {
        case WRONG_ADDRESS: throw WrongAddress();
        case WRONG_DATA: throw WrongData();
        // usw.
      }
    }
    

    Einfach die Funktion aufrufen und du bekommst deine Exception.

    So hatte ich es in der Variante B angedacht (Funktion ThrowException() ).

    l'abra d'or schrieb:

    Allerdings bin ich wirklich nicht sicher, ob das mit Exceptions so eine gute Lösung ist. Werfen nur um gleich wieder gefangen zu werden?

    Ich arbeite gerade an einem allgemeingültigen Client. Wie der Anwender auf welche Fehlermeldungen regaiert, wollte ich ihm überlassen.

    In meinen Anwendungsfällen, wird einfach nur der Fehlertext ausgegeben. Aber wenn z.B. ein Anwender Daten zyklisch pollen möchte, dann kann er je nach Fehler entscheiden, ob er im nächsten Zyklus wieder die gleiche Anfrage raus schickt oder den User informiert und abbricht.

    l'abra d'or schrieb:

    Fängt der Nutzer eine Exception nicht ab, wird das Programm beendet! Wäre ja verdammt toll, wenn das Programm "abstürzt" nur weil der User was falsches eingibt. Am besten geschieht dies direkt in der Config und der User heult den Support voll, dass das Programm kaputt ist, weil es gar nimmer startet 😉

    Also das wäre in meinen Augen aber ein schwerwiegender Fehler des Anwenders meines Programmcodes, wenn er sich um mögliche Exceptons nicht kümmert und einfach ignoriert.

    Aber wo zieht man die Grenze zwischen "normalen" Fehlern und Ausnahmen? Wenn ich Daten in eine Zieladresse eines Empfängers schreiben möchte und die Adresse ist ungültig, dann ist das in meinen Augen eine Ausnahme, auf die reagiert werden muss. Wenn allerdings die Anfrage vom Server abgewiesen wird, da der Empfäger busy ist, dann kann man die Anfrage z.B. verwerfen oder es nach Zeit x nochmal probieren. Schon eher ein "normaler" Fehler - aber dann müsste ich einer WriteData()-Methode einen Returnwert für den Fehlercode verpassen. 😞



  • Ich bleibe mal beim Bsp HTTP. Ich würde für jeden Statuscode einen regulären Handler schreiben. Mit all diesen Antworten rechne ich, wenn ich mich mit einem Server verbinde. Ein Ausnahme wäre z.B. wenn der Rechner keine Netzwerkkarte hat oder jemand das Kabel durchgeschnitten hat:) In so einem Fall macht es keinen Sinn, das Programm im derzeitigen Zustand weiterlaufen zu lassen. Entweder weist das Programm den Anwender darauf hin, das Kabel wieder zu flicken oder beendet sich einfach, weil es mit kaputten Kabel sinnlos ist.
    Ich weiß zwar nicht genau was Du in dinem Beispiel mit "ungültige Adresse" genau meintest, aber das klingt für mich auch nach einem Fall für eine Ausnahme.



  • Mit "ungültige Addresse" meine ich die Adressierung eines ungültigen "virtuellen" Speicherbereiches, der durch das Protokoll definiert ist. Es werden Datenbereiche definiert, die als universelle Datenschnittstelle nach außen dienen.

    Nun soll z.B. mein Client Daten vom Server lesen oder Daten in diese Bereiche schreiben. Der Datenbereich ist im Protokoll festgelegt. Nun kann es aber aufgrund des Protokoll-Designs vorkommen, dass dieser Adressbereich überschritten wird. Das sollte der Client natürlich vor dem Abschicken der Anfrage überprüfen, aber es ist eben technisch möglich. Dafür gibt es einen entsprechenden Fehlercode als "Exception-Response".

    Für mich eine Exception, da es nicht vorkommen darf. Wenn doch, hat der Client vergessen eine unsinnige Eingabe zu überprüfen.


Anmelden zum Antworten