Design Problem: Exceptions



  • Danke für die Tipps. 🙂

    Und musst du bei einem BAD_ERROR wirklich das Plugin killen? Das ist IMHO auch nicht so gut. Vielleicht ist das Plugin ja wichtig, aber es hab ne unerwartete Eingabe, die das Plugin gewaltig verwirrt hat - weiterlaufen tut es ja trotzdem.

    Wenn es eine Eingabe ist, die das Plugin gewaltig verwirrt hat, und es kein schlimmer Fehler ist, wird nicht BAD_ERROR geworfen, BAD_ERROR wird nur geworfen, wenn es wirklich keine Möglichkeit gibt weiterzuarbeiten.
    Das richtig einzuschätzen liegt im Ermessen desjenigen, der die Plugins schreibt.

    Hm, zentral... Heißt das, ich baue einen try-Block um den Funktionsaufruf jener Funktion auf?
    Ich wollte es eigentlich so handhaben, dass die Klasse das selber korrigieren kann. Oder soll ich eine andere Funktion nehmen bzw. auch eine weitere Klasse darum herum?

    Das Weiterwerfen ist ausschließlich zum Loggen gedacht, d.h. das kann der Benutzer selber entscheiden, Anzeigen kann er's sicher auch.

    Jedenfalls danke für die Tipps, wenn ich Zeit habe, werde ich das mal gründlich überarbeiten.

    MfG Eisflamme 🙂



  • OT: Deine Plugins, sind das DLL-Dateien?
    Eine C++ Exception darf eine DLL nämlich niemals verlassen. Fehler lassen sich bei DLLs nur über Rückgabewerte oder Hilfsfunktionen (GetLastError) erkennen.



  • Eine C++ Exception darf eine DLL nämlich niemals verlassen

    Doch das geht wenn man die Laufzeitbibliothek als DLL linkt.



  • such schrieb:

    Eine C++ Exception darf eine DLL nämlich niemals verlassen

    Doch das geht wenn man die Laufzeitbibliothek als DLL linkt.

    Nein. DLLs haben ein vorgeschriebenes Interface. Diese Regeln legen unter anderem fest, dass eine DLL ausschließlich Funktionen exportieren darf, die einer bestimmten Aufrufkonvention folgen.
    Exceptions waren dort nie eingeplant und verletzen diese Aufrufkonvention. Deswegen wird es höchstens dann funktionieren, wenn DLL und EXE vom selben Compiler mit derselben Compilerversion stammen. Und selbst das ist nicht garantiert.



  • Ups, gut, dass du mir das sagst, ich wäre voll reingerannt. 😞



  • Hi,

    //by value - wir wollen ja kein undefiniertes verhalten.

    Es ist auch so nicht undefiniert, weil SelectMainLoop eine konstante Referenz zurückliefert, die durch den Aufbau ebenfalls gültig ist.

    std::string cMsg = m_psClient->SelectMainLoop();

    naja, SelectMainLoop kann auch Exceptions werfen und wo sollen die dann bitte aufgefangen werden?
    Im Übrigen:

    int main()
    {
    try
    {
    Main main;
    main.Execute();
    }
    catch(...)
    {
    ...
    }
    return 0;
    }
    

    Was bringt hier das catch? Was soll das außer Fehlerausgabe und Wertrückgabe denn bitte tun?

    Ich verstehe in der Tat überhaupt nicht, wann und wo es logisch ist Exceptions einzusetzen, wer sie behandeln sollte, inwiefern etc. anscheinend habe ich völlig falsche Vorstellung davon.
    Kennt jemand dazu gutes Material (kein Buch)?

    Danke jedenfalls schon mal,

    MfG Eisflamme



  • Mis2com schrieb:

    //by value - wir wollen ja kein undefiniertes verhalten.

    Es ist auch so nicht undefiniert, weil SelectMainLoop eine konstante Referenz zurückliefert, die durch den Aufbau ebenfalls gültig ist.

    Mag ja sein, aber du kopierst den Inhalt ja in die lokale variable cMsg - und gibst dann eine referenz darauf zurueck.

    [quote[naja, SelectMainLoop kann auch Exceptions werfen und wo sollen die dann bitte aufgefangen werden?[/quote]
    Was wirft sie denn fuer exception?
    Irgendwas wo du sinnvoll reagieren kannst?

    Was bringt hier das catch? Was soll das außer Fehlerausgabe und Wertrückgabe denn bitte tun?

    Dem User eine Nachricht ausgeben:
    "Es tut uns Leid aber wir haben voll Mist gebaut."
    und dann das programm beenden.
    bzw. anbieten das programm neu zustarten, oder im debug modus neu starten oder fehlerbericht drucken. ka, irgendwas halt.

    Das ist besser als wenn es einfach abstuertzt und eine standard meldung "unhandled exception bla bla" kommt.

    Ich verstehe in der Tat überhaupt nicht, wann und wo es logisch ist Exceptions einzusetzen, wer sie behandeln sollte, inwiefern etc. anscheinend habe ich völlig falsche Vorstellung davon.
    Kennt jemand dazu gutes Material (kein Buch)?

    Exceptional C++ hat mir sehr geholfen, wobei das eher nicht auf wann werfe ich exception eingeht, sondern exception sicherheit. ka ob dir das was bringt.

    uU fuehlst du dich aber in java wohler - dort werden exception so aehnlich behandelt wie du es tust.

    im prinzip gilt aber:
    sowenig try-catch bloecke wie moeglich (aber natuerlich nicht zu wenige).
    meistens erledigt RAII die ganze arbeit, ich habe deshalb auch quasi nie try catch bloecke...

    eine exception ist naemlich eine ausnahme - etwas das im prinzip nicht vorkommt wenn alles halbwegs ordentlich laeuft.



  • Hallo,

    uU fuehlst du dich aber in java wohler - dort werden exception so aehnlich behandelt wie du es tust.

    Nein, ich will nicht Java programmieren, die Sprache ziehe ich in Zukunft zwar auch in Betracht, aber das, was ich mache, gefällt mir selber nicht besonders gut, ich bin nur etwas hin- und hergerissen und weiß nicht, wie ich es tun soll...

    Dem User eine Nachricht ausgeben:
    "Es tut uns Leid aber wir haben voll Mist gebaut."
    und dann das programm beenden.
    bzw. anbieten das programm neu zustarten, oder im debug modus neu starten oder fehlerbericht drucken. ka, irgendwas halt.

    Das ist besser als wenn es einfach abstuertzt und eine standard meldung "unhandled exception bla bla" kommt.

    Gut, ich dachte erst, dass catch in der main() wirklich etwas behandeln würde, also nicht nur Ausgabe, so ist mir das dann auch klar.

    naja, SelectMainLoop kann auch Exceptions werfen und wo sollen die dann bitte aufgefangen werden?

    Was wirft sie denn fuer exception?
    Irgendwas wo du sinnvoll reagieren kannst?

    Ja, wenn der Server die Verbindung z.B. trennt o.Ä.
    Oder wie sollte ich darauf reagieren?
    Hier mal der weniger gute Code von SocketClnt:

    const std::string& SocketClnt::SelectMainLoop() 
    {
    	if(!m_bValid)
    		throw Exception::SocketError("Kein valides Socket.", Exception::RS_ERROR);
            // Falls kein Buffer vorhanden -> erstellen
    	if(!m_cstrBuffer)
    		m_cstrBuffer = new char[NBUFFERSIZE];
    
    	// Nachrichtenliste leeren
    	m_strRecv = "";
    
    	// fdset leeren
    	FD_ZERO(&m_fdsRead);
    	// Server fdset hinzufügen
    	FD_SET(m_sSocket, &m_fdsRead);
    
    	// Timeout zurücksetzen
    	m_tvTimeout.tv_sec = m_tvTimeout.tv_usec = 0;
    	// Schauen, ob was empfangen wurde
    	if((m_nRc = select(0, &m_fdsRead, 0, 0, &m_tvTimeout) > 0))
    	{
    		// Daten empfangen
    		m_nRc = recv(m_sSocket, m_cstrBuffer, NBUFFERSIZE-1, 0);
    
    		// Bei m_nRc == 0 hat der Server offensichtlich die Verbindung unterbrochen
    		if(m_nRc == 0)
    			throw Exception::SocketError("Der Server hat die Verbindung unterbrochen.", Exception::RS_ERROR);
    		// Wenn beim Empfangen der nachricht ein Fehler stattfindet, so kann man damit rechnen, dass die Verbindung
    		// nicht mehr valide ist
    		if(m_nRc == SOCKET_ERROR)
    		{
    			int m = WSAEFAULT;
    			int n = WSAGetLastError();
    			throw Exception::SocketError("Empfangen der Nachricht fehlgeschlagen.", Exception::RS_ERROR);
    		}
    		// Für die Nachrichtenauflistung mit Delimiter \n ist es hier sinnvoller, \n direkt zu setzen
    		m_cstrBuffer[m_nRc] = '\0';
    
    		m_strRecv += m_cstrBuffer;
    		// Nachricht hinzufügen
    	}//WSAEINVAL
    	// Empfangene Strings zurückliefern
    	return m_strRecv;
    }
    

    Ich werde das Konzept für die Sockets noch einmal überarbeiten, jedenfalls bin ich davon ausgegangen, dass ein nicht-verbundenes Socket auch keine Daseinsberechtigung hat und daher nicht valide ist. Ich hörte hier im Forum doch auch einmal davon, dass Methoden wie open() oder close() für basic_fstream kein gutes Design wären, daher übertrug ich dies auf Sockets, schließlich macht ein nicht-verbudenes Socket genauso wenig Sinn wie ein fstream ohne Dateiverbindung.
    Das mit dem Buffer sollte auch klar sein, der wird nur ein Einziges mal allokiert.
    Und ich hielt es eigentlich für eine gute Idee, das mit Exceptions zu machen, wenn getrennt wird...

    Also Exceptions sind Ausnahmen, klar, nur inwiefern?
    Anscheinend ist die Trennung vom Server, die am Ende jedes Programmes stattfinden sollte, keine Ausnahme, sondern praktisch die Regel...
    Mir ist klar, dass Exceptions nicht zur Programmsteuerung dienen sollten, in Java scheint dies genau anders zu sein?

    Jedenfalls will ich nicht die Sprache wechseln, sondern hier mal besser werden, danke vielmals. 🙂

    MfG Eisflamme



  • Mis2com schrieb:

    Ja, wenn der Server die Verbindung z.B. trennt o.Ä.
    Oder wie sollte ich darauf reagieren?

    Wie willst du denn darauf reagieren? Am besten garnicht. Lass den user entscheiden was er will.
    Normalerweise werde ich mir einen anderen server suchen, wenn mir dieser einen socket error liefert.
    vielleicht will ich es aber auch in 1 minute nochmal probieren...

    die socket klasse kann das nicht wissen. sie meldet den fehler und wartet ab.

    Ich werde das Konzept für die Sockets noch einmal überarbeiten, jedenfalls bin ich davon ausgegangen, dass ein nicht-verbundenes Socket auch keine Daseinsberechtigung hat und daher nicht valide ist.

    Sehe ich genauso.

    Ich hörte hier im Forum doch auch einmal davon, dass Methoden wie open() oder close() für basic_fstream kein gutes Design wären, daher übertrug ich dies auf Sockets, schließlich macht ein nicht-verbudenes Socket genauso wenig Sinn wie ein fstream ohne Dateiverbindung.

    ]
    exakt.

    bedenke aber: was passiert wenn ein lesefehler bei fstream auftritt? (OK, angenommen man setzt mit setexception fuer failbit eine exception) - es fliegt dir eine exception um die ohren. die datei ist aber weiterhin 'offen' - auch wenn jede lese oder schreiboperation vermutlich auch fehlschlagen wird.

    Mir ist klar, dass Exceptions nicht zur Programmsteuerung dienen sollten, in Java scheint dies genau anders zu sein?

    C++ arbeitet _sehr_ sparsam mit exception. zB ausser .at() wirft keine methode der STL defaultmaessig.

    das ist bei streams zwar doof, aber ist halt so 😞

    ich sehe es in etwa so: sobald eine exception in einem normalen durchlauf fliegen kann, dann ist es keine ausnahme.

    zB darf EOF beim lesen aus einer datei keine exception werfen. wenn es aber lesefehler gibt, oder gar die datei nicht gefunden wird - dann schon. denn dann ist etwas unvorhergesehenes passiert.

    bei sockets ist es aehnlich. der socket liest und liest, bis die verbindung beendet wird - soweit alles klar. allerdings muesste eine exception fliegen, wenn ich vom geschlossenen socket lesen will. das kann man natuerlcih auch immer etwas anders sehen - also zwing dich nicht dazu alles genauso zu sehen wie ich.



  • Hi,

    gut, genauso wie du muss ich es nicht sehen, aber so oder so... Mein Konzept ist momentan einfach nicht tragbar, soviele catch-Anweisungen... das finde ich ja selbst nicht schön.
    Aber z.B. kommt es auch vor, dass ein Socket die Verbindung verliert, weil der Provider mal kurz die Verbindung trennt und wiederverbindet (kommt vor, bei T-Online z.B.). Dies ist nicht sehr schlimm, aber ärgerlich, wenn keine automatische Wiederverbindung stattfindet.
    Und da es sich hierbei um eine Ausnahme handelt, finde ich zwei Exceptions doch schon angemessen.
    Allerdings, soll die nächsthöhere Instanz - hier GetAndProcessMessages - diese Ausnahme einfach ignorieren? In deinem Fall hättest du kein einziges catch darin und die Application-Klasse (also ich werde es demnächst wohl auch so mit Application machen)... sollte die überhaupt was davon mitbekommen?
    Nagut, Serveruser stellt eine Klasse dar, die einen Manager der Nachrichten und Antworten zu einem Server vertritt. Allerdings beinhaltet ServerUser auch eine Verbindung zum Server, jetzt gibt es 2 Möglichkeiten:

    1. ServerUser selbst stellt so eine Verbindung wieder her
    2. Application tut das über öffentliche Methoden von ServerUser

    Ich mag mich nicht so recht entscheiden, ServerUser würde naheliegen, weil es bei mehreren Servern lästig wird, wenn dem Fehler ein Zeiger auf die Klasse, die wiederverbunden werden soll mitgeliefert wird, obwohl das Serveruser doch eigentlich alleine könnte.
    Oder soll ich ServerUser eine Art Main-Methode geben, sodass dort GetAndProcessMessages aufgerufen wird, oder die Nachrichten verwaltet werden?

    Nochmals vielen Dank,

    MfG Eisflamme



  • wenn die connection verloren wurde->exception innerhalb werfen und abarbeiten, wenn das nich klappt->exception nach aussen.
    alternativ bool wert throwException. wenn true ->direkt exception nach aussen werfen, wenn false innerhalb abarbeiten



  • Äh, das widerspricht zu ziemlich dem, was Shade gerade sagte, weil eine verlorene Verbindung keine Ausnahme ist, da am Ende immer getrennt wird und bei dem Port 80 z.B. auch immer sofort eine Trennung erfolgt.
    In dem Sinne ist eine Exception hier unangebracht, weil sie dann zur Programmsteuerung dienen würde, wo kein Ausnahmefall vorliegt. oO

    Aber gut...



  • Woher weiss man warum die Verbindung geschlossen wurde? das kann man nur wissen, wenn man das verwendete Protokoll kennt - (da man sonst ja nicht weiss, ob dieses Ende der Verbindung beabsichtigt war oder nicht)

    Dieses Problem hat der Socket. Er weiss, die Verbindung ist zuende - warum weiss er allerdings nicht.
    Der Anwender der Klasse kann es aber sehr wohl wissen. Naemlich dann wenn noch Daten erwartet werden, aber der Socket zu ist - sprich, wenn ich von einem geschlossenen Socket lesen will.

    Ich sehe sockets als streams an, deshalb mal folgender pseudo code:

    while(getline(socket, str))
    {
      cout<<str;
    }
    

    Jetzt koennte man SocketCLOSE als eine Art EOF interpretieren. Dann waere obriger Code korrekt.

    Irgendwie gefaellt mir der obrige Code. Das lesen von einem Socket sollte sich wirklich nicht von dem lesen aus einer datei unterscheiden - denn genau das ist es ja in vielen faellen auch 🙂

    eine exception koennte dann fliegen, wenn wir folgenden code verwenden wollen wuerden:

    while(getline(socket, str))
    {
      cout<<str;
    }
    socket.get(c); //PENG denn socket ist bereits zu
    

    Wenn es dann noch eine reconnect() methode gibt, hat der Anwender alle moeglichkeiten richtig mit dem socket umzugehen.

    der socket alleine ist dumm - erst das darueberliegende protokoll macht ihn klug 🙂



  • OK, ich verstehe...

    Deine Idee ist wirklich interessant, was muss ich für die Verwendung von getline machen?

    > und << sind zu überladen, soll ich erben lassen?

    MfG Eisflamme



  • Mis2com schrieb:

    Deine Idee ist wirklich interessant, was muss ich für die Verwendung von getline machen?

    > und << sind zu überladen, soll ich erben lassen?

    Einfach einen eigenen stream implementieren.
    Das geht ueber einen streambuffer und uber das erben von ostream bzw. istream

    letztens gab es dazu eh eine frage hier. ich habe da gleich Josuttis fdstream zum abschauen empfohlen 🙂

    ansonsten schau dir mal an wie es socket++ macht - wird eh zeit dass boost sockets bekommt.



  • und worum handelt es sich bei so einem Streambuf?



  • Mis2com schrieb:

    und worum handelt es sich bei so einem Streambuf?

    Der macht im Prinzip die ganze Arbeit.

    Er cacht die Daten, schreibt und bzw. oder liest.

    Ein stream ist mittels eines streambuffers implementiert



  • hab mir mal den basic_streambuf der stream klassen angeschaut, sieht sehr interessant aus...
    aber mal ne frage:
    auf meinem weg durch den buffer bin ich auf FILE objekt gestoßen,naja was heisst gestoßen, ich habs schon einige male benutzt, hab mich aber bisher nie dafür intressiert wies funktioniert,kann es sein, dass er ind er struktur definierte pointer *buffer auf einen speicherbereich auf der festplatte zeigt? oder zeigt er auf was anderes?


Anmelden zum Antworten