Wie Programm verlassen, wenn kein Parameter zurückgegeben werden darf?
-
Plotzenhotz schrieb:
Nein, ich schlage NICHT vor ein Programm abzubrechen wenn ein Plugin nicht geladen werden konnte. "kann nicht geladen werden" ist (egal ob Plugin/Datenfile/...) ein Fall der auch bei einem 100% Fehlerfreien Programm auftreten kann. Siehe mein Posting weiter oben wo ich das alles erklärt habe und du es (vermutlich) nicht gelesen

ich habe es gelesen, aber du hast meins nicht genau gelesen:
mit terminate haust du alle daten des users weg. einfach bumm weg. stundenlange arbeit fuer die katz. und das nur weil ein feature nicht funktioniert.
oder wie entscheidest du was ein fataler fehler ist? Beispiel die rechtschreibe pruefung schmiert ab. da ist was kaputt. bei der text verarbeitung gibts ne array ueberschreitung.
was machen? die software ist definitiv kaputt. das darf einfach nicht passieren. aber die anwendung abschiessen deswegen?
oder andere sachen: zB ein server und ein client sendet ne kaputt anfrage und der server ist buggy -> peng, error. sofort alle anderen connections killen?
-
@Shade Of Mine:
Ich habe deines auch genau gelesen, bloss das Beispiel was du brachtest passt überhaupt nicht zu dem was ich geschrieben hab. Was soll ich antworten wenn du von Laufzeitfehlern schreibst und ich von Programmierfehlern. Aber egal.
Man kann Argumente für und gegen beide Varianten finden. Wer Wert auf Verfügbarkeit legt, der wird eher ne Exception werfen. Wer Wert auf Sicherheit legt wird eher das Programm abschiessen. Wer Wert auf Sicherheit und Verfügbarkeit legt sollte nach Möglichkeit einen Prozess pro Client verwenden, und den dann natürlich auch abschiessen wenn was passiert ist.
Vonwegen verlorene Daten im Fall "Server"... blubb? Was soll das für ein Server sein der erstmal lange mit dem Client rumshakert und ihm "ok, habs gespeichert" signalisiert etc. - so dass der Client die Daten auf seiner Seite vernichtet/als übertragen markiert, und in wirklichkeit dann aber Probleme bekommt wenn er abschmiert? Ich habe selbst schon Client/Server Teile programmiert, und die kommen wunderbar damit klar wenn du den Server und/oder Client zu einem beliebigen Zeitpunkt killst.
Vonwegen verlorener Daten im Fall "Applikation" - ja, ok, Stromausfall? Kind/Ehefrau/Hund/man selbst stolpert übers Netzkabel? Automatische periodische Backups sind in dem Fall angesagt. Funktioniert bei vielen Programmen wunderbar. Eine "gute" Applikation sollte auch nach Möglichkeit nie etwas machen wobei es schlimm wäre wenn sie mitten drin abgeschossen wird, also z.B. diverse Config/Option Files umschreiben ohne Copy-Modify-Rename ist z.B. pfui.
Ob man eine Anwendung wegen eines solchen Fehlers abschiesst hängt wieder davon ab wie schlimm es wäre wenn das Programm ein falsches Ergebnis produziert. Wenn die ganze Tragik darin liegt dass der User auf einmal bunte Sonderzeichen am Schirm hat statt seinem C++ Programm an dem er gerade arbeitet (um ein Beispiel aus dem Leben zu greifen - die "Anwendung" ist in dem Fall Visual Studio 2005), dann ist das nicht so schlimm. Wenns aber (um beim Thema Programmier-Tools zu bleiben) um ein Source-Control System (ok, da wären wir wieder bei nem Server) geht dann würde ich ohne nachzudenken immer die "terminate()" Variante wählen. Die möglichen Auswirkungen wenn der Server mit falschen/kaputten Daten weiterläuft (und vielleicht nichtmal wer draufkommt) wären fatal.
Guck dir mal das "Buffer Security Check" Feature vom MSVC 8 an, das macht im Prinzip genau dasselbe. Das schiesst dir nämlich auch einfach deinen ganzen Prozess unterm Hintern weg wenn eine "vermutlich überschriebene Rücksprungadresse" erkannt wurde.
Was man dagegen in Libraries machen sollte ist eine schwierige Frage, da es ja keine brauchbare "Error" Klasse in C++ gibt...
Leider hat es sich eingebürgert in C++ von std::exception abzuleiten anstelle von std::runtime_error
Kannst du das belegen?
Guck dir Open Source Projekte an. z.B. die ansonsten so coole Boost.
class xml_unescape_exception : public std::exception //... class BOOST_FILESYSTEM_DECL filesystem_error : public std::exception //... class base64_exception : public std::exceptionDas sind alles Exceptions die als Folge eines reinen runtime errors fliegen können, z.B. weil ein File kaputt ist o.ä.
Vom User z.B. als Precondition zu verlangen er muss erstmal selbst prüfen ob ein BASE64 String überhaupt gültig ist macht ja wohl kaum einen Sinn - dann kann er den gleich selbst decoden.Ein anderes Beispiel:
namespace std { class bad_alloc : public exception { //... }; }Muahaha...
-
Wenn sogar bad_alloc und boost das selbst machen, müssen runtime_error und logic_error wohl irgendein Problem haben. Wohl dass sie von string abhängen. In dem Fall kann ich es voll und ganz verstehen dass man diese Klassen meidet (auch wenn ich immer brav von runtime_error abgelitten hab
)
-
Plotzenhotz: IMO vergisst du, dass Ströme in C++ gebuffert werden. Deshalb können sehr wohl Daten verloren gehen, wenn keine Destruktoren aufgerufen werden.
-
Wieso sollte ich das vergessen? Nein, das vergesse ich nicht. Es ist bloss nicht wichtig. Streams wird wohl hoffentlich niemand für wichtige Daten verwenden. Das wäre ja noch schöner. Und dann ist da immernoch std::flush...
-
@Plotzenhotz:
Aeh, du speicherst daten immer total redundant und zu jederzeit sind mindestens 2 kopien der datensaetze im system sicher verstaut?
andernfalls klappt dein abschiessen nicht. weil der client und der server ja beide sterben koennen. und da tust du wirklich immer alles sofort syncen und den ganzen mist? und die daten auch redundant halten? komplett die ganze laufzeit ueber?
ich nicht. das ist ein enormer aufwand.
ich kann ein programm mit einem fehler dennoch sauber beenden - zumindest so sauber wie moeglich. einfach alle daten wegzuschmeissen ist ne miese idee.
destruktoren die handles noch offen haben, zB shared memory und aehnliches - da kann nicht immer alles geschlossen werden. log files werden nicht fertig geschrieben, etc.
ein terminate() ist nur dann sinnvoll wenn man an einem punkt steht wo nichts mehr geht und zwar garnichts. und das kommt nie vor. also mir ist es noch nie passiert. es gibt immer sachen die man tun kann.
was enorm wichtig ist, die daten zu sichern.
klar kommen auch mal abschuesse vor wenn es nen hardware reset gibt und dann ist es wichtig dass die daten in einem konsistenten zustand sind. aber hier kann man auch repairs laufen lassen beim naechsten start der anwendung.
stell dir mal nen db server vor der ab und zu sich einfach beendet. und dabei zerschiesst er jedesmal die indizes, so dass sie neu aufgebaut werden muessen. wie willst du je erkennen wo das problem liegt?
wenn du vernuenftiges error handling machst, dann kannst du den server nach nem fatalen error sauber runterfahren. teile der anwendung sind nicht betroffen - quasi kein fehler betrifft die _komplette_ anwendung. eine anwendung besteht immer aus einzelnen modulen die interagieren. und so ein modul kann mal defekt sein. das mag sich in andere module uebertragen weil die von korrektem funktionieren der anderen module ausgehen, aber du wirst quasi nie die komplette anwendung schrotten.
und wenn nun zB ein query an den server geschickt wird und die verarbeitung des queries schlaegt fehlt - und zwar nicht im sinne "parse error" sondern es macht echt bumm und es wird schrott geliefert. die storage engine bekommt jetzt diesen schrott query und schmiert darauf hin ab.
muss ich jetzt den kompletten db server abschiessen? sofort. ohne fehler meldung, ohne backtrace und aehnlichem? ohne infos woran es gelegen haben koennte?
oder sollte ich nicht lieber einen sauberen shutdown machen und sagen "ok leute, irgendwas passt mit der query verarbeitung in der storage engine nicht. die letzten x queries waren: blablabla, das problem trat bei foo in bar nach baz auf..."
weiters beende ich die daten connetcions zu allen clients sauber und riskiere nicht dass diese irgendwelche daten verlieren weil sie mir das query schicken und nachher selber vielleicht die daten wegschmeissen.
ich wuerde echt mal gerne wissen wie du das deinem boss erzaehlst, dass die Abteilung XY die arbeit der letzten 5 stunden verloren hat weil sie keine backups gemacht haben und dann ein fehler auftrat als jemand blaue schrift auf gelbem hintergrund machen wollte (weil da n dummer bug war der die anwendung abgeschossen hat).
-
Aeh, du speicherst daten immer total redundant und zu jederzeit sind mindestens 2 kopien der datensaetze im system sicher verstaut?
Ich stecke Daten in eine Datenbank. Nachdem ich sie committed habe sind sie für mich "safe". Wenn der DB-Server nicht sicher genug ist (z.B. nicht geclustert) liegt das ausserhalb der Verantwortlichkeit meines Programms - lässt sich aber leicht beheben indem man einen einfachen Server in einen Cluster verwandelt. Kostet viel Geld, aber wer so krasse Ansprüche stellt muss das eben bezahlen. Macht aber bloss ein Problem wenn der DB-Server selbst ein Problem hat, was sehr selten vorkommt.
Wenn ich mit "nicht so wichtigen" Daten arbeite spare ich mir natürlich zu gunsten der kürzeren Entwicklungszeit gewisse Dinge, sollte auch klar sein. Ob die Änderung der Schriftfarbe bei einem Terminal jetzt gespeicher wird oder nicht ist im Prinzip egal - wichtig ist bei dem Beispiel bloss dass durch einen Fehler/Abbruch nicht die kompletten Einstellungen für einen User verloren gehen (schlimm)/kaputt gehen (noch schlimmer).
andernfalls klappt dein abschiessen nicht.
Genau. Also klappen schon, aber es wäre nicht gut.
weil der client und der server ja beide sterben koennen. und da tust du wirklich immer alles sofort syncen und den ganzen mist? und die daten auch redundant halten? komplett die ganze laufzeit ueber?
Pseudocode:
-- Client: while(x->haveNewData()) { dataBlock = x->getNextNewDataBlock() socket->sendDataBlock(dataBlock) dataSafe = socket->recvDataSafe() if(dataSafe.dataId != dataBlock.getId()) write_log(...), terminate() else if(!dataSafe.isSafe) write_log(...), whatever() else x->deleteDataBlock(dataBlock.getId()) } -- Server: dataBlock = socket->recvDataBlock() db_connection->begin() if(!db_connection->haveData(dataBlock.getId())) db_connection->storeData(dataBlock) else // haben wir schon, können wir ignorieren db_connection->commit() socket->sendDataSafe(true, dataBlock.getId())Was daran SO viel Aufwand ist weiss ich nicht.
(...)
stell dir mal nen db server vor der ab und zu sich einfach beendet. und dabei zerschiesst er jedesmal die indizes, so dass sie neu aufgebaut werden muessen.Ja, und genau da scheinst du nicht zu verstehen worauf ich hinaus will.
- Ein DB Server der bei einem Powerloss irgendetwas zerschiesst was nicht zu reparieren wäre ist ein Haufen Schrott und kein DB Server. Und wenn er doch zu jedem Zeitpunkt durch einen Powerloss unterbrochen werden kann/darf ohne dass etwas passiert, dann kann er sich auch jederzeit mittels "no-cleanup-terminate" selbst verabschieden ohne dass dabei etwas passiert. Ist doch nachvollziehbar, oder?
- Ein DB Server der evtl. Tabellen zerschiesst oder statt einem Rollback ein Commit macht, oder einen der unzähligen anderen möglichen Fälle die "böse" wären, bloss weil irgendein Programmierfehler mit einem "versuche sauber niederzufahren" behandelt wurde anstelle eines "no-cleanup-terminate" ist ebenso ein Haufen Schrott und kein DB-Server. Du schreibst dann weiter von Modulen, und nicht alles auf einmap kaputt sein etc. - das ist schon wahr, bloss woher willst du wissen WAS noch OK ist und was nicht?
wie willst du je erkennen wo das problem liegt?
Indem ich das "oops.txt" File (oder wie du es auch nennen willst) checke welches der DB-Server netterweise geschrieben hat bevor er sich verabschiedete. Eine "schreibe jenes in File x.txt, flushe und schliesse das File und kille dann den Prozess" Funktion lässt sich mit vertretbarem Aufwand so implementieren dass sie funktioniert, egal wie kaputt der State des Programms schon ist. Solange du die paar nötigen Parameter noch auf den Stack bekommst, dir nicht in der Zwischenzeit ein wildgewordener Thread diese überschreibt, und die Funktion ihr HANDLE (Kernelmode Objekt und daher sicher) noch vom OS bekommt kann da eigentlich nix schief gehen.
Und wenn der Prozess schon SO kaputt ist dass selbst das nichtmehr geht (z.B. Stack voll bis oben hin/keine HANDLEs mehr verfügbar/...), dann ist es auch schon egal, dann rettet dich sowieso nixmehr.und wenn nun zB ein query an den server geschickt wird und die verarbeitung des queries schlaegt fehlt - und zwar nicht im sinne "parse error" sondern es macht echt bumm und es wird schrott geliefert. die storage engine bekommt jetzt diesen schrott query und schmiert darauf hin ab.
muss ich jetzt den kompletten db server abschiessen? sofort.
Wenn die storage engine im gleichen Prozess läuft wie der Teil wo sie ihren Input herbekommt, dann ja. Wenn die storage engine ein eigener Prozess ist dann nein. In dem Fall meldest du einen Fehler (z.B. Syntaxfehler/...( an den anderen Prozess, und dieser soll sich selbst darauf abschiessen, da er "weiss" dass er niemals queries produziert die einen Syntaxfehler auslösen könnten.
sofort. ohne fehler meldung, ohne backtrace und aehnlichem? ohne infos woran es gelegen haben koennte?
oder sollte ich nicht lieber einen sauberen shutdown machen und sagen "ok leute, irgendwas passt mit der query verarbeitung in der storage engine nicht. die letzten x queries waren: blablabla, das problem trat bei foo in bar nach baz auf..."
Ich gehe nun mal davon aus du meinst den Fall storage engine und das defekte Modul leben im selben Prozess (siehe oben).
Ich habe schon geschrieben dass es Sinn machen kann a) den terminate Handler zu verbiegen und b) diverse Infos zu loggen bevor man dem Prozess den Todesstoss versetzt. Das ist *eine* Sache. Eine *andere* Sache ist es sich darauf zu verlassen dass das komplette Stack-Unwinding noch gut geht. Das ist IMGO bei einem wirklich wichtigen Projekt nicht nur ziemlich mutig sondern eher fahrlässig.Wenn alle Programmteile/Module/Prozesse/... sauber zusammenarbeiten gehen dabei (=beim Prozess abschiessen) auch keine Daten verloren. Da sie eben noch nirgends gelöscht sind wo sie "entstanden" sind/zwischengespeichert wurden aber noch nicht "safe" weitergegeben wurden. Wenn sie "safe" weitergegeben wurden dann hat sie eben ein anderes Modul.
weiters beende ich die daten connetcions zu allen clients sauber und riskiere nicht dass diese irgendwelche daten verlieren weil sie mir das query schicken und nachher selber vielleicht die daten wegschmeissen.
Ein Client der Daten verliert weil er ein "connection reset by peer" bekommt anstelle der "is ok jetzt, habs weggesichert" Meldung vom Server ist ein Haufen Schrott und kein Client. Ganz einfach deswegen weil es SO oft dazu kommen kann dass irgendwo eine Netzwerkverbindung flöten geht.
ich wuerde echt mal gerne wissen wie du das deinem boss erzaehlst, dass die Abteilung XY die arbeit der letzten 5 stunden verloren hat weil sie keine backups gemacht haben und dann ein fehler auftrat als jemand blaue schrift auf gelbem hintergrund machen wollte (weil da n dummer bug war der die anwendung abgeschossen hat).
Siehe oben. Wenn die Daten wichtig sind gehen sie nicht verloren. Wenn doch hat ein Programmierer scheiss gebaut oder sich nicht an die Vorgaben gehalten - das selbe Problem kann man aber immer haben, ganz unabhängig davon ob man sich nun an "terminate()" oder "throw" hält.
---
Ein neuer Grund gegen "throw" (für "solche Fehler" wo wir uns ja nunmehr einig sind was "solche Fehler" bedeutet): alles was man werfen kann kann man auch fangen (catch(...)). Das kombiniert mit der Tatsache dass z.B. in vielen Libs an diversen Stellen "catch(...) {}" Blöcke vorkommen ... ist nicht so gut wenn ich was werfen will was bis nach main() durchgeht und dort dann zu exit(99) o.Ä. führt.
Noch ein Grund: Multithreading. Wie teile ich allen Threads mit sie mögen bitte "throw critical_error();" machen? Shared state ist nunmal shared state, und der kann genauso kaputt sein - was dann alle Threads betrifft, womit wir schon die Notwendigkeit haben alle zu beenden.
-
Plotzenhotz schrieb:
Indem ich das "oops.txt" File (oder wie du es auch nennen willst) checke
leider nicht moeglich da vernuenftige infos rein zu schreiben, weil du im terminate handler kein stack unwinding hinter dir hast - du hast also 0 ahnung vom kontext wo der fehler aufgetreten ist.
waehrend ich mit meiner exception ne bessere funktionalitaet habe: weil eben mehr infos zur verfuegung stehen um ein logfile zu schreiben. weiters werden destruktoren verlaesslich aufgerufen und ich kann entscheiden ob der fehler fatal genug war um einen sofortigen shutdown zu rechtfertigen.
sachen die ich mit terminate nicht kann.
terminate bringt mir keine zusaetzlichen features die mir eine exception vorenthaelt.
du schreibst korrekterweise: woher weiss man was alles kaputt ging: tja, vielleicht weiss ich es ja. vielleicht ist es eine grosse anwendung die sachen macht die unabhaengig voneinander sind. beispiel: webserver. wenn das modul zur verarbeitung von PHP code abschmiert, dann ist es nicht zwangslaeufig noetig auch die perl und python module abzuschalten. uU aber doch. ich entscheide das einfach spaeter und bleibe flexibel und kann mich immer umentscheiden. vielleicht will ich alle dynamischen sachen abdrehen und nur noch statische seiten liefern wenn ein fehler aufgetreten ist.
vielleicht habe ich aber ein gutes sandbox model und kann es mir leisten einfach das kaputte modul neu zu initialisieren und den vorfall nur zu loggen.
alles moeglichkeiten die ich mit terminate nicht habe.
ein terminate schiesst mir den ganzen server ab - egal ob es das ist was ich will. das eine modul kann nie soviel ueber die komplette anwendung wissen um so eine entscheidung faellen zu koennen.nicht alle modul-fatelen fehler sind anwendungs-fatal.
wie gesagt: hintergrundfarbe in einem word dokument aendern: das modul "ColorChooser" schmiert ab. Wilder zeiger irgendwo. muss ich jetzt wirklich das dokument killen? oder soll ich nicht lieber vorher noch schnell einen versuch starten es zu retten?du bist mit terminate auf ein "bumm schluss aus" festgelegt. mit exception kannst du genau so ein "bumm schluss aus" machen, wenn es sinn macht - aber gleichzeitig kannst du auch den fehler korrigieren wenn das mehr sinn machen sollte. du hast mehr infos ueber den kontext des fehlers. und es gibt resourcen die dein OS dir nicht schliessen kann - die du uU schon freigeben solltest.
es ist einfach so: exception bieten _mir_ die moeglich zu entscheiden ob ich _meine_ anwendung abschiesse. Waehrend ein terminate mir diese entscheidung weg nimmt und ich mich ploetzlich auf diese 3rd party code verlassen muss das die entwickler genug ahnung von meiner anwendung haben um die richtigen entscheidungen zu treffen.
finde ich sehr fragwuerdig.
PS:
was du von den redundanten daten gesagt hast: du brauchst fuer sowas transaktionen wenn du die komplexen daten auf dem server stabil halten willst.
ein gaengiges transaktionsmodell fuer c++ basiert auf RAII -> und das verlangt dass der dtor aufgerufen wird
dh, du hast inkonsistente daten nach dem terminate. moeglich diese zu reparieren, aber wenn ne exception fliegt ist das reparieren nicht mehr noetig - weil es n automatisches rollback der transaktion gibt.
-
@Shade Of Mine:
Ja, ich weiss nicht was ich noch alles schreiben soll. Ist eigentlich so einfach und ich habs jetzt mehrfach erklärt. Du schreibst selbst "Wilder zeiger irgendwo." - was willst du mehr? Wieso gehst du davon aus dass mit diesem wilden Zeiger nicht einfach *irgendwas* überschrieben wurde, zum Beispiel lebenswichtige Daten für die Module die du noch für "gut" hältst. Oder "das Dokument". Eine 0 an die erste Stelle und du speicherst ein leeres Dokument. Toll. Grosses Kino. Viel Spass mit deinen Exceptions.
-
was du von den redundanten daten gesagt hast: du brauchst fuer sowas transaktionen wenn du die komplexen daten auf dem server stabil halten willst.
ein gaengiges transaktionsmodell fuer c++ basiert auf RAII -> und das verlangt dass der dtor aufgerufen wird dh, du hast inkonsistente daten nach dem terminate. moeglich diese zu reparieren, aber wenn ne exception fliegt ist das reparieren nicht mehr noetig - weil es n automatisches rollback der transaktion gibt.Wo brauche ich für das Rollback bzw. das Transaction-Handling RAII oder einen noch lebenden Prozess? Nach dem terminate() hab ich nichtmal inkonsistente Daten wenn ich eine in-process Datenbank ala sqlite verwende, weil mir die DB brav ein Rollback macht wenn die connection unterbrochen wurde vor dem commit. Im Fall einer in-process DB ist das Teil des Recovery-Prozesses der wiederum teil des Initialisierungsvorganges ist. Wozu brauche ich also da nen Dtor oder ne Exception?
Mit einer "echten" DB schon garnicht, weil mir die auch automatisch ein Rollback macht wenn die Connection unterbrochen wird - also z.B. durch "connection reset by peer" (Socket-Fehler halt).
-
was habt ihr nur gegen exceptions

mal'n einfaches beispiel... try { schritt1(); schritt2(); schritt3(); ... // noch'n haufen anderer komplizierter kram... ... } catch (...) { // hier weiss man dass die ganze aktion in die // hose gegangen ist und kann entsprechend reagieren. // wiederholen, daten retten, programm abbrechen, // was auch immer... } ...ohne exceptions bräuchte man viele if's, flags, gotos oder sonstwas um nach einem missglückten schritt abzubrechen...
-
Shade Of Mine schrieb:
leider nicht moeglich da vernuenftige infos rein zu schreiben, weil du im terminate handler kein stack unwinding hinter dir hast - du hast also 0 ahnung vom kontext wo der fehler aufgetreten ist.
waehrend ich mit meiner exception ne bessere funktionalitaet habe: weil eben mehr infos zur verfuegung stehen um ein logfile zu schreiben. weiters werden destruktoren verlaesslich aufgerufen und ich kann entscheiden ob der fehler fatal genug war um einen sofortigen shutdown zu rechtfertigen.
sachen die ich mit terminate nicht kann.
Im Gegenteil. Bei einer Exception hast du im Normalfall so gut wie null Kontext, es sei denn du kümmerst dich explizit darum (wer macht das?). Ein terminate ruft abort auf, das generiert dir, so die ulimits das zulassen unter Unix kostenlos einen Coredump, den du dann mit dem Debugger bis ins letzte Bit untersuchen kannst. Unter Windows würde ich vermuten dass man einstellen kann, dass direkt der Debugger an der fraglichen Stelle aufpoppt.
Ums mal auf den Punkt zu bringen: Es ist genau die Tatsache, dass terminate KEIN unwinding macht, dafür verantwortlich, dass der volle Kontext erhalten bleibt. Unwinding zerstört den Kontext, bürdet dir also als Programmierer auf, den Kontext manuell mit der Exception mitzuschicken.
Übrigens wird das korrekte Aufrufen irgendwelcher Destruktoren im Absturzfall IMHO für wichtiger gehalten als es ist. Die meisten Destruktoren machen eh nur was, das sowieso vom OS erledigt wird. Bei allem anderen ist es wichtiger, dass das Programm beim nächsten Start nicht über die Überreste des letzten Absturzes stolpert, die übrigens auch von einem Stromausfall stammen können.
-
sollen keine exceptions verwendet werden, bleibt für das problem nur noch das "unsanfte" beenden des ganzen programms. mit leeren dummys zu arbeiten ist meiner meinung nach nie sauber.
da die vorliegende funktion eh eine referenz zurückgibt, wäre es angemessener, die funktion zu verändern, auch wenn dies eigentlich nicht gewünscht ist.
template<class KeyType, class ValueType> bool DictionaryAsHashTable<KeyType, ValueType>::get(ValueType& ref) { if(currentNode != NULL) { ref = currentNode->value; // falls value schon ne referenz is return 1; } else return 0; }muss sich halt der aufrufende selbst um fehlerbereinigung handeln und der methodenentwickler is fein raus

-
Ums nochmal ganz klar zu machen (@net und andere): ich bin ein grosser Fan von Exceptions. Zur Behandlung von "Laufzeitfehlern". Ich bin aber auch der Meinung dass der korrekte Weg auf die Verletzung von preconditions oder invariants zu reagieren ist das Programm ohne stack unwinding zu beenden.
-
Plotzenhotz schrieb:
...
Vonwegen verlorene Daten im Fall "Server"... blubb? ...Falls das auf meinen Einwand hin gemeint war:
Ich habe schon Server für Clienten (über ganz Deutschland verteilte Hardwareterminals unterschiedlichster Hersteller mit sehr kleinen Toleranzen - keine Chance, auf deren Implementation selbst Einfluß zu nehmen) programmiert, bei denen man mir Körperteile entfernt hätte, wenn ich nicht alles versucht hätte, um den Server am Laufen zu halten.
Ja, es gibt auch Situationen, wo ein technischer Defekt vorliegt und man wirklich nicht mehr sinnvoll arbeiten kann (deswegen Herunterfahren des Prozesses und automatischer Restart) - aber auch dann war aus Verfügbarkeitsgründen der Versuch des Aufräumens (konkret: Aktives Abmelden vom Nameservice) immer noch vorzuziehen (die Reaktionszeiten des Nameservices auf ein "unabgemeldetes Abschmieren" waren sehr hoch).Und in allen anderen Fällen (99%) haben wir auf verschiedensten Ebenen Retries eingebaut (tw. auch Retrymechnismen der Clients genutzt).
Das Teil läuft nun seit 4,5 Jahren und unsere Kunden loben einstimmig die inhaltliche wie technische Zuverlässigkeit ....
und alles ohne terminate(). :pWie gesagt: So sehr ich Dir in der Theorie auch zustimmen mag ... in der Praxis kann kaum jemand entscheiden, ob der unpassende Parameter in seiner Funktion ein terminate() rechtfertigt - weswegen der Ansatz in der Praxis eher eine untergeordnete Rolle spielen dürfte. Vielleicht programmiere ich aber auch zu wenig für menschliche Nutzer, um den Ansatz würdigen zu können.
Plotzenhotz schrieb:
...Wenn der DB-Server nicht sicher genug ist (z.B. nicht geclustert) liegt das ausserhalb der Verantwortlichkeit meines Programms ...
Nicht, wenn Du den DB-Server programmierst.

Ich habe allerdings den Eindruck, dass Dein "Im Fehlerfall tot umfallen"-Konzept sich immer mehr dem annähert, was viele hier mit exceptions umsetzen:
- Programmabbruch nur in Spezialfällen
- Ressourcen aufräumen (allerdings vom OS und nicht im Dtor)
- Kontrolle nochmal wiederbekommen (statt im catch halt im eigenen terminate-Handler zum Fehlerinfos wegschreiben, ....).
Das Einzige, was Deinen Vorschlag noch von "unserem" unterscheidet, ist eigenlich nur, dass kein Stackunwinding mehr vor Programmende stattfindet.
..- und ich weiß nicht, ob das den heiligen Krieg rechtfertigt, der hier ausgebrochen ist.Gruß,
Simon2.
-
Falls das auf meinen Einwand hin gemeint war:
Nö, das war nicht an dich gerichtet.
Wie gesagt: So sehr ich Dir in der Theorie auch zustimmen mag ... in der Praxis kann kaum jemand entscheiden, ob der unpassende Parameter in seiner Funktion ein terminate() rechtfertigt - weswegen der Ansatz in der Praxis eher eine untergeordnete Rolle spielen dürfte.
Jau
Ich bin halt der Meinung ich sollte mir erstmal über die Theorie im Klaren sein, die Gefahren kennen, wissen was passieren kann, warum soetwas "grundlegendes" wie stack unwinding Schaden anrichten kann. Dann kann ich diverse Faktoren gegeneinander abwägen, und entscheiden was wichtiger ist, bzw. was sinnvoller ist. Und ich denke dass es Fälle gibt wo man alles ausschliessen muss was theoretisch irgendwie gefährlich sein könnte und das Programm lieber sofort beendet und neu startet, genauso wie es Fälle gibt wo man sinnvollerweise komplett auf Error-Checking/-Handling verzichtet. Das meiste wird sich natürlich irgendwo im Bereich dazwischen ansiedeln.Das Einzige, was Deinen Vorschlag noch von "unserem" unterscheidet, ist eigenlich nur, dass kein Stackunwinding mehr vor Programmende stattfindet.
Genau. Weil Stack-Unwinding gefährlich ist
*duck-und-renn*BTW (in Bezug auf ein früheres Posting von dir): dass eben kein stack unwinding passiert wenn eine Exception nirgends gefangen wird ist auch der Grund warum einige Leute immer wieder predigen: "catch(...) + rethrow" (als "finally" Ersatz) ist böse, Guards (die dann im Dtor cleanup durchführen) sind gut. Weil durch "catch(...) + rethrow" eben stack unwinding bis zu einem gewissen Punkt erzwungen wird, auch wenn die weitergeworfene Exception dann im Endeffekt keiner fängt. Was aber wieder ein anderes Thema ist.
Und jetzt geh ich pennen, gute Nacht

-
Plotzenhotz schrieb:
Vorweg ein paar Kleinigkeiten. Diverse Files (File-Handles meine ich), Speicher, sonstige Resourcen (Sockets, ...) werden normalerweise wenn ein Prozess terminiert vom OS aufgeräumt, darum braucht man sich keine Sorgen zu machen.
Du machst dir das viel zu einfach. Wir reden hier nicht von Windows oder Linux, die sowas evtl. machen. Hier geht es um ISO C++, und das gibt keinerlei Garantie dafür. Deshalb ist es grundsätzlich eine feine Sache, Destruktoren abarbeiten zu lassen. Allein das reicht schon aus, um Exceptions expliziten Aufrufen von Funktionen wie terminate, abort, exit oder was auch idR den Vorzug zu geben.
Plotzenhotz schrieb:
Weiters sollte man wissen dass das Stack-Unwinding nur dann passiert wenn die Exception auch irgendwo gefangen wird.
Ja, das kann aber keine Begründung sein. Wer eine Exception wirft, macht irgendwas falsch, wenn er nicht vorhat, sie zu fangen.
Plotzenhotz schrieb:
Wird sie nicht gefangen wird der Prozess einfach so terminiert - macht dann auch keinen Unterschied mehr.
Nö, einfach so nicht. Es wird terminate() aufgerufen.
Plotzenhotz schrieb:
Nu zu dem Grund warum ich meine dass es in Fällen wo man eine Precondition verletzt sieht schlauer ist das Programm einfach abzubrechen, und zwar ohne Stack-Unwinding. Einfache Antwort: eben genau weil dann kein Stack-Unwinding mehr passiert, also keine Destruktoren mehr laufen.
Destruktoren auszuführen hat nur dann einen Sinn wenn man davon ausgeht dass das Programm noch in einem gültigen Zustand ist.Aber genau deshalb wirft man doch Exceptions, weil das Programm sich zu diesem Zeitpunkt durch einen Fehler von einem definierten in einen undefinierten Zustand verändern kann. Ist das Programm vorher schon in einem undefinierten Zustand, dann ist das ein logischer Fehler des Programmierers und nicht der Verwendung von Exceptions zuzuschreiben.
Kleines Beispiel: jemand schreibt eine Container Klasse mit einer Funktion, um mehr Speicher anzufordern.void resize(size_t n) { delete[] p_; p_ = new T[n]; }Du kommst jetzt daher und sagst, Exceptions sind falsch, da Preconditions nicht mehr erfüllt sind, und durch Stack-Unwinding Unsinn passiert. Du hast in dem Beispiel natürlich recht. Das ist aber keine Lösung. Ist es denn nicht tausend mal besser, einfach die Logik zu korrigieren?
void resize(size_t n) { T* tmp = new T[n]; delete[] p_; p_ = tmp; }Schon sind wieder alle Preconditions erfüllt und Exceptions steht nichts mehr im Wege. Ob das immer so möglich ist, ist natürlich eine andere Frage. Aber so sollte man doch grundsätzlich erstmal an die Problematik herangehen, anstatt zu schreien, Stack-Unwinding ist böse und nimm besser terminate.
Plotzenhotz schrieb:
Wo dieser Fehler genau liegt können wir nicht wissen, der kann direkt in der aufrufenden Funktion liegen, oder in einem ganz anderen Modul.
Ich formuliere das mal etwas anders. Wenn zum Zeitpunkt eines throw's Preconditions nicht erfüllt sind, und sich das Programm bereits in einem undefinierten Zustand befindet, liegt der Fehler 60cm vorm Monitor.

Plotzenhotz schrieb:
Der Denkfehler liegt also IMHO darin anzunehmen es sei sicher noch irgendwelchen Code auzuführen wenn ein unbekannter Fehler dazu geführt hat dass ein Ereignis eingetreten ist welches per definitionem nie eintreten darf.
Was sind für dich denn "unbekannte" Fehler? Wenn ich eine Exception fliegen lasse, und mich auf das Stack-Unwinding verlasse, dann sind das immer bekannte Fehler. Bei Hardware- oder OS-Exceptions wird sowieso kompromisslos weggeschossen ohne Stack-Unwinding. Dafür brauche ich kein terminate().
Plotzenhotz schrieb:
Ich meine man sollte zwischen 'Exceptions' und 'Errors' unterscheiden. Eine Exception ist eben etwas was vorkommen kann und darf wenn das Programm 100% Fehlerfrei ist. Für so einen Fall würde ich NICHT vorschlagen terminate() aufzurufen - wäre ja komplett doof. Für den Fall allerdings dass etwas eintritt was nur passieren kann wenn das Programm eben NICHT 100% OK ist, man also einen Programmierfehler "gefunden" hat, in dem Fall wäre es oft schlauer das Programm abzuschiessen.
Viele Programmierer meinen solche Fälle sollte man in Release garnicht prüfen, dafür müsse ein assert() reichen.
All die Diskussion, und du willst uns nur sagen, dass du lieber terminate anstatt assert verwendest? Oder wie ist das zu verstehen? Wobei ich das bedenklich finden würde, wenn es so wäre. Unterscheidest du nicht zwischen Programmier- (Logik-) Fehlern und Runtime Fehlern?
-
So Leute ich hab mich nu registriert. Nicht als "Plotzenhotz" - das mag wohl einige verwirren, aber egal.
Lies doch bitte einfach was ich schon 3x hier geschrieben habe. Du hast ganz eindeutig nicht verstanden wovon ich rede. Danke und gute Nacht.
-
Tja, wenn du meinst.
Mit der Einstellung wirst du aber schlechte Chancen haben, mal was neues kennenzulernen oder dich gar weiterzuentwickeln.