C++ ohne STL?
-
ich mag nicht mehr als "ungeschickt" sagen. und meine größte hochachtung gilt denen, die damals die streams so gegossen haben, wie sie jetzt sind. es war neuland, und das, was sie vollbrachten ist beeindruckend gut für seine zeit. nu hat sich c++ ein wenig gewandelt in richtungen, die absolut nicht vorhersehbar waren, deswegen ein wenig ungeschickt aus heutiger sicht.
ich beschäftige mich ja mit einer alternativen implemetierung und die zeichen stehen gut, daß ich damit baden gehe, weil meine nicht genug können und dafür nur ein fitzelchen schneller sind. die exe-größe ist kein argument, weil der brocken in ner dll (shared lib) zu liegen hat. und daß es open/close gibt, darf man ja ignorieren, wenn man mag.
-
@Topic: Man kriegt vor allem mit Templates sehr leicht riesige Executables. Ich finde es nicht unbedingt besser, wie bei qsort einen Funktionszeiger zu übergeben, aber es ist platzsparender, als für jedes Vergleichskriterium bei ein und demselben Elementtyp das Template komplett neu zu instanzieren. Auf dieses Problem (wenn es sich als eines erweist) stößt man eben in der STL dauernd.
ich beschäftige mich ja mit einer alternativen implemetierung und die zeichen stehen gut, daß ich damit baden gehe, weil meine nicht genug können und dafür nur ein fitzelchen schneller sind.
Wenn du nur das EOF nicht so affig machst wie die iostreams, sind sie schon 10mal besser. Wie war das noch gleich? Es hielt sich lange das Gerücht, dass in Java Streams auf eine Exception enden, ohne dass man es verhindern könne und da wurde ja wahnsinnig drauf geschimpft. Jetzt scheint es eher auf die iostreams zuzutreffen, wenn man die Exceptions anschaltet, aber hier schimpft keiner? Benutzt keiner Exceptions bei I/O-Fehlern? Ich habe mir deine Streams noch nicht angeschaut, aber das EOF interessiert mich jetzt, daher werde ich sie mir mal runterladen.

-
Ich behaupte mal, dass nen C++ Compiler für so ne Plattform seine eigene kleine "STL" mitbringt die auch auf die Plattform angepasst ist

-
Optimizer schrieb:
aber es ist platzsparender, als für jedes Vergleichskriterium bei ein und demselben Elementtyp das Template komplett neu zu instanzieren. Auf dieses Problem (wenn es sich als eines erweist) stößt man eben in der STL dauernd.
für sort müßte man nen zeiger auf less und einen auf swap übergeben. ungern. die inline-optimierung geht flöten. aber nette idee, einfach mal heap_sort zu nehmen, weils kleineren code macht als intro_sort.
[quoteWenn du nur das EOF nicht so affig machst wie die iostreams, sind sie schon 10mal besser.[/quote]
ich denke daran, daß das normalverhalten ist, daß der user while(!eof) read() macht, aber wenn er zu weit liest ne exception fliegt. wie in java.
aber egal, für was ich mich jetzt entscheide, messungen werden ergeben, daß es doch ganz anders hätte besser gemacht worden sein können wird (synaktisch korrekt?).
-
volkard schrieb:
ich mag nicht mehr als "ungeschickt" sagen. und meine größte hochachtung gilt denen, die damals die streams so gegossen haben, wie sie jetzt sind. es war neuland, und das, was sie vollbrachten ist beeindruckend gut für seine zeit. nu hat sich c++ ein wenig gewandelt in richtungen, die absolut nicht vorhersehbar waren, deswegen ein wenig ungeschickt aus heutiger sicht.
Klar war es aus damaliger Sicht eine Meisterleistung allerdings sind sie für heutig Verhältnisse veraltet und müssten ausgetauscht oder stark verbessert werden. Wenn man sich C++ ohne Exceptions und Templates vorstellt dann sind sie gar nicht mal so schlecht. Allerdings ändert das nichts daran, dass man sie überarbeiten muß. Spätestens bei der Standardisirung hätten aber viele der Probleme klar sein müssen. Zu mindest das Problem mit den Exceptions liegt ja auf der Hand von daher sind die heutigen Standard Iostreams aus meiner persöhnlichen Sicht schlecht realisiert. Man hat einfach eine unausgereifte Idee zum Standard erhoben.
volkard schrieb:
ich beschäftige mich ja mit einer alternativen implemetierung und die zeichen stehen gut, daß ich damit baden gehe, weil meine nicht genug können und dafür nur ein fitzelchen schneller sind. die exe-größe ist kein argument, weil der brocken in ner dll (shared lib) zu liegen hat. und daß es open/close gibt, darf man ja ignorieren, wenn man mag.
Ich hab mich auch schon seit einiger Zeit Gedanken über eine Implementirung von Streamklassen gemacht. Hier mal meine Vorschläge. Vielleicht sind ja eine brauchbare Ansätze dabei.
Modifier würde ich folgendermasen realisieren.
class Target{ public: virtual uint write(const char*, uint)=0; }; class ostream{ public: template<class T> ostream&operator<<(const T&t){ ::write(*this, t); return *this; } private: Target*target; }; template<class T> class hex_modifed_ostream:public T{ public: explicit hex_modified_ostream(ostream&out): T(out){} template<class T> hex_modified_ostream&operator<<(const T&t){ T::opertor<<(t); return *this; } ostream&operator<<(int i){ // hex Implementirung } private: ostream&out; }; template<class ostreamT> void write(ostreamT&out, int i) { // dec Implementirung } template<class ostreamT> void write(ostreamT&out, char c) { // ... } //... struct Foo { int a,b; }; template<class ostreamT> void write(ostreamT&out, const Foo&foo){ out<<foo.a; hex_modifed_ostream<ostreamT> hex_out(out); out<<foo.b; } // ... ostream out; foo(out); // einmal dec und einmal hex hex_modifed_ostream<ostream> hex_out(out); foo(hex_out); // zweimal hexIch sehe folgende Vorteile:
- Neue modifier können nach Lust und Laune definiert und in Kombination mit alten genutzt werden.
- Eine Ausgabefunktion kann von Außen gesagt bekommen wie etwas zu formatiren ist. Sie kann diese Einstellung benutzen oder sie überschreiben und beim Beenden der Funktion ist die Überschreibung automatisch aufgehoben. Zum Beispiel wäre eine Funktion die ein vector<T> ausgibt doch gehandicapt wenn man T nicht formatieren könnte. Dein cout<<hex(100) Ansatz könnte da Probleme bereiten.
Folgende Schwachpunkte sehe ich:
- Compiler muß gut inlinen können sonst kommt es zu Overheat.
- Alle Ausgabefunktionen sind Templates. Dies dürfte eigentlich kein Problem sein wenn diese Funktion auch wirklich nur für die Ausgabe zuständig ist. Wenn spezielle Formatirung nötig ist (wie zum Beispiel bei int bin => dec) dann kann man auch auf eine Funktion zurückgreifen die ein String zurück gibt und den String ausgeben. Die Formatirungsfunktion muß ja nicht umbedingt ein Template sein.
Ich hab noch nicht hiermit gearbeitet also könnten es versteckte Probeme geben.
Bei den Eingabeströmen weiß ich nicht so recht ob es eine gute Idee aber eventuel könnte man die Erkennung der Daten von deren Einlesung trennen. Ich hab schon mehrmals gute Erfahrungen mit der Trennung von Erkennung und Einlesung gemacht.
Um einen Puffer der die gelesenen Zeichen enthält bis, dass es sicher ist, dass die Eingabe korrekt oder falsch ist kommt man nicht umher.
istream in; if(in.is_at<int>()) do_bar_stuff(in) else if(in.is_at<Foo>()) do_foo_stuff(in))Ein Foo anzulegen könnte kostspielig sein und nur um zu testen ob das nächste was der Stream enthält ein Foo ist eines anlegen zu müssen klingt verschwenderisch. Was sich auch als sehr nützlich erwiesen hat war ein was_at.
template<class T> bool istream::was_at(const T&a){ if(is_at<T>()){ T b; *this>>b; if(b == a) return true; // nutze Puffer zeichen um Einlesen rückgängig zu machen } return false; } //... vector<int>vec; if(in.was_at('{')) { do{ int i; in>>i; vec.push_back(i); }while(in.was_at(',')); if(!in.was_at('}')) throw format_error; }Keine Ahnung ob generel Purpose Streams dies brauchen. Ich hab es zu mindest als sehr nützlich empfunden.
So nun reißt meine Ideen mal in Stücke, damit ich nicht irgendwann über unerwachtete Probleme stolpere.

Optimizer schrieb:
@Topic: Man kriegt vor allem mit Templates sehr leicht riesige Executables. Ich finde es nicht unbedingt besser, wie bei qsort einen Funktionszeiger zu übergeben, aber es ist platzsparender, als für jedes Vergleichskriterium bei ein und demselben Elementtyp das Template komplett neu zu instanzieren. Auf dieses Problem (wenn es sich als eines erweist) stößt man eben in der STL dauernd.
Ja aber in der Prakis wird ja nur basic_ostream<char, char_traits<char> > und basic_istream<char, char_traits<char> > verwendet und meist auch noch durch explicite Instancirung in einer Library. Das ist ja etwas anderes als std::sort wo die Parameter die ganze Zeit wechseln.
Optimizer schrieb:
Benutzt keiner Exceptions bei I/O-Fehlern?
Ich habe nur schlecht Erfahrungen mit ihnen gemacht und ich glaube den meißten wäre es auch zu affig jedesmal für jeden Strom die Ausnahmen einschalten zu müssen.
Optimizer schrieb:
Wenn du nur das EOF nicht so affig machst wie die iostreams, sind sie schon 10mal besser.
Eigentlich finde ich folgendes recht einfach als Test:
if(in.peek() == EOF)istream::eof ist affig, da gebe ich dir recht.
-
ich denke an
template<typename T> struct hexer{ }; template<> struct hexer<unsigned int>{ unsigned int& hexedVictim; hex(unsigned int& hv): hexedVictim(hv){ } friend ostream& operator<<(ostream& out,hex const& h){ unsigned int x=h.hexedVictim; do out<<"0123456789abcdef"[x%16]; while(x/=16); } }; template<typename T> hexer<T> hex(T& t){ return hexer<T>(t); }also sehr direkte handles auf die zu hexenden daten. und sachen, die nicht gehext werden können, müssen halt compilerfehler liefern. also hex war keine funktion, die etwa einen string zurücklifert, auch wenn es den anschein hat, wenn man den vorugen code so liest.
Ben04 schrieb:
Um einen Puffer der die gelesenen Zeichen enthält bis, dass es sicher ist, dass die Eingabe korrekt oder falsch ist kommt man nicht umher.
man muß aber darum umher kommen. immerhin kann das gerade zu lesende objekt ein sound-sample von 30M länge sein.
können die dateiformate nicht so sein, daß man weiß, was für ein typ danach kommt?
-
Ben04 schrieb:
Ein Foo anzulegen könnte kostspielig sein und nur um zu testen ob das nächste was der Stream enthält ein Foo ist eines anlegen zu müssen klingt verschwenderisch. Was sich auch als sehr nützlich erwiesen hat war ein was_at.
ich hab allerbeste erfahrungen mit
Foo::Foo(istream& in) :Base1(in), Base2(in), member1(in), member2(in) { }es ist einfach nur schweineschnell. und einfach. und mir ausreichend wartbar. und die factory liest einfach immer die klassenkenzeichnung vor dem objekt und ruft den entsprechenden new auf. die virtual void writeAt schreibt vor die nutzdaten die klassenkennzeichnung. da kann xml mal ganz schön nach hause gehen. um's dem menschen noch ein wenig netter in den files zu machen, hab ich noch nach der klassenkenzeichnung "{\n" und nach den nutzdaten "}\n\n" geschrieben. geschrieben wurde ascii und zahlen hex (für speed). menschen können zwar drin rumändern, aber perl-scrips machen das noch besser.
Eigentlich finde ich folgendes recht einfach als Test:
if(in.peek() == EOF)istream::eof ist affig, da gebe ich dir recht.
äh. peek() liefert nen char, alles ander wäre unfug, oder? da passt EOF nicht rein.
-
volkard schrieb:
Ben04 schrieb:
Ein Foo anzulegen könnte kostspielig sein und nur um zu testen ob das nächste was der Stream enthält ein Foo ist eines anlegen zu müssen klingt verschwenderisch. Was sich auch als sehr nützlich erwiesen hat war ein was_at.
ich hab allerbeste erfahrungen mit
Foo::Foo(istream& in) :Base1(in), Base2(in), member1(in), member2(in) { }es ist einfach nur schweineschnell. und einfach. und mir ausreichend wartbar. und die factory liest einfach immer die klassenkenzeichnung vor dem objekt und ruft den entsprechenden new auf. die virtual void writeAt schreibt vor die nutzdaten die klassenkennzeichnung. da kann xml mal ganz schön nach hause gehen. um's dem menschen noch ein wenig netter in den files zu machen, hab ich noch nach der klassenkenzeichnung "{\n" und nach den nutzdaten "}\n\n" geschrieben. geschrieben wurde ascii und zahlen hex (für speed). menschen können zwar drin rumändern, aber perl-scrips machen das noch besser.
Klar ist das schweine schnelaber nur bedingt vom Menschen lesbar. So wie ich es sehe kommt ein Container nicht umher die Zahl der Elemente als erstes abzuspeichern (oder irre ich da?). Der User sieht dann 3 {1 5 6} fügt mal schnell ein Element hinzu und du hast 3 {1 5 6 7} und das wäre ein Problem. XML ist ja nicht so sehr für Schnelligkeit ausgelegt, eher für leichte Veränderbarkeit vom Menschen mit Notpad. Meiner Meinung nach vergleichst du 2 verschiedene Paar Schuhe.
volkard schrieb:
Eigentlich finde ich folgendes recht einfach als Test:
if(in.peek() == EOF)istream::eof ist affig, da gebe ich dir recht.
äh. peek() liefert nen char, alles ander wäre unfug, oder? da passt EOF nicht rein.
peek() liefert aus genau diesem Grund ein int zurück.
http://www.cppreference.com/cppio/peek.html
-
Hallo,
wie gefallen euch eigentlich die boost::iostreams?http://www.boost.org/libs/iostreams/doc/index.html
Mein erster Eindruck ist durchaus positiv (Filtering-Streams z.B. fand ich schon immer sehr nützlich). Hab' mich aber noch nicht intensiv genug damit beschäftigt um die Streams wirklich beurteilen zu können.
-
Ben04 schrieb:
So wie ich es sehe kommt ein Container nicht umher die Zahl der Elemente als erstes abzuspeichern (oder irre ich da?). Der User sieht dann 3 {1 5 6} fügt mal schnell ein Element hinzu und du hast 3 {1 5 6 7} und das wäre ein Problem.
jup.
deswegen hatte ich da gemogelt und {1 5 6 7} nur gemacht und es war noch ok, weil containers selten sind. schlimm sind natürlich strings ohne die längenangabe am anfang.
ich dachte an zwei sorten streams, plainstreams und binarystreams, normalerweise werden binarystreams verwendet, aber der user kann jederzeit die welt auch auf nen plainstream speichern oder von da laden. naja, als zukonftsvision. war so aber auch schon ok und ich hab statt 10 min (konkurrenz) nur 20 sec gebraucht für 30M strukturierte daten. hab das projekt aber nicht fortgeführt.
-
elise schrieb:
warum nicht?
printf zum bleistift.
und wozu braucht es die string klasse *g*Hm, string meide ich irgendwie und bastel immer spezialisierte Alternativen (die wahrscheinlich viel schlechter sind, aber egal
). Ich denke die Stream-Klassen braucht man, sonst modelliert man sich seine Container eben selbst 
-
HumeSikkins schrieb:
Hallo,
wie gefallen euch eigentlich die boost::iostreams?http://www.boost.org/libs/iostreams/doc/index.html
Mein erster Eindruck ist durchaus positiv (Filtering-Streams z.B. fand ich schon immer sehr nützlich). Hab' mich aber noch nicht intensiv genug damit beschäftigt um die Streams wirklich beurteilen zu können.
Sieht cool aus. So ein bzip2- oder Regex-Filter kann schon praktisch sein und es ist schön, solche feinen Dinge in einer zusammenhängenden Bibliothek vorzufinden. Aus boost ist ganz schön was geworden.
-
volkard schrieb:
ich dachte an zwei sorten streams, plainstreams und binarystreams, normalerweise werden binarystreams verwendet, aber der user kann jederzeit die welt auch auf nen plainstream speichern oder von da laden. naja, als zukonftsvision. war so aber auch schon ok und ich hab statt 10 min (konkurrenz) nur 20 sec gebraucht für 30M strukturierte daten. hab das projekt aber nicht fortgeführt.
Du hast aber dafür die Eigenschaft geopfert, dass die Daten ohne weiteres vom Menschen lesbar sind. Die Idee 2 austauschbare Streamsorten zu benutzen hatte ich auch schon hab sie aber verworfen da beispielsweise XML nict bloß anders formatiert ist, sondern auch noch Metadaten benötigt.
<a> <b>8</b> <c>12</c> </a>Eigentlich nur mit Metadaten sinnvoll.
<Geburtstag> <Tag>8</Tag> <Monat>12</Monat> </Geburtstag>Zum generiren von XML braucht es also mehr Informationen als für binäre Representationen. Dann gibt es noch ein weiters Problem dies wäre auch gültiges XML
<Geburtstag> <Monat>12</Monat> <Tag>8</Tag> </Geburtstag>Dies würde aber Probleme bereiten da die binären Streams mit Hilfe der Reihenfolge die Daten deuten.
Meiner Meinung nach kann man nur die Zeichenquelle und Ausgabe teilen.
@HumeSikkins Ich finde, dass es die IOStreams um sehr nützliche Funktionen erweitert. Diese auch gut umsetzt aber leider die alten Probleme nicht löst.
-
Ben04 schrieb:
Die Idee 2 austauschbare Streamsorten zu benutzen hatte ich auch schon hab sie aber verworfen da beispielsweise XML nict bloß anders formatiert ist, sondern auch noch Metadaten benötigt.
<a> <b>8</b> <c>12</c> </a>Eigentlich nur mit Metadaten sinnvoll.
<Geburtstag> <Tag>8</Tag> <Monat>12</Monat> </Geburtstag>äh. es kann doch ein (mit RVO natürlich) :tag(readAttrib(in,"Tag")) sein. im binary-mode zu nollkosten weg und im normalemode auch nd im xml-mode wird halt lästig gehanselt.
-
volkard schrieb:
äh. es kann doch ein (mit RVO natürlich) :tag(readAttrib(in,"Tag")) sein. im binary-mode zu nollkosten weg und im normalemode auch nd im xml-mode wird halt lästig gehanselt.
Das würde natürlich gehen aber wieviel Informationen braucht man? Diese Frage kann man beantworten wenn man sich auf einige Formate beschränkt. Man kann aber keine allgemein gültige Antwort finden wie sie für ein System das für Ausweitbarkeit ausgelegt ist gebraucht werden würde.
Desweiteren bin ich mir sicher, dass es in sehr vielen Fällen der Programmirer von vorn herein weiß welches Format er braucht. Zum Beispiel macht es keinen Sinn 30MB mit XML auf 100MB aufzupumpen allerdings wäre es auch keine gute Idee eine Konfigurationsdatei im binär Format zu schreiben. Ich glaube, dass es sehr viel Überzeugungsarbeit braucht um den Programmirer dazu zu bewegen da etwas sinnvolles hinzu schreiben wenn er genau weiß, dass er es nicht brauchen wird. Die Idee ist natürlich gut wenn man wirklich eine Duallösung braucht.
-
Ich finde das eigentlich relativ klar. Wenn ich einigermaßen in Echtzeit Objekte über ein Socket wo hin schicken möchte, werde ich eine binäre Repräsentation nehmen. Von Menschen nicht lesbar, effizient und ein bisschen unportabel. Sachen wie eine Konfiguration sollten als XML geschrieben werden. Aus
class Configuration { public: string getName(); void setName(string); ... };Muss dann automatisch sowas wie
<Configuration> <name>Hanswurst</name> </Configuration>werden.
Wenn ich es mega-portabel und unabhängig brauche, muss ein Serializer her, der sich an ein offenes Format wie SOAP (XML mit wahnsinnig viel Zusatzinformationen erzeugt, die ich noch nicht verstanden habe) hält.
Es gibt kein besser oder schlechter, man wird doch immer klare Anforderungen an die Objektserialisierung haben und am besten, man hat für alles entsprechende Filter-Streams.
-
warum gleich xml?
<Configuration> <name>Hanswurst</name> </Configuration>naja, ich schreib und editiere leiber
Configuration { name: "Hanswurst" }
-
warum gleich xml?
<Configuration> <name>Hanswurst</name> </Configuration>naja, ich schreib und editiere leiber
Configuration { name: "Hanswurst" }Mein Kollege Klaus schreibt und ediert gerne:
configuration begin name := 'Hanswurst' endWozu braucht man schon Standards, wenn doch jeder sein eigenes Süppchen kochen kann
.
-
Der Anfang meines Beitrag sollte natürlich Volkard zitieren. Habe die Tags vergessen. Sorry.
-
kopf_schuettler schrieb:
Wozu braucht man schon Standards, wenn doch jeder sein eigenes Süppchen kochen kann
.wozu *immer* xml nehmen? mehr augenmaß bei der verwendung der werkzeuge erbitte ich. sachen dort einsetzen, wo sie sinnvoll sind, aber nur dort.