Eigene Exception-Klasse -> kurze frage



  • gast_xy schrieb:

    Hallo,

    ich bin etwas unsicher was die Exception-Problematik aufwirft und ich habe schon einiges gelesen.

    Ich würde gerne wissen ob folgendes schwachsinnig wäre und wenn ja - wie man es gängiger weise macht:

    Ich lege eine eigene Klasse Exception an, die lauter Methoden hat die jeweils einfach nur eine ausgabe produzieren.

    Und wenn dann z.B etwas passiert dann wird einfach die Methode der Exception klasse aufgerufen. Also von Exception werfen kann hier nicht mehr die rede sein.

    ...
    if(a<b)
        Exception::throw_invalid_size();
    ...
    
    //hier der Exception.cpp teil
    
    void Exception::throw_invalid_size()
    {
         std::cout << "Invalid size for parameter b!" << std::endl;
    }
    

    Danke für Hilfe

    Hi, das macht wirklich keinen Sinn, da du nicht gezwungen bist darauf zu reagieren.

    Zur File-Geschichte: Also direkt ein exit finde ich bei sowas immer schon hart, versuch dein Programm so zu stricken das du ohne das Programm zu beenden Gültig wieder aus deiner Funktion rauskommst, sei es mit nem throw oder return-Wert.



  • Und wie sähe dann eine eigene Exception Klasse aus?
    Dazu müsste ich wohl alles in try und catch Blöcke packen oder?
    Wäre das dann nicht langsamer als ohne try und catch?

    Danke für weitere Hilfe



  • #include <iostream>
    using namespace std;
    class ExceptionHandler
    {
    	int error;
    public:
    	ExceptionHandler(int error_) : error(error_) {}
    	void output() 
    	{
    		switch (error)
    		{
    		case 0:
    			cout << "Fehler " << error << endl;
    			break;
    		default:
    			cout << "Unbekannt " << endl;
    			break;
    		}
    	}
    
    };
    
    class Test
    {
    public:
    	Test(){}
    	void doSomething()
    	{
    		throw ExceptionHandler(0);
    	}
    };
    
    int main()
    {
    	try
    	{
    		Test t;
    		t.doSomething();
    	}
    
    	catch(ExceptionHandler& e)
    	{
    		e.output();
    	}
    	return 0;
    }
    

    So könnte man es machen... (Wegen der Geschwindigkeit weiß ich nicht...)

    Gruß
    zeigerzeiger



  • Hi,

    also ich würde exceptions möglichst wenig "Eigenintelligenz" verpassen. Üblicherweise hinterlegt man in ihnen im Wesentlichen die Information, was schief gelaufen ist. Was mit dieser Information angefangen wird, sollte IMO derjenige entscheiden, der die exception fängt ...

    In dem Beispiel oben z.B.: Wer sagt denn, dass die exception (oder die werfende Klasse/Funktion) in einem Umfeld verwendet wird, in der eine Ausgabe möglich/gewünscht ist ? Wenn das Teil in einer GUI läuft, kommt ein cout nicht besonders gut ... ebensowenig, wenn es in einem Multithreadingserver läuft (und die Standardausgabe überflutet, die Performance runterreißt und Synchronisierungsproblerme verursacht).

    Schönes Beispiel dafür: std::exception
    Ich mach' das dann immer so:

    struct myExcept : public runtime_error {
          int error;
          myExcept(int& r, string const& t) : runtime_error(t), error(r) {}
          char const* what() const throw() { // erben von runtime_error
             ostringstream ost; 
             ost << runtime_error::what() << "; error: " << error;
             return ost.str().c_str();
          }
       };
    
    // main.cpp
    int main() {
       try {
          meineFkt(); // kann myExcept werfen
       }
       catch(myExcept& e) {
          logErrorMsg(myExcept.what()); // HIER interessiert mich der "Fehlertext"
    
          switch(e.error) { // hier kann ich je nach Fehlernummer reagieren
             case 1: return 5;
             case 2: retry(); break;
             case 3: terminate();
             case 4: cout << "Das war jetzt bloed\n"; break;
             default: cerr << "Weiss nicht";
          }
       }
       return 0;
    }
    

    Gruß,

    Simon2.



  • Danke -

    wie verwende ich jetzt aber das struct , die methode meineFkt() muss ja das struct kennen. D.h doch ich muss in jeder Klasse dieses Struct zugänglich machen oder?
    Wäre es evtl. besser/möglich statt dem struct ne Klasse zu schreiben die man dann included? Oder rede ich jetzt wirres Zeug? 😕



  • gast_xy schrieb:

    Danke -

    wie verwende ich jetzt aber das struct , die methode meineFkt() muss ja das struct kennen. D.h doch ich muss in jeder Klasse dieses Struct zugänglich machen oder?
    Wäre es evtl. besser/möglich statt dem struct ne Klasse zu schreiben die man dann included? Oder rede ich jetzt wirres Zeug? 😕

    Struct ist dasselbe wie eine Klasse.
    Einziger Unterschied ist der "Default beim Zugriffsschutz": Bei struct ist alles public, was nicht anders gekennzeichnet ist, bei class eben private.

    Ergo: Du kannst Deinen vorgeschlagenen Weg gehen - egal, ob Du es struct oder class nennst.

    Gruß,

    Simon2.



  • Danke Simon!

    Nur noch eine kleine Frage zur Geschwindigkeit: da ich im Moment an einer zeitkritischen Anwendung bastele (man mag es kaum glauben wegen meiner anfängerfragen) ist es mir sehr wichtig dass ich möglichst wenig Zeit verbrate.
    Das verwenden der try und catch Blöcke - ist das langsamer als z.b das Blosse verwenden wie oben z.B mit dem exit(1) und der ausgabe?

    Danke vielmals für eure Super Hilfe



  • Simon2 schrieb:

    Hi,

    also ich würde exceptions möglichst wenig "Eigenintelligenz" verpassen. Üblicherweise hinterlegt man in ihnen im Wesentlichen die Information, was schief gelaufen ist. Was mit dieser Information angefangen wird, sollte IMO derjenige entscheiden, der die exception fängt ...

    In dem Beispiel oben z.B.: Wer sagt denn, dass die exception (oder die werfende Klasse/Funktion) in einem Umfeld verwendet wird, in der eine Ausgabe möglich/gewünscht ist ? Wenn das Teil in einer GUI läuft, kommt ein cout nicht besonders gut ... ebensowenig, wenn es in einem Multithreadingserver läuft (und die Standardausgabe überflutet, die Performance runterreißt und Synchronisierungsproblerme verursacht).

    Schönes Beispiel dafür: std::exception
    Ich mach' das dann immer so:

    struct myExcept : public runtime_error {
          int error;
          myExcept(int& r, string const& t) : runtime_error(t), error(r) {}
          char const* what() const throw() { // erben von runtime_error
             ostringstream ost; 
             ost << runtime_error::what() << "; error: " << error;
             return ost.str().c_str();
          }
       };
    
    // main.cpp
    int main() {
       try {
          meineFkt(); // kann myExcept werfen
       }
       catch(myExcept& e) {
          logErrorMsg(myExcept.what()); // HIER interessiert mich der "Fehlertext"
    
          switch(e.error) { // hier kann ich je nach Fehlernummer reagieren
             case 1: return 5;
             case 2: retry(); break;
             case 3: terminate();
             case 4: cout << "Das war jetzt bloed\n"; break;
             default: cerr << "Weiss nicht";
          }
       }
       return 0;
    }
    

    Gruß,

    Simon2.

    Jo, da geb ich dir recht... Allerdings war das auch nur ein kurzer Schnellschuß in der Konsole... Trotzdem gehe ich hier 100% konform mit dir...

    Gruß
    zeigerzeiger



  • gast_xy schrieb:

    Danke Simon!

    Nur noch eine kleine Frage zur Geschwindigkeit: da ich im Moment an einer zeitkritischen Anwendung bastele (man mag es kaum glauben wegen meiner anfängerfragen) ist es mir sehr wichtig dass ich möglichst wenig Zeit verbrate.
    Das verwenden der try und catch Blöcke - ist das langsamer als z.b das Blosse verwenden wie oben z.B mit dem exit(1) und der ausgabe?

    Danke vielmals für eure Super Hilfe

    Hi,

    also try-catch installieren "exceptionhandler" und verbrauchen ein wenig Zeit, aber nicht besonders viel. Wenn Du nur einen einzigen Block hast (und den nur ein einziges mal durchläufst), sollte das nicht besonders kritisch sein.

    Viel Performance kostet das Werfen und Fangen einer exception - aber das sollte ja nur im Fehlerfall passieren und dort spielt üblicherweise die Performance kaum eine Rollen.

    AAAAABER: Kümmere Dich um Performance, wenn es dran ist ! Und das ist dann, wenn die Anwendung fachlich korrekt arbeitet und Du in der Testphase bist. Alles Andere ist üblicherweise vergebliche Liebesmüh' (kannst ja mal nach "premature optimization" googlen - da gibt's so Aussage wie "the root of all evil" und so 😉 ).

    Gruß,

    Simon2.



  • danke,
    nehmen wir an mein code arbeitet korrekt und kommt zu keiner exception,
    wäre es dann nicht außerordentlich langsamer z.B Hundert von try anweisungen durchzulaufen? Oder wäre es da vernachlässigbar.

    Mit dem premature optimization ist schon klar - aber ich würde halt gern wissen ob die verwendung von try viel langsamer ist als z.b nur ne if-abfrage - dürfte ja intern dann wohl dasselbe werden oder?



  • Mati schrieb:

    danke,
    nehmen wir an mein code arbeitet korrekt und kommt zu keiner exception,
    wäre es dann nicht außerordentlich langsamer z.B Hundert von try anweisungen durchzulaufen? Oder wäre es da vernachlässigbar....

    Das kann ich Dir nicht beantworten - das solltest Du einfach mal messen.

    Aaaaber: Es entspricht NICHT dem Durchlauf von 100 if-Abfragen. Die "if-Abfragen" (Suche nach passendem exception-handler) würden nur im Fehlerfall durchgeführt. So gesehen kann der Code sogar schneller werden (allerdings darf man nicht die "ifs vor dem throw" vergessen)....
    Das Vorgehen ist einfach ein Anderes.

    Wesentlicher ist hier die Struktur Deines Programms: 100 try-Durchläufe würden mich sehr skeptisch machen ! Denk dran: Man macht nur da ein try, wo man auch wirklich auf eine Fehlersituation reagieren will. Das sind genau betrachtet recht wenige Stellen....
    Bitte nicht sowas:

    int f() {
       try {
          fachlicheFkt1();
          fachlicheFkt2();
          fachlicheFkt3();
       }
       catch(except& e) {
          return 1;
       }
       return 0;
    }
    
    int main() {
       for(int i=0; i<100000; ++i) {
          if(f()) return 1;
       }
       return 0;
    }
    

    sondern:

    int f() {
       fachlicheFkt1();
       fachlicheFkt2();
       fachlicheFkt3();
       return 0;
    }
    
    int main() {
       try {
          for(int i=0; i<100000; ++i) {
             f();
          }
       }
       catch(except& e) {
          hackeBenutzerDieHandAb();
          throw; // man kann auch einfach weiterwerfen
       }
       return 0;
    }
    

    (und selbst da würde ich eher die 100000 Funktionscalls von f() wegoptimieren als mich um try/catch zu sorgen.)

    Gruß,

    Simon2.



  • man packt ein try um den ganzen code herum. wieviele kleine try blöcke dann noch intern sind, ist egal. der stack unwind code etc. wird generiert. so oder so.



  • Shade Of Mine schrieb:

    man packt ein try um den ganzen code herum. wieviele kleine try blöcke dann noch intern sind, ist egal. der stack unwind code etc. wird generiert. so oder so.

    Äh da bin ich mir nicht ganz sicher... ich weiß nicht genau, wo/wann die exceptionhandler installiert werden.

    try { // <- wird hier auch schon der exceptionhandler für B installiert ...
       a();
       try { // <- .. oder erst hier ?
          b();
       }
       c();
       catch(B&) {}
    }
    catch(A&) {}
    

    Gruß,

    Simon2.


Anmelden zum Antworten