vlib
-
kingruedi schrieb:
hehe, mach mal einen monotone oder svn server auf
theoretisch hab ich ja einen. muß mir mal erzählen lassen, wie man sowas verwendet.
btw. für welchen Compiler testest du? GCC 4?
windows: ms toolkit 2005
windows: mingw (gcc 3.4.2)
linux: gcc 3.3.5-20050130
-
Bei mir gehts immer noch nicht...
mkdir .outMakefile echo keep > .outMakefile/keep g++ -Wall -Werror -pipe -fno-rtti -O3 -s -DNDEBUG -fomit-frame-pointer -ffast-math -fno-enforce-eh-specs -fmerge-all-constants -c -o .outMakefile/assert.o -MD assert.cpp g++ -Wall -Werror -pipe -fno-rtti -O3 -s -DNDEBUG -fomit-frame-pointer -ffast-math -fno-enforce-eh-specs -fmerge-all-constants -c -o .outMakefile/credits.o -MD credits.cpp g++ -Wall -Werror -pipe -fno-rtti -O3 -s -DNDEBUG -fomit-frame-pointer -ffast-math -fno-enforce-eh-specs -fmerge-all-constants -c -o .outMakefile/FileReader.o -MD FileReader.cpp g++ -Wall -Werror -pipe -fno-rtti -O3 -s -DNDEBUG -fomit-frame-pointer -ffast-math -fno-enforce-eh-specs -fmerge-all-constants -c -o .outMakefile/FileWriter.o -MD FileWriter.cpp g++ -Wall -Werror -pipe -fno-rtti -O3 -s -DNDEBUG -fomit-frame-pointer -ffast-math -fno-enforce-eh-specs -fmerge-all-constants -c -o .outMakefile/iostream.o -MD iostream.cpp g++ -Wall -Werror -pipe -fno-rtti -O3 -s -DNDEBUG -fomit-frame-pointer -ffast-math -fno-enforce-eh-specs -fmerge-all-constants -c -o .outMakefile/main.o -MD main.cpp g++ -Wall -Werror -pipe -fno-rtti -O3 -s -DNDEBUG -fomit-frame-pointer -ffast-math -fno-enforce-eh-specs -fmerge-all-constants -c -o .outMakefile/os.o -MD os.cpp g++ -Wall -Werror -pipe -fno-rtti -O3 -s -DNDEBUG -fomit-frame-pointer -ffast-math -fno-enforce-eh-specs -fmerge-all-constants -c -o .outMakefile/prime.o -MD prime.cpp g++ -Wall -Werror -pipe -fno-rtti -O3 -s -DNDEBUG -fomit-frame-pointer -ffast-math -fno-enforce-eh-specs -fmerge-all-constants -c -o .outMakefile/PrimeGenerator.o -MD PrimeGenerator.cpp swap.hpp: In function `void swap(Data&, Data&) [with Data = Size]': Stack.hpp:52: instantiated from `void swap(Stack<PrimeGenerator::Runner>&, Stack<PrimeGenerator::Runner>&)' PrimeGenerator.hpp:95: instantiated from here swap.hpp:6: Fehler: Abfrage des Elementes »swap« in »a«, das vom Nicht-Klassentyp »long unsigned int« ist make: *** [.outMakefile/PrimeGenerator.o] Fehler 1
Mit meinem i386-pc-linux-gnu-g++ gehts übrigens. Wird wohl was mit der größe von datentypen z tun haben...
-
ness schrieb:
Bei mir gehts immer noch nicht...
jup. momentchen.
-
jetzt müßte es gehen.
lag an den datentypen.
weil ich swap nicht mehr für alle typen angeboten hab, sondern nur noch explizit angebe, was geswapped werden kann, und weil ich swap für unsigned long noch nicht hatte, konnte er bei dir Size nicht swappen.
-
kingruedi schrieb:
hehe, mach mal einen monotone oder svn server auf
eigentlich würde ein script um wget reichen, das bescheidsagt, wenn sich die vhlib.tar.bz geändert hat.
-
volkard schrieb:
die vlib soll nicht nur replacement der standardlib sein, sondern ein paar sachen schon anders machen. da flag wird schon ein wenig deutlicher bei
Aber das Allokatoren Konzept finde ich schon ziemlich gut, da es flexibel und ohne Overhead ist. Man braucht es zwar selten, aber wenn man mal Speicher woanders anlegen will, dann ist man aufgeschmissen und es kostet ja keinen Overhead.
Aber true und false Flags sind leider nicht so eindeutig. Mach doch ein
enum { OnHeap=true, OnStack=false };
-
Was mir persöhnlich nicht gefällt ist, dass ASSERT eine Ausnahme wirft. Das schafft meiner Meinung nach viel mehr Probleme als es löst.
Zum Beispiel kann es in keinem Destruktor eingesetzt werden.
Desweiteren kann man mittels catch(...) die Assertion auch abfangen also ist es nicht mehr sicher, dass eine Assertion immer knallt.
Wenn eine Assertion fehlschlägt dann ist der Wurm in der Programlogik also ist es nicht sicher, dass die Destruktoren ihren Job erledigen. Sollten einer deshalb während dem Stackunwinding abstürtzen dann merkt man nicht mal, dass die Assertion fehlschlug.
Man weiß nichtmal von wo die Ausnahme flog und man fragt wie zum Teufel der Programfaden dahin gelangt ist. Die Assertion schlug ja nicht fehl und danach gibt es kein throw mehr.
Desweiteren kann es selbst richtige Programlogik als falsch erscheinen lassen. Beispiel:
Obj::Obj(){ ptr = new int; *ptr = 1; } void Obj::foo() { // Mach irgendetwas, dass *ptr 0 ist was es aber nicht sein sollte } void Obj::swap(Obj&other) { int*temp = this->ptr; ptr = other.ptr; ASSERT(*ptr != 0); other.ptr = temp; ASSERT(*other.ptr != 0); } Obj::~Obj() { delete ptr; // Ein Genuß wie das hier knallt }
Das eigentliche Problem ist zwar, dass foo auf 0 testen sollte, aber du weißt ja hoffentlich wie schnell man ein ASSERT vergessen hat. in swap hat man sich gedacht, dass ein zusätzliches ASSERT nie überflüssig ist und teste sobald ein Objekt wieder in einem funktionierenden Zustand ist. this ist korrekt beim ersten ASSERT, other noch nicht wird es aber am Ende von swap sein.
So nun hat foo aber gewütet und das erste ASSERT wirft eine Ausnahme, other bleibt deshalb korrupt. Nun kommt es zum doppelten Löschen im Destruktor. Der Debugger wird sich hier melden. Der Programmierer wird denken, dass ihm irgendwo ein Flüchtigkeitsfehler bei der Speicherverwaltung unterlaufen ist und diese überprüfen und dabei ist diese fehlerfrei.
Ganz unabhängig davon ist
#define CHECK(cond) if(cond);else raiseAssertError(#cond,__FILE__,__LINE__)
fehlerhaft. Beispiel:
namespace foo { void raiseAssertError(char const* cond,char const* file,int line); void bar() { CHECK(false); // Hier wird foo::raiseAssertError aufgerufen } }
Das müsste
#define CHECK(cond) if(cond);else ::raiseAssertError(#cond,__FILE__,__LINE__)
heißen.
Dann währe ein FAIL(), FAIL_MSG(msg), CHECK_MSG(what, msg), ASSERT_MSG(what, msg) sicher nicht verkehrt.
In addModUnguarded würde ich die Argumente noch mit einer Assertion überprüfen.
Ganz dumme Frage aber was bezweks du mit
inline void operator delete(void*,void*) throw(){ }
-
Ben04 schrieb:
Ganz dumme Frage aber was bezweks du mit
inline void operator delete(void*,void*) throw(){ }
der wird aufgerufen, wenn der entsprechende placement new eine excpetion wirft. ok, das tut er zwar nicht, aber weiß das auch der linker?
-
volkard schrieb:
template<typename Iterator> void sort(Iterator begin,Iterator end);
allerdings sofort probleme, denn die iteratoren können noch nicht zum beispiel op+(Iterartir,Size). das müßte dann nachgerüctet werden.
Das wär weniger das Problem, denke/hoffe ich, hab eh grad mit meinen LinkedList Iteratoren zu kämpfen. :p
Aber range-checked sind sie ja, oder hab ich's falsch verstanden?volkard schrieb:
Dauert dir aber wohl zu lange, bis ich da fertig wär.
kann man nicht wissen. ich brauche noch keinen. wenn ich einen brauche, schreib ich halt einen. wenn du vorher einen hast, sag bescheid.
Ok, ansonsten kann ich nur (kennst du wahrscheinlich schon, aber wurst) www.sortieralgorithmen.de empfehlen.
-
Ben04 schrieb:
Was mir persöhnlich nicht gefällt ist, dass ASSERT eine Ausnahme wirft. Das schafft meiner Meinung nach viel mehr Probleme als es löst.
aber es löst auch probleme. und zwar darf ja durch ein einfache arraygrenzenüberschreitung irgendwo nicht auf einmal die datenbank wo anders in nem inkonsistenten zustand gelassen werden.
Zum Beispiel kann es in keinem Destruktor eingesetzt werden.
jup. zur zeit hoffe ich, daß assertions in destruktoren was extrem seltenes sind.
Desweiteren kann man mittels catch(...) die Assertion auch abfangen also ist es nicht mehr sicher, dass eine Assertion immer knallt.
man darf eben AssertError nicht fangen, ohne es weiterzuwerfen, außer man ist die main().
Wenn eine Assertion fehlschlägt dann ist der Wurm in der Programlogik also ist es nicht sicher, dass die Destruktoren ihren Job erledigen.
da wäre ich mir nicht so sicher.
Sollten einer deshalb während dem Stackunwinding abstürtzen dann merkt man nicht mal, dass die Assertion fehlschlug.
jo. bisher kenne ich einen kritischen fall. wenn ein FileWriter destruiert wird und deswegen flush() aufruft und die platte voll ist.
Desweiteren kann es selbst richtige Programlogik als falsch erscheinen lassen. Beispiel:
... void Obj::swap(Obj&other) { int*temp = this->ptr; ptr = other.ptr; ASSERT(*ptr != 0); other.ptr = temp; ASSERT(*other.ptr != 0); } Obj::~Obj() { delete ptr; // Ein Genuß wie das hier knallt }
[/quote]
aua. nun könnte ich sagen, daß man assert wie ne ausgabe auch halten sollte, also auf keinen fall im exception-unsicheren teil veranstalten. das würde ich auch sofort, wenn ich mir sicher wäre, daß es klappt. aber die range checked iteratoren werfen ja auch. und das kann ja oft passieren, wenn man am wenigsten damit rechnet. ein ausweg wäre ne datei assertlog.txt, die zuätzlich geschrieben wird. in den seltenen fällen, die du anführtst, hätte man dann wenigstens noch ne chance, zu sehen, was los war.Das müsste
#define CHECK(cond) if(cond);else ::raiseAssertError(#cond,__FILE__,__LINE__)
heißen.
ok. ist gefixt.
In addModUnguarded würde ich die Argumente noch mit einer Assertion überprüfen.
ok. in allen Unguarded-sachen.
hab vor jahren nach exceptionwerfendem assert umgestellt und es war bisher immer prima. meyers schlägt das auch vor. normalerweise soll dieses assert, wenn der debugger an ist, auch noch __asm int 3; ausführen, was einen auf der fehlerzeile in den debugger wirft. damit kann man dann auch die schwierigen fälle sehr gut verfolgen. normalerweise, hält man einfach das programm an, wenn man den fehler repariert hat. ist ne kritische sache unten am leben, die ber dtor gerettet werden muss, sagt man dem debugger halt, er soll bis programmende laufen.
ich will es drauf ankommen lassen, ob die von die genannten probleme sich wirklich zeigen werden und lass ASSERT vorläufig werfen.
-
GPC schrieb:
Aber range-checked sind sie ja, oder hab ich's falsch verstanden?
ja, sind sie. das könnte recht praktisch werden, wenn man nen sortieralgo baut. bin mal gespannt.
-
volkard schrieb:
jetzt müßte es gehen.
lag an den datentypen.
weil ich swap nicht mehr für alle typen angeboten hab, sondern nur noch explizit angebe, was geswapped werden kann, und weil ich swap für unsigned long noch nicht hatte, konnte er bei dir Size nicht swappen.Ja, sowas hab ich mich auch gedacht...
Aber, was bezweckst du damit? Ich meine, zumindest primitivtypen könntest du doch normal swappen?namespace help { template<class T,bool simple_swap> struct sh { static inline void swap(T& a,T& b) { a.swap(b); }; }; template<class T> struct sh<T,true> { static inline void swap(T& a,T& b) { T tmp(a); a=b; b=tmp; }; }; }; template<class T> inline void swap(T& a,T& b) { help::sh<T,type_traits<T>::is_primitive||type_traits<T>::is_pointer>::swap(a,b); };
-
ness schrieb:
Aber, was bezweckst du damit?
daß jeder, der ne klasse baut, die swappen können soll, das auch anbieten muss. dabei garantiert er dann auch, daß swap exceptionsicher ist.
[/quote]Ich meine, zumindest primitivtypen könntest du doch normal swappen?[/quote]
jo. nix dagegen.namespace help { template<class T,bool simple_swap> struct sh { static inline void swap(T& a,T& b) { a.swap(b); }; }; template<class T> struct sh<T,true> { static inline void swap(T& a,T& b) { T tmp(a); a=b; b=tmp; }; }; }; template<class T> inline void swap(T& a,T& b) { help::sh<T,type_traits<T>::is_primitive||type_traits<T>::is_pointer>::swap(a,b); };
hab keine type_traits.
für pointer hab ich schontemplate<typename Data> inline void swap(Data*& a,Data*& b){ Data* tmp(a); a=b; b=tmp; }
was auch zu funktionieren scheint.
nu bin ich am überlegen, ob ich die primitiven typen lieber aufzählen sollte, oder ob ich meta-programming-monster baue.
vol umfang des codes sollte es ähnlich sein.
-
volkard schrieb:
aber es löst auch probleme. und zwar darf ja durch ein einfache arraygrenzenüberschreitung irgendwo nicht auf einmal die datenbank wo anders in nem inkonsistenten zustand gelassen werden.
Kommt natürlich auf die Situation an. Ich würd wenn immer möglich ein Program nicht ungetestet auf eine wichtige Datenbank loslassen. Immer zuerst auf entweder eine Kopie der Richtigen oder auf eine die mit Zufallsdaten gefüllt ist loslassen, jedenfals eine die man getrost reseten kann. Aber du hast recht dies ist in der Tat ein Problem.
volkard schrieb:
Desweiteren kann man mittels catch(...) die Assertion auch abfangen also ist es nicht mehr sicher, dass eine Assertion immer knallt.
man darf eben AssertError nicht fangen, ohne es weiterzuwerfen, außer man ist die main().
Ich hab schon viele Artikel im Internet die zu folgendem geraten haben gesehen:
class Foo{ public: ~Foo(){ try{ bar(); }catch(...){ } } };
Das wäre ja schon beinahe tötlich für dein ASSERT. Da ein fehlerhaftes Program ja gar nicht mal abstürtzt.
Vielleicht wäre folgender Ansatz eine Lösung:
// Nicht von Error ableiten, soll ja nicht // gefangen werden oder? class AssertError { static unsigned instance_count; public: AssertError(const char*msg){ // Hiermit ist sichergestellt, dass die Assertion // auch gemeldet wird. cerr<<"Assert: "<<msg<<endl; ++instance_count; } AssertError(const AssertError&){ ++instance_count; } ~AssertError(){ // hiermit ist sichergestellt, dass das Assert nicht abgefangen // wird --instance_count; if(instance_count == 0) abort(); } }; unsigned AssertError::instance_count = 0;
-
Ben04 schrieb:
volkard schrieb:
aber es löst auch probleme. und zwar darf ja durch ein einfache arraygrenzenüberschreitung irgendwo nicht auf einmal die datenbank wo anders in nem inkonsistenten zustand gelassen werden.
Ich hab schon viele Artikel im Internet die zu folgendem geraten haben gesehen:
class Foo{ public: ~Foo(){ try{ bar(); }catch(...){ } } };
Das wäre ja schon beinahe tötlich für dein ASSERT. Da ein fehlerhaftes Program ja gar nicht mal abstürtzt.
Genaugenommen wäre es tödlich, besagten "Rat" anzunehmen und nicht, das ASSERT so zu machen. Ein Destruktor soll natürlich prinzipiell keine Exception werfen. Das heißt aber nicht, dass er mit try-catch umgeben werden soll. Das heißt eher, dass wenn der Destruktor was loggt und er konnte es nicht in die Datei schreiben, dass speziell dieser Schreibvorgang gesichert wird und die restliche Arbeit des Destruktors weiterläuft und die von anderen anstehende Destruktoren auch.
Ich finde das ASSERT gut so, es spricht gar nichts dagegen, den technisch einwandfreien Exception-Mechanismus zu nutzen. Es ist auch nicht so, dass volkard damit was völlig neues auf die Beine gestellt hat, das ist gängige Praxis in C++ und anderen Sprachen.
-
Ben04 schrieb:
Ich hab schon viele Artikel im Internet die zu folgendem geraten haben gesehen:
class Foo{ public: ~Foo(){ try{ bar(); }catch(...){ } } };
lol. das ist auch sachlich falsch, weil ich dann bei ner nicht-fertig-geschriebenen datei denke, sie sei fertig. dann schon lieber fürstlich abschmieren. ich stelle mir nur mal vor, mein textverarbeitungsprogramm würde mit solchen methoden schreiben.
Das wäre ja schon beinahe tötlich für dein ASSERT. Da ein fehlerhaftes Program ja gar nicht mal abstürtzt.
zur vlib muß dann auch ein style-guide her. jo, ich schreib ihn gerade mal.
Vielleicht wäre folgender Ansatz eine Lösung:
[cpp]
// Nicht von Error ableiten, soll ja nicht
// gefangen werden oder?
[/quote]
stimmt. soll in der main gefangen werden, da mach ich halt die zeile dazu.den zähler überleg ich mir noch, ob mir irgendein fall einfällt, wo der stört.
-
destruktoren dürfen nix tun, was normale laufzeitfehler verursachen könnte. man wird nicht mit assert fragen, ob daten auf die platte geschrieben werden konnten. man wird beim schreiben trotzdem immer testen (bei mir mit SYSCHECK) und gegebenenfalls exceptions werfen.
ich kann nur verhindern, daß bei destruktoren unfug passiert, wenn ich dort nichts aufrufe, was laufzeitfehler haben könnte. insofern ist flush() im destruktor von FileReader schlecht.
andererseits muß aber genau dieses flush aufgerufen werden.eigentlich muss nur die erste exception geworfen werden und bei allen folgenden wird nicht geworfen, sondern auggegeben. also in raiseAssertError das if, das den zähler prüft.
-
volkard schrieb:
Ben04 schrieb:
Ich hab schon viele Artikel im Internet die zu folgendem geraten haben gesehen:
class Foo{ public: ~Foo(){ try{ bar(); }catch(...){ } } };
lol. das ist auch sachlich falsch, weil ich dann bei ner nicht-fertig-geschriebenen datei denke, sie sei fertig. dann schon lieber fürstlich abschmieren.
So "lol" ist die Idee gar nicht mal. Es steckt der gleiche Grundgedanke dahintert als hinter einer exceptionwerfenden Assertion : retten was noch zu retten ist. Eine Exception in einem Destruktor ist ein Programfehler genauso wie eine fehlgeschlagene Assertion. Ich will jetzt nicht sagen, dass dies eine Glanzlösung ist da sie sehr viele Schwächen hat. Besser wäre natürlich die fliegende Ausnahme zu fangen und durch eine AssertError Ausnahme zu ersetzen, geht aber nicht.
Zu deinem Problem mit dem flush : Flush kann eine Ausnahme werfen. Also was sind die mögliche Lösungen:
a) Flush raus aus dem Destruktor und explicit aufrufen. Schlecht da dann im Fall einer Ausnahme nie geflusht wird.
b) Flush wirft nicht und gibt einen Fehlerwert zurück den man ignoriert da man im Fehlerfall nichts schlaues machen kann.
c) try Block um flush bauen wie oben gezeigt, was eigentlich das gleiche wie b) ist.
d) Streams nicht puffern.
Ideal was die Fehlerbehandlung ist nur d. Eine Kombination aus a und d könnte ich mir auch noch vorstellen : Nicht werfende Ausgaben gruppieren und in einen eignen Scope packen und noch einen ScopeGuard hinein der das Buffern einschaltet. Durch den ScopeGuard gewinnt man zwar keine Zeile im Vergleich mit a) aber wenn man ihn vergisst ist das kein Drama. Dagegen würden bei a) die Daten verloren gehen.
-
So "lol" ist die Idee gar nicht mal.
Es ist fast immer lol, grundsätzlich alle Exception-Arten zu fangen, von ein paar sehr wenigen Fällen wie main() mal abgesehen.
retten was noch zu retten ist. Eine Exception in einem Destruktor ist ein Programfehler genauso wie eine fehlgeschlagene Assertion
Es gibt hier zwei Dinge zum differenzieren: WO tritt der Fehler auf und WAS für ein Fehler ist es. Eine Assertion ist ein Logikfehler, der gar nicht sein darf. Definitionsgemäß gibt es keinen AssertionError. In der Praxis gibt es ihn aber, weil Programmierer Menschen sind. Jetzt zum Destruktor: Er sollte nichts großartiges tun, was Exception-verdächtig ist, z.B. I/O-Sachen. Wär ja blöd, wenn beim Zerstören von etwas noch was kaputt geht. Die Hauptidee ist aber, dass der Destruktor und andere Destruktoren ihre verdammt wichtige Arbeit machen. Deshalb werde ich nicht im Destruktor eine Datenbank-Verbindung herstellen, eine Datei herausladen, dekomprimieren und auf meinen ftp hochladen. Da kann so viel schief gehen, dass mir schon schlecht wird. Aber vor einem AssertionError im Destruktor muss ich keine Angst haben, denn den gibt es definitionsgemäß gar nicht.
Auch wenn ich ein geiles Array mit Indexprüfung habe und das wirft mir ne Exception im Destruktor... warum sollte ich sie fangen? Ich darf ums Verrecken keinen ungültigen Index benutzen, Destruktor hin oder her. Bei solchen logischen Fehlern, die nicht auf Dateizugriff oder sonst was zurückzuführen sind, was nicht in meiner Macht liegt, soll das Programm sich abschießen, nichts anderes ist sinnvoll. Es gibt nichts zu retten.
Deshalb fängt man so einen Error nicht. Man fängt überhaupt niemals Exceptions, die man nicht erwartet.
-
morgen,
nach laengerer Zeit auch nochmal was von mir. Sorry das ich nichts mehr geschrieben hab, aber war in Siegen
mit der Immatrikulation und dem ganz bloeden Papierkram beschaeftigt.Hab mir die aktuelle Version mal gezogen und die geht auch prinzipiell durch den gcc4, allerdings findet
er die peekNoEof()-Funktion nicht, welche in der Reader.cpp aufgerufen wird. Kann die auch sonst nirgends
finden, hab ich doch wieder eine aeltere Version gezogen?mfg
v R