Return false oder throw exception



  • Was spricht dagegen eine eigene Klasse dafür zu haben?

    throw ExceptionMismatch(filename, line, input);
    


  • Es gibt bereits abgeleitete Klassen von std::exception, z.B. std::runtime_error. Die haben dann auch einen Konstruktor, der einen String mit der Fehlermeldung nimmt:

    try {
      if (!boost::regex_match(input, what, expression)) {
        std::string msg("Mismatch: ");
        msg += filename + " (" + boost::lexical_cast<std::string>(line) + "): " + input + "\n";
        throw std::runtime_error(msg);
      }
    } catch (const std::exception& e) {
      std::cerr << "Exception: " << e.what() << "\n";
    }
    


  • Tomahawk schrieb:

    // Noch blöder, da ich einen zusätzlichen std::ostringstream benötige.
    try {
      std::ostringstream errors;
      if (!boost::regex_match(input, what, expression)) {
        errors << "Mismatch: " << filename << " (" << line << "): " << input << "\n";
        throw std::exception();
      }
    } catch (const std::exception& e) {
      std::cerr << "Exception: " << errors.str() << "\n";
    }
    

    Die Frage stellt sich nicht, da das da nicht kompiliert.

    Außerdem ist es reichlich sinnlos, den Exception-Mechanismus (Rücksprung über mehrere Methoden hinweg) auszuhebeln indem man throw und catch in derselben Methode hat, die noch dazu auf einen im übergeordneten Scope angesiedelten Error-String zugreift.



  • Danke für den Tipp mit std::runtime_error!

    hhhh schrieb:

    Was spricht dagegen eine eigene Klasse dafür zu haben?

    throw ExceptionMismatch(filename, line, input);
    

    Hmmm, es gibt etwa 20 verschiedene Fehlermeldungen mit verschiedenen Kombinationen von Parametern = 20 Klassen?

    pumuckl schrieb:

    ...
    std::string msg("Mismatch: ");
    msg += filename + " (" + boost::lexical_cast<std::string>(line) + "): " + input + "\n";
    ...
    

    Funktioniert das tatsächlich, bekomme so eine "cannot add two pointers" Fehlermeldung?

    Vielleicht ist folgende Lösung mit einem std::runtime_error am generischsten (KISS-Prinzip):

    std::ostringstream errors;
    ...
    try {
      std::ifstream ifs(filename);
      if (ifs.fail()) {
        errors << "Cannot find or open file: " << filename << "\n";
        throw std::runtime_error(errors.str()); 
      }
      if (!boost::regex_match(input, what, expression)) { 
        errors << "Found mismatch: " << filename << " (" << line << "): " << input << "\n"; 
        throw std::runtime_error(errors.str()); 
      } 
    } catch (const std::exception& e) { 
      std::cerr << "Exception: " << e.what() << "\n"; 
    }
    


  • Tomahawk schrieb:

    pumuckl schrieb:

    std::string msg("Mismatch: ");
    msg += filename + " (" + boost::lexical_cast<std::string>(line) + "): " + input + "\n";
    

    Funktioniert das tatsächlich, bekomme so eine "cannot add two pointers" Fehlermeldung?

    Kommt drauf an, was filename (und evtl. input) sind. Ich war davon ausgegangen, dass du benannte String-Variablen auch tatsächlich als Strings verwaltest, nicht als char-Pointer.



  • Tomahawk schrieb:

    Hmmm, es gibt etwa 20 verschiedene Fehlermeldungen mit verschiedenen Kombinationen von Parametern = 20 Klassen?

    Nein. Es stellt sich zwar die Frage, warum es so viele verschiedene Parameterkombinationen gibt, aber den Konstruktor zu überladen scheint mir doch wesentlich sinnvoller.



  • cooky451 schrieb:

    Verwende an den Stellen Exceptions, an denen ein bestimmtes Token benötigt wird, und du ohne auch nicht mehr weiter machen kannst. Definiere aber Testfunktionen die die Fehlerbehandlung über Rückgabewerte regeln, damit die Exceptions nicht zu einem if-else werden.

    PS: Wirf keinen std::string oder so, sondern einen std::runtime_error. Wenn du noch einen Fehlercode brauchst, erbe von std::runtime_error.

    Nur mal so off-topic: Was sind denn exceptions, die nicht runtime_errors sind? Also beispielsweise "throw compile_time_error". Oder "throw error_before_program_runs" 😃 .



  • ich bins schrieb:

    Was sind denn exceptions, die nicht runtime_errors sind?

    Schau doch einfach in die Standard-Referenz deiner Wahl. Beispielsweise logic_error. Wird zwar auch erst zur Laufzeit geworfen und gefangen, besagt aber, dass beim Coding Blödsinn passiert ist. runtime_error sind Fehler, die zur Compilerzeit nicht verhindert werden können.



  • pumuckl schrieb:

    Tomahawk schrieb:

    pumuckl schrieb:

    std::string msg("Mismatch: ");
    msg += filename + " (" + boost::lexical_cast<std::string>(line) + "): " + input + "\n";
    

    Funktioniert das tatsächlich, bekomme so eine "cannot add two pointers" Fehlermeldung?

    Kommt drauf an, was filename (und evtl. input) sind. Ich war davon ausgegangen, dass du benannte String-Variablen auch tatsächlich als Strings verwaltest, nicht als char-Pointer.

    <filename> und <input> sind schon std::string Objekte, aber " (" oder "): " sind doch char-Pointer bzw. werden als solche Fehlerquelle ausgegeben. However, es kann eben keinen überladenen Stringoperator geben, der zwei chars "addiert".



  • Tomahawk schrieb:

    However, es kann eben keinen überladenen Stringoperator geben, der zwei chars "addiert".

    Mal sehen:
    filename + " (" + boost::lexical_caststd::string(line) + "): " + input + "\n";
    -> string + char* + string + char* + string + char*
    = string + string + char* + string + char*
    = string + char* + string + char*
    = string + string + char*
    = string + char*
    = string

    Wo sind da zwei char* nebeneinander, wenn filename und input wirklich keine char* sind?



  • pumuckl schrieb:

    Tomahawk schrieb:

    However, es kann eben keinen überladenen Stringoperator geben, der zwei chars "addiert".

    Mal sehen:
    filename + " (" + boost::lexical_caststd::string(line) + "): " + input + "\n";
    -> string + char* + string + char* + string + char*
    = string + string + char* + string + char*
    = string + char* + string + char*
    = string + string + char*
    = string + char*
    = string

    Wo sind da zwei char* nebeneinander, wenn filename und input wirklich keine char* sind?

    OK, das passt, wenn es tatsächlich immer mind. ein std::string zwischen zwei char* steht. Aber darauf kann ich mich in meinen Exeption-Routinen nicht verlassen und daher sind mir die std::ostringstreams robuster.



  • @Tomahawk
    So lange du mit einem std::string anfängst, sollte es funktionieren. Auch sowas wie string + char* + char* + char*.
    Weil "+" halt von links nach rechts ausgewertet wird.



  • Nachfolgender Code ist compilierbar. Wenn ich allerdings die auskommentierten Zeilen aktiviere bekomme ich folgende Fehlermeldung:

    sstream(603): error C2248: 'std::basic_ios<_Elem,_Traits>::basic_ios' : cannot access private member declared in class 'std::basic_ios<_Elem,_Traits>'

    Woran das liegt kann ich mir im Moment nicht erklären. Es könnte mit der Instanzierung in throw exception(filename) zu tun zu haben 😕

    #include <fstream>  // NOLINT
    #include <iostream>  // NOLINT
    #include <string>
    
    class Exception : public std::exception {
    public:
      explicit Exception(const std::string& filename) {
        str = filename;
        // oss << filename;
      }
    
      const char* what() const {
        return str.c_str();
        // return oss.str().c_str();
      }
    
    private:
      std::string str;
      // std::ostringstream oss;
    };
    
    int main() {
      std::string filename("some.txt");
      try {
        throw Exception(filename);
      } catch (const std::exception& e) {  // NOLINT
        std::cerr << "Exception: " << e.what() << "\n";
      }
      return 0;
    }
    


  • Tomahawk schrieb:

    bekomme ich folgende Fehlermeldung:

    Das ist nur ein Teil der Fehlermeldung. Bei templates ist das leider ziemlich mühsam, den eigentlichen Fehler zu finden. Schau mal weiter runter bis du findest, wo in deinem Code die Ursache für den fehler liegt.[/quote]



  • Deine Exception-Klasse ist ziemlich seltsam. Erstens würde ich nicht von std::exception ableiten, sondern von std::runtime_error, da dies eine spezifischere Beschreibung ist. Desweiteren musst du what() nicht überschreiben. Der Klassennamen ist ungefähr so brauchbar, wie ein

    throw "blafuh";
    

    Wie wäre es mit:

    struct my_error : std::runtime_error
    {
        explicit my_error(std::string const& what)
            : runtime_error(what)
        {}
    };
    

    Das ganze dann noch in ein hübsches Makro verpackt, falls du mehrere Exception-Klassen brauchst:

    #define DEFINE_ERROR(name, base) \
        struct name##_error : base \
        { \
            explicit name##_error(std::string const& what) \
                : base(what) \
            {} \
        };
    

    Und dann noch einen guten Namen für die Exception auswählen:

    DEFINE_ERROR(some_name, std::runtime_error)
    

    😉



  • Tomahawk schrieb:

    Nachfolgender Code ist compilierbar. Wenn ich allerdings die auskommentierten Zeilen aktiviere bekomme ich folgende Fehlermeldung:

    sstream(603): error C2248: 'std::basic_ios<_Elem,_Traits>::basic_ios' : cannot access private member declared in class 'std::basic_ios<_Elem,_Traits>'

    Objekte, die geworfen werden, werden immer kopiert (auch wenn du per Referenz fängst), d.h. insbesondere wird von allen nicht einfachen Membern der Kopierkonstruktor aufgerufen. Aber C++-Streams sind nicht kopierbar.


Anmelden zum Antworten