C zu C++ - Einfacherer Übergang?
-
namespace invader schrieb:
[...] Man verschwendet also Zeit, nur um sich um Sprachfeatures zu kümmern, die man gar nicht nutzen will. Im Prinzip das selbe wie die Kopierkonstruktoren und Zuweisungsoperatoren, für die man ständig Zeit opfert obwohl man sie eigentlich gar nicht braucht. [...]
Schlechtes Beispiel. Schreibst Du ständig Kopierkonstruktoren und Zuweisungsoperatoren für eigene Klassen? Ich nicht. Das macht man in der Regel nur für wenige "kleine Bausteine" wie zB std::string oder std::vector, welche in den zwei Fällen praktischerweise Teile der Standardbibliothek sind. Wenn Du eigene Klassen aus solchen Bausteinen zusammensetzt, kannst Du das mit dem Kopieren und Zuweisen komplett dem Compiler überlassen. Das ist überhaupt eine der wichtigsten Lektionen, die man bei C++ gelernt haben sollte und die bei Dir scheinbar bisher nicht angekommen ist.
-
Shade Of Mine schrieb:
Man muss nachdenken, OMG.
Das ist kein Argument. In Java muss ich dauernd finallys schreiben obwohl die mit meinem Problem nichts zu tun haben... Dieser Vorgang nennt sich nunmal "programmieren".Es ist schon ein gewaltiger Unterschied, ob ich über das eigentliche Problem oder die Sprache nachdenken muss. Das was du in finally-Blöcke schreibst, gehört ja zu deinem Problem. Dateien müssen nun mal wieder geschlossen werden (dass man dazu in Java optisch unschön geschachtelte finally-Blöcke braucht, ist eben die Eigenheit von Java, aber das ist ein anderes Thema). Wenn ich aber sinnlose Kopierkonstruktoren schreiben muss (oder zumindest private deklarieren), hat das mit meinem Problem nichts zu tun.
aber java ist nicht toll? welche sprache dann?
Na C zum Beispiel
in c++ schreibt man den code garnicht, weil er automatisch generiert wird.
...und zwar falsch, außer für einfache Klassen.
und objekte in C kopieren funktioniert ja auch automatisch. ist dann halt immer eine flache kopie.
Bei C ist das nicht weiter problematisch, weil C nicht so tut als wäre es eine High-Level-Sprache. Bei C hat man den Überblick darüber, wo Objekte kopiert werden und wo man explizit eine tiefe Kopie machen muss. Der Sinn von C++ ist ja gerade, dass solche Dinge versteckt werden, was leider nicht klappt.
Aber dieser Vergleich des impliziten Kopierens in C und C++ macht schön deutlich, wie genau das selbe Feature in C sinnvoll ist, aber in C++ störend. Das ist ja genau mein Punkt: Mehr Features führen nicht automatisch zu einer besseren Sprache; es kommt auf die Zusammenstellung der Features an.
in java muss man auch ein clone schreiben wenn es notwendig ist...
Eben: _Wenn_ es notwendig ist, also wenn mein Problem es erfordert.
wenn du nicht kopieren willst, einfach boost::nocopy und gut ist.
Immer diese blöden Workarounds bei C++...
Aber gerade C++ verleitet dazu, nicht wiederverwendbaren code zu schreiben, der auch schlecht wieder "repariert" werden kann.
das ist deine meinung. hast du dafür auch fakten?
Hab ich doch erklärt (Verzicht auf Exceptionsicherheit und Verzicht auf Kopierkonstruktoren und Zuweisungsop. obwohl "notwendig").
c++ code ist von hausaus exception sicher, es sei denn du hast einen fehler gemacht.
Unsinn. C++-Code ist nur dann automatisch exceptionsicher, wenn man (und alle Frameworks und Bibliotheken, die man verwendet) sich voll auf das RAII-Paradigma einlässt. Aber bei vielen Frameworks wurde gar nicht daran gedacht, zumal Exceptions ja erst nachträglich auf C++ draufgepappt wurden. Und C-Bibliotheken natürlich sowieso nicht (und das, obwohl doch die Kompatibilität zu C der vielgepriesene Vorteil von C++ ist
)
schreib mal bitte mein Java Beispiel mit dem 2 Dateien kopieren in C mit korrekter Fehlerbehandlung.
int cf(const char *a, const char *b) { int err = 0; FILE *src = fopen(a, "rb"); FILE *trg = fopen(b, "wb"); if (!src || !trg) { err = -1; goto end; } if (copy(trg, src) < 0) err = -2; end: if (src) fclose(src); if (trg) fclose(trg); return err; }
Und was soll daran jetzt so schlimm sein? (Und wenn man sich für fclose-Fehler interessiert, erhöht das nicht mal die Zeilenanzahl, während man in C++ ein Problem hat, wenn im Destruktor was schief geht).
Mal davon abgesehen dass du nie printf auf fehler überprüfen wirst
Kann ich aber, wenn mein Problem es erfordert, dass ich weiß ob dort ein Fehler aufgetreten ist.
Alle deine "argumente" kann ich problemlos auf diese Sprache anwenden. oder zeig mir eine sprache die nicht komplex ist aber dennoch alle tools so anbietet dass man immer gut damit arbeiten kann.
Du hast ja nicht verstanden, was ich gesagt habe. Was ich an C++ kritisiere ist nicht, dass bestimmte Dinge etwas mühsam zu machen sind (das gibt es in jeder Sprache), sondern die "Werkzeugkasten"-Philosophie von C++, die zur Folge hat dass man mehr mit der Sprache und ihren ganzen Features zu tun hat als mit dem eigentlichen Problem.
kleines beispiel: typelist
...schönes Feature für Leute, die lieber über ihre Programmiersprache nachdenken als über die Probleme, die sie eigentlich damit lösen wollen...
Gewisse Features erlauben deutlich schöneren Code. Egal wie du schöner definierst: kürzer, eleganter, schneller,... Pattern Matching in OCaml ist für gewisse Situationen einfach in jeder hinsicht besser als alles was Java bietet.
"Schönen, eleganten" Code habe ich definiert als "die vorhandenen Möglichkeiten am besten ausnutzend", und mit dieser Definition ist deine Behauptung natürlich Unsinn. Dass bestimmte Sprachen für bestimmte Probleme besser geeignet sind und dann kürzeren, verständlicheren Code erlauben, ist ja keine Frage.
schau dir zB von gmp das C und das C++ interface an. gmp ist häßlich, aber die c++ variante ist um soviele dimensionen besser zu benutzen als die C variante - und das obwohl der c++ code eher schlecht ist.
Kenn ich nicht, aber auf den ersten Blick scheint es doch eine ordentliche C-Bibliothek zu sein.
Natürlich bietet sich für so etwas Operatorüberladung an, und wenn man sie hat sieht das Programm damit "natürlicher" aus, aber letztendlich ist der Unterschied nur syntaktisch. Genauso wie ein Parser, den man in einer funktionalen Sprache mit nur einem Bruchteil der Zeilen schreiben kann im Vergleich zu z.B. Java, auch nichts anderes macht.
(das restliche Rumgetroll-Hintergrundrauschen in dem Thread ignoriere ich mal weiterhin)
-
namespace invader schrieb:
Es ist schon ein gewaltiger Unterschied, ob ich über das eigentliche Problem oder die Sprache nachdenken muss. Das was du in finally-Blöcke schreibst, gehört ja zu deinem Problem. Dateien müssen nun mal wieder geschlossen werden (dass man dazu in Java optisch unschön geschachtelte finally-Blöcke braucht, ist eben die Eigenheit von Java, aber das ist ein anderes Thema). Wenn ich aber sinnlose Kopierkonstruktoren schreiben muss (oder zumindest private deklarieren), hat das mit meinem Problem nichts zu tun.
Doch. Es is Teil des Problems zu definieren, ob ein Objekt kopierbar sein soll oder nicht, weil dies in Zukunft bestimmt wie mit dem Objekt umgegangen werden kann. Dieses Problem befindet sich aber auf Softwaretechnischer Ebene, nicht auf Programmebene.
So wie ich dich verstehe bist du für ein "opt-in" bei der Kopiersemantik, während C++ ein "opt-out" verwendet. Es ist eine Entscheidung im Sprachdesign, was verwendet wird. Für Java ist ein "opt-out" nicht sinnvoll, da aufgrund der Semantik der Referenzen in Java nicht geschlossen werden kann, wie ein Objekt überhaupt kopiert werden könnte. Dies ist in C++ wesentlich einfacher, weil externe Referenzen einen Sonderfall darstellen. In C++ ist ein "opt-in" hingegen nicht sinnvoll, weil C++ hauptsächlich auf der value-Semantik aufbaut. Und zu der gehört nunmal, dass Objekte kopiert werden können.
...und zwar falsch, außer für einfache Klassen.
Einfache Klassen sind aber der Normalfall. Die meisten Klassen verwenden einfachste Objektkomposition sowie Vererbunghierarchien. Und von den wenigen Stellen in denen Zeiger verwendet werden sind die allermeisten wieder Zeiger auf externe Objekte, weil die internen Zeiger durch die RAII-basierten Container der STL zu genüge abgedeckt werden. Also reicht auch hier meistens wenn die Addresse kopiert wird. Dass man eine DeepCopy schreiben muss ist somit unglaublich selten. Hier gilt als Faustregel: schreibst du einen dtor, dann musst du den copy-ctor mit implementieren. Und das ist wirklich nichts was man mehrmals täglich machen muss. Wesentlich öfter möchte man einfache Datenkollektionen kopieren, und für DIE jeweils einen copy-ctor zu schreiben ist übelste Frohnarbeit.
Nebenbei fällt mir neben dem Singleton kaum eine Situation ein, bei der ich sowas wie boost::nocopy bräuchte.
Unsinn. C++-Code ist nur dann automatisch exceptionsicher, wenn man (und alle Frameworks und Bibliotheken, die man verwendet) sich voll auf das RAII-Paradigma einlässt. Aber bei vielen Frameworks wurde gar nicht daran gedacht, zumal Exceptions ja erst nachträglich auf C++ draufgepappt wurden.
Dass schlecht designte und veraltete Bibliotheken ne Menge am Sprachdesign zerschießen können passiert dir in jeder Sprache. Das ist ein Nullargument. Dass man C-Code anders handlen muss als C++-Code ist offensichtlich. Wenn man will, kann man sich Wrapperklassen schreiben, meistens reichts aber, wenn man in diesen Fällen einfach sinnvoll den dtor und copy-ctor der verwendenden Klassen implementiert. Auch C++ Programmierer können pragmatisch sein
-
namespace invader schrieb:
Es ist schon ein gewaltiger Unterschied, ob ich über das eigentliche Problem oder die Sprache nachdenken muss. Das was du in finally-Blöcke schreibst, gehört ja zu deinem Problem.
Interessanter Punkt.
Es gehört nur in Java zu meinem Problem, in C++ nicht. Weil Java mich dazu zwingt es zu meinem Problem zu machen. In anderen Sprachen ist das finally nicht mein Problem.
Dateien müssen nun mal wieder geschlossen werden (dass man dazu in Java optisch unschön geschachtelte finally-Blöcke braucht, ist eben die Eigenheit von Java, aber das ist ein anderes Thema). Wenn ich aber sinnlose Kopierkonstruktoren schreiben muss (oder zumindest private deklarieren), hat das mit meinem Problem nichts zu tun.
in python und c++ ist der komplette code unnötig. dort ist es nicht mein problem
in c++ schreibt man den code garnicht, weil er automatisch generiert wird.
...und zwar falsch, außer für einfache Klassen.
dann sind deine klassen falsch.
wenn du nur korrekte klassen als member hast, dann ist der standard copy ctor einwandfrei.
und objekte in C kopieren funktioniert ja auch automatisch. ist dann halt immer eine flache kopie.
Bei C ist das nicht weiter problematisch, weil C nicht so tut als wäre es eine High-Level-Sprache. Bei C hat man den Überblick darüber, wo Objekte kopiert werden und wo man explizit eine tiefe Kopie machen muss. Der Sinn von C++ ist ja gerade, dass solche Dinge versteckt werden, was leider nicht klappt.
und deshalb sind automatische falche kopien ok? aber in c++ sind automatische tiefe kopien nicht ok?
Aber dieser Vergleich des impliziten Kopierens in C und C++ macht schön deutlich, wie genau das selbe Feature in C sinnvoll ist, aber in C++ störend. Das ist ja genau mein Punkt: Mehr Features führen nicht automatisch zu einer besseren Sprache; es kommt auf die Zusammenstellung der Features an.
ich würde sagen, dass du eine doppelmoral an den tag legst. so kann man nicht diskutieren.
in java muss man auch ein clone schreiben wenn es notwendig ist...
Eben: _Wenn_ es notwendig ist, also wenn mein Problem es erfordert.
selbes in c++. ich glaube aber dein c++ ist nicht ok. denn wenn du dauernd copy ctors implementieren musst, dann passt da was nicht.
wenn du nicht kopieren willst, einfach boost::nocopy und gut ist.
Immer diese blöden Workarounds bei C++...
wie genau verbietest du eine kopie in C?
GARNICHT.
wow. ist ja viel besser als eine zeile code.c++ code ist von hausaus exception sicher, es sei denn du hast einen fehler gemacht.
Unsinn. C++-Code ist nur dann automatisch exceptionsicher, wenn man (und alle Frameworks und Bibliotheken, die man verwendet) sich voll auf das RAII-Paradigma einlässt. Aber bei vielen Frameworks wurde gar nicht daran gedacht, zumal Exceptions ja erst nachträglich auf C++ draufgepappt wurden. Und C-Bibliotheken natürlich sowieso nicht (und das, obwohl doch die Kompatibilität zu C der vielgepriesene Vorteil von C++ ist
)
ich habe damit null probleme. weil libraries die exceptions verwenden sowieso exception sicher sind und libraries die keine exceptions verwenden auch keine gefahr laufen exceptions abzubekommen.
und c libraries sind leicht wrapbar, dank scopeguards.
aber klar, schlechte libraries sind immer wieder ein problem. aber das ist ein problem das JEDE SPRACHE hat.
int cf(const char *a, const char *b) { int err = 0; FILE *src = fopen(a, "rb"); FILE *trg = fopen(b, "wb"); if (!src || !trg) { err = -1; goto end; } if (copy(trg, src) < 0) err = -2; end: if (src) fclose(src); if (trg) fclose(trg); return err; }
und jetzt nochmal das ganze in c++
ifstream src(a); ofstream trg(b); copy(a,b);
aber gut, du hast dich wenigstens getraut die gotos zu machen, das ist immerhin schon etwas. was du natürlich vergessen hast, war ordentliches error reporting:
welche datei ist fehlgeschlagen (welcher datei name war es denn?), hat das fclose funktioniert? und du öffnest unnötigerweise die 2. datei wenn die erste nicht aufgeht.
c++ bietet alle diese von mir genannten features von hausaus.
Und was soll daran jetzt so schlimm sein? (Und wenn man sich für fclose-Fehler interessiert, erhöht das nicht mal die Zeilenanzahl, während man in C++ ein Problem hat, wenn im Destruktor was schief geht).
die probleme habe ich oben beschrieben und das dtor "problem" haben wir schon so oft besprochen... es ist als rede man gegen eine wand...
Mal davon abgesehen dass du nie printf auf fehler überprüfen wirst
Kann ich aber, wenn mein Problem es erfordert, dass ich weiß ob dort ein Fehler aufgetreten ist.
doppel moral
mal ist es gut, mal ist es schlecht dass man etwas ignorieren kann.
bei exceptions findest du es schlecht dass man exception sicherheit ignorieren kann, bei error codes ist es wieder ein vorteil...Du hast ja nicht verstanden, was ich gesagt habe. Was ich an C++ kritisiere ist nicht, dass bestimmte Dinge etwas mühsam zu machen sind (das gibt es in jeder Sprache), sondern die "Werkzeugkasten"-Philosophie von C++, die zur Folge hat dass man mehr mit der Sprache und ihren ganzen Features zu tun hat als mit dem eigentlichen Problem.
du kannst keine philosophie kritisieren die du nicht verstehst, das macht keinen sinn.
kleines beispiel: typelist
...schönes Feature für Leute, die lieber über ihre Programmiersprache nachdenken als über die Probleme, die sie eigentlich damit lösen wollen...
die alternative sind void*. das ist auch nicht wirklich das gelbe vom ei.
aber als man in java damals nur void* in collections gesteckt hat, da war das cool. dann gabs generics und plötzlich sind void* in java auch wieder schlecht.
lustig, nicht wahr? solche features sind nicht zwingend erforderlich, erleichtern das programmieren aber enorm. selbes mit typelists, weil void* einfach furchtbar ist.
Dass bestimmte Sprachen für bestimmte Probleme besser geeignet sind und dann kürzeren, verständlicheren Code erlauben, ist ja keine Frage.
und wieder doppel moral.
bestimmte sprachen sind in bestimmten situationen besser, aber die tools um besser zu sein braucht man ja nicht. wie zB typelists unnötig sind weil man ja void* hat.Natürlich bietet sich für so etwas Operatorüberladung an, und wenn man sie hat sieht das Programm damit "natürlicher" aus, aber letztendlich ist der Unterschied nur syntaktisch. Genauso wie ein Parser, den man in einer funktionalen Sprache mit nur einem Bruchteil der Zeilen schreiben kann im Vergleich zu z.B. Java, auch nichts anderes macht.
wartbarkeit, produktivität und lesbar sind also keine relevanten punkte?
das ist diese doppel moral.
du magst c++ nicht. bitte. das ist ja kein problem. ich mag zB ruby nicht so. aber deine argumente sind haarsträubend. weil du auf der einen seite die produktivität von c++ code kritisierst und auf der anderenseite dann sagst dass c++ hier nur syntax sugar bietet. aber was ist syntax sugar wenn nicht produktivität?c++ codes sind den bruchteil eines c codes lang.
das datei beispiel mal korrekt:int err=0; FILE* src=NULL; FILE* trg=NULL; if(!(src=fopen(a,"r")) { err = -1; goto end; } if(!(trg=fopen(b,"r")) { err = -2; goto end; } if(!copy(src, trg)) { err = -3; goto end; } end: if(src) { fclose(src); } if(trg) { fclose(trg); } if(err) { set_error(err); } /*errno setzen*/ return err;
nochmal dazu den c++ code:
ifstream src(a); ofstream trg(b); copy(a,b);
und jetzt denk über produktivität nach.
c++ verhindert dass man über eine menge sachen nachdenken muss, wie eben zB aufräumen, error codes oder generell fehler behandlung.im gegenzug muss man eben an exception sicherheit denken. in c und java verbringt man eine menge zeit mit aufräum arbeiten. lustig wird es nämlich wenn resourcen voneinander abhängen zB wenn man einen buffer für eine datei setzt, aber die datei nicht aufgeht. dann muss man an viele sachen denken - das ist halt programmieren.
c++ nimmt auf diesem gebiet enorm viel arbeit ab. sowas wie aufräumen gibt es in c++ nicht. dafür muss man an anderer stelle eben mehr denken. aber nicht so triviale sachen wie ob da ein copyctor her muss oder nicht. das passiert automatisch. ich habe noch nie nachgedacht ob ich einen copyctor brauche oder nicht. genausowenig wie ich nachdenke ob ich in java oder c einen copyctor brauche.
du musst dich wohl mehr mit unterschiedlichen sprachen befassen. wenn du c zB für eine tolle sprache hältst und von produktivität redest, dann passt da etwas nicht. C hat seine stärken, aber sicher nicht in der produktivität. in python oder c++ oder sogar java hat man mit viel weniger code viel mehr.
deshalb eine kleine frage: in welchen sprachen hast du denn schon programme geschrieben?
-
namespace invader schrieb:
in c++ schreibt man den code garnicht, weil er automatisch generiert wird.
...und zwar falsch, außer für einfache Klassen.
Wie schon angetönt müssen Kopierkonstruktoren etc. für ein paar wenige Verwaltungsklassen mit spezieller Kopiersemantik geschrieben werden. Der Rest (und das ist der Grossteil) funktioniert.
namespace invader schrieb:
und objekte in C kopieren funktioniert ja auch automatisch. ist dann halt immer eine flache kopie.
Bei C ist das nicht weiter problematisch
Doch, du musst nämlich ständig dran denken und Fälle unterscheiden. Manchmal reicht flache Kopie, manchmal nicht. Ausserdem gibt es keine standardisierte tiefe Kopierfunktion, bei jeder Bibliothek sehen Kopien etwas anders aus. Bei C++ hingegen hast du mit
Typ kopie = original;
jedes Objekt kopieren (sofern eine Kopie sinnvoll ist).namespace invader schrieb:
Der Sinn von C++ ist ja gerade, dass solche Dinge versteckt werden, was leider nicht klappt.
Was klappt nicht? Kopierkonstruktoren jedenfalls schon, wie erwähnt.
namespace invader schrieb:
in java muss man auch ein clone schreiben wenn es notwendig ist...
Eben: _Wenn_ es notwendig ist, also wenn mein Problem es erfordert.
Und wo ist jetzt der Nachteil, wenn einem diese Arbeit von der Programmiersprache abgenommen wird?
namespace invader schrieb:
wenn du nicht kopieren willst, einfach boost::nocopy und gut ist.
Immer diese blöden Workarounds bei C++...
Immer diese Nicht-C++ler, die alle Konzepte, welche nicht Sprachmittel sind, als Workarounds darstellen...
namespace invader schrieb:
Hab ich doch erklärt (Verzicht auf Exceptionsicherheit und Verzicht auf Kopierkonstruktoren und Zuweisungsop. obwohl "notwendig").
Es wurden aber auch entsprechende Gegenargumente gebracht.
namespace invader schrieb:
Unsinn. C++-Code ist nur dann automatisch exceptionsicher, wenn man (und alle Frameworks und Bibliotheken, die man verwendet) sich voll auf das RAII-Paradigma einlässt.
Ja, und welcher C++-Programmierer, der sich auskennt, tut das nicht?
namespace invader schrieb:
Aber bei vielen Frameworks wurde gar nicht daran gedacht, zumal Exceptions ja erst nachträglich auf C++ draufgepappt wurden.
Exceptions sind seit dem ersten Standard in C++.
namespace invader schrieb:
Und C-Bibliotheken natürlich sowieso nicht
Auch hier wurde schon ein Gegenargument gebracht. Viel mehr als eine ScopeGuard-Klasse brauchst du nicht, um C-Code exceptionsicher zu machen. Dank Templates geht das sogar sehr generisch.
namespace invader schrieb:
Mal davon abgesehen dass du nie printf auf fehler überprüfen wirst
Kann ich aber, wenn mein Problem es erfordert, dass ich weiß ob dort ein Fehler aufgetreten ist.
Tust du bestimmt nicht an allen Orten, wo Fehler auftreten können. Das würde Code durch die Abfragen und das Hochreichen viel zu hässlich machen. In C++ erfährt man von solchen Fehlern, ohne im Anwendungscode etwas zu tun.
namespace invader schrieb:
kleines beispiel: typelist
...schönes Feature für Leute, die lieber über ihre Programmiersprache nachdenken als über die Probleme, die sie eigentlich damit lösen wollen...
Du hast den Einsatzzweck von Typlisten nicht verstanden.
-
namespace invader schrieb:
aber java ist nicht toll? welche sprache dann?
Na C zum Beispiel
C ganz sicher nicht. Fehlerbehandlung ist einfach willkürlich. Dies führt sogar zu Sicherheitslücken! Wiederverwendbarkeit ist äußerst gering. Selbst in ein und dem selben Projekt ist man ständig damit beschäftigt, die selben Datenstrukturen neu zu implementieren, allein weil es keine Templates gibt. Jede Überprüfung muss explizit hingeschrieben werden, da sie nicht implizit versteckt werden können (zB Bereichsüberprüfungen). Es gibt keine Funktoren, sondern nur teure Funktionspointer, Namensbereich-Kollisionen etc. C ist wirklich nicht das gelbe vom Ei.
-
rüdiger schrieb:
namespace invader schrieb:
aber java ist nicht toll? welche sprache dann?
Na C zum Beispiel
C ganz sicher nicht.
Dieser ganze high-level Schnick-Schnack ist doch pure Zeitverschwendung. Selbst C geht da schon zu weit. Wir sollten uns wieder auf einfachere Dinge konzentrieren. Turingmaschinen! Und lasst uns auch all das ignorieren, was die C++ Spezis von sich geben. Ist doch alles nur erlogen und erstunken. Da ist eine riesige Verschwörung im Gange. Selbst Herr Stroustrup hat es einmal zugegeben.
rüdiger schrieb:
[...] Selbst in ein und dem selben Projekt ist man ständig damit beschäftigt, die selben Datenstrukturen neu zu implementieren, allein weil es keine Templates gibt [...]
Du bist viel zu verwöhnt von dem high-level Schnick-Schnack, der Typsicherheit, Generizität, etc. Der gemeine C-Programmierer hat eine ganz andere Ekelgrenze. Die scheuen nicht vor Zeiger-Arithmetik-Hacks und void*-Orgien zurück. Das ist echte Generizität. Wo Programmierer noch Programmierer sind und keine Weicheier. Ganz "ohne Sicherheitsgurt" und ohne Compilerunterstützung geht das auch -- man muss nur cool genug dafür sein -- oder eben keine Ahnung von C++ haben ... :p
-
krümelkacker schrieb:
[...] Selbst Herr Stroustrup hat es einmal zugegeben. [...]
Upps! Das war der falsche Link. Der hier ist richtig.
-
krümelkacker schrieb:
Die scheuen nicht vor Zeiger-Arithmetik-Hacks und void*-Orgien zurück. Das ist echte Generizität.
Absolut und da sagt noch jemand angesichts derartiger Konstrukte:
typedef unsigned (*PROCESS)(void*, unsigned); unsigned CalcNextStep(void* setOfFormulas, unsigned curTime){ } unsigned CursorMove(void* cursorData, unsigned curTime){ } unsigned UpdateTable(void* tableTarget, unsigned curTime){ } PROCESS TodoList[8] = {CalcNextStep, CursorMove, UpdateTable};
in C gäbe es weder Funktoren noch Generizität. Einige Aufgaben sind überhaupt nur mit C-Mitteln lösbar:
Mit der oben skizzierten Schablone lassen sich z.B. recht elegant flexible TODO-Listen/Stacks für synchron und automatisiert ablaufende Prozesse (von beliebiger Lebensdauer) basteln, von deren Terminierung (die nach n-maliger Zuteilung eines Scheibchens Rechenzeit eintritt) auch die eventuelle Aufnahme(Löschung) weiterer Prozesse in die (aus der) TODO-Liste abhängen kann),
ein Beispiel für eine Problemstellung, die jetzt vielleicht nicht alltäglich ist, aber die mit C++ Mitteln vermutlich überhaupt nicht lösbar wäre. D.h. solange man nicht gewillt ist, als C++ Programmierer auf C-Funktoren zurückzugreifen.
-
keinFunktor schrieb:
[...] aber die mit C++ Mitteln vermutlich überhaupt nicht lösbar wäre. [...]
Vermutlich hast Du völlig recht. Ich denke nicht, dass man dazu C++ kennen muss, um das beurteilen zu können. Es reicht völlig daran zu glauben. Wenn nur genug Leute daran glaube, wird es auch wahr.
-
keinFunktor schrieb:
ein Beispiel für eine Problemstellung, die jetzt vielleicht nicht alltäglich ist, aber die mit C++ Mitteln vermutlich überhaupt nicht lösbar wäre. D.h. solange man nicht gewillt ist, als C++ Programmierer auf C-Funktoren zurückzugreifen.
Erstens ist obige Lösung auch C++. Es wäre töricht, ein neues C++ nehmen zu wollen, was auf alle Mittel verzichtet, die es auch in C gibt. Sollen wir auf
int
verzichten? Zweites haben wir mit einer Job-Liste voller Job* mit virtual void process() auch alle Möglichkeiten. Man nimmr mal dieses und mal jenes. Wenn zu den Funktionsaufrufen auch noch Übergabeparameter gebunden werden sollen, sind Objekte nicht extrem unpassend. Das Gericht wird gerne mit Templates serviert.
-
keinFunktor schrieb:
ein Beispiel für eine Problemstellung, die jetzt vielleicht nicht alltäglich ist, aber die mit C++ Mitteln vermutlich überhaupt nicht lösbar wäre. D.h. solange man nicht gewillt ist, als C++ Programmierer auf C-Funktoren zurückzugreifen.
funktionszeiger != funktor
aber davon abgesehen, solche state tabellen sind eine normale lösung.ich habe zB eine anwendung die über die serielle schnittstelle kommuniziert und das protokoll ist in einer state table abgebildet:
state_table[] = { { CURRENT_STATE, DATA, NEW_STATE, ACTION } };
current_state definiert den aktuellen status, data die art der aktuellen anfrage, new_state ist der neue status und action ein functor der ausgeführt wird um die anfrage zu beantworten.
uU könnte man das auch irgendwie anders machen: aber wozu? diese state tabellen sind 1:1 aus der doku übernommen.
-
Shade Of Mine schrieb:
c++ codes sind den bruchteil eines c codes lang.
das datei beispiel mal korrekt:int err=0; FILE* src=NULL; FILE* trg=NULL; if(!(src=fopen(a,"r")) { err = -1; goto end; } if(!(trg=fopen(b,"r")) { err = -2; goto end; } if(!copy(src, trg)) { err = -3; goto end; } end: if(src) { fclose(src); } if(trg) { fclose(trg); } if(err) { set_error(err); } /*errno setzen*/ return err;
nochmal dazu den c++ code:
ifstream src(a); ofstream trg(b); copy(a,b);
Die Beispiele machen komplett unterschiedliche Sachen. Wo ist die Fehlerbehandlung im C++ Code? ofstream trg(b) erstellt z.B. immer eine neue Datei, auch wenn ifstream src(a) nicht funktioniert hat. Fail.
-
das ist was anderes schrieb:
Shade Of Mine schrieb:
c++ codes sind den bruchteil eines c codes lang.
das datei beispiel mal korrekt:int err=0; FILE* src=NULL; FILE* trg=NULL; if(!(src=fopen(a,"r")) { err = -1; goto end; } if(!(trg=fopen(b,"r")) { err = -2; goto end; } if(!copy(src, trg)) { err = -3; goto end; } end: if(src) { fclose(src); } if(trg) { fclose(trg); } if(err) { set_error(err); } /*errno setzen*/ return err;
nochmal dazu den c++ code:
ifstream src(a); ofstream trg(b); copy(a,b);
Die Beispiele machen komplett unterschiedliche Sachen. Wo ist die Fehlerbehandlung im C++ Code? ofstream trg(b) erstellt z.B. immer eine neue Datei, auch wenn ifstream src(a) nicht funktioniert hat. Fail.
ja, das war ziemlich gemogelt. Hier ist ein unnützes beispiel, wie es anders ginge: http://www.cplusplus.com/reference/iostream/ios/exceptions/
stattdessen würde man es wohl so schreiben müssen:
ifstream src (a); if (!src) throw file_error() << errinfo_file_name (a) << errinfo_errno(); ...
-
Dafür lässt sich leicht etwas bauen:
template<typename Stream, std::ios_base::openmode DefMode> class ExceptionStreamAdapter : public Stream { public: ExceptionStreamAdapter( ){ } explicit ExceptionStreamAdapter ( char const * filename , std::ios_base::openmode mode = DefMode) { exceptions ( std::fstream::eofbit | std::fstream::failbit | std::fstream::badbit ); open(filename, mode); } }; typedef ExceptionStreamAdapter<std::fstream, std::ios_base::in> eifstream; typedef ExceptionStreamAdapter<std::fstream, std::ios_base::out> eofstream; //... try { eifstream reader("src.dat"); oifstream writer("tgt.dat"); copy(reader, writer); } catch(std::exception const & e) { std::cerr << e.what() << '\n'; }
Auf den ersten Blick hat man bei beiden Lösungen (C und C++) vielleicht etwa gleich viel Code. Den Aufwand für den Adapter hat man jedoch nur einmal. Ressourcenfreigabe im Fehlerfall bekommt man quasi freihaus. In C muss man für jeden Anwendungsfall alles neu schreiben, je nachdem in welcher Reihenfolge irgendwelche Ressourcen angefordert werden und was mit den Ressourcen passieren soll. Soviel erstmal zum Thema Wiederverwendbarkeit.
Außerdem lassen sich die davon instanziierten Objekte an jeder Stelle verwenden, an der auch andere Streamobjekte verwendet werden dürfen.
Zu guter Letzt sorgen Exceptions und RAII dafür, dass man sich um solche Dinge wie Ressourcenfreigabe nicht mehr kümmern muss.
Aber was rede ich: Die Wissenden wissen das eh schon, und die Indoktrinierten interessieren sich eh nicht für logische Argumente.
-
Da ist schon vieles sinnvolles und auch einiges weniger sinnvolle gesagt.
Du kommst von C und möchtest C++ ergänzen. Genau das war die Fragestellung für alle C-Programmierer als C++ als Erweiterung hinzu kam. Der Umstieg ging leicht,
weil alles bisherige behalten und man sich mit den neuen Dingen vertraut machen konnte. Hier ein Danke an die Entwickler von C++!Vorschläge zum Übergang:
1. Die Benutzung der fertigen Stream-Klassen kann schon mal überzeugen.
2. Die Erweiterung von struct nach class ansehen, eigene Klassen schreiben.
3. Überlagerung von Funktionen und Operatoren ansehen.Damit hast du bereits einen guten Einstieg nach C++. Für mehr bleibt alles offen. Du findest Geschmack und möchtest das mehr vielleicht auch einsetzen.
Leider ist das Für und Wieder von C und C++ hier im Forum ein heisses - oft kontrovers - geführtes Thema. Bitte nicht wieder eine fast religiös erscheinende Auseinandersetzung - hatten wir schon genug!
Mache es einfach wie ich und andere das auch gemacht haben. Dein C-Wissen kann nie schaden oder gar hinderlich sein! :p
-
berniebutt schrieb:
Vorschläge zum Übergang:
1. Die Benutzung der fertigen Stream-Klassen kann schon mal überzeugen.
2. Die Erweiterung von struct nach class ansehen, eigene Klassen schreiben.
3. Überlagerung von Funktionen und Operatoren ansehen.4. C vergessen.
Es gibt nichts schlimmeres als einen Mix von C und C++.
-
Tachyon schrieb:
Auf den ersten Blick hat man bei beiden Lösungen (C und C++) vielleicht etwa gleich viel Code. Den Aufwand für den Adapter hat man jedoch nur einmal.
Er bedeutet aber noch mehr Code Obfuscation. Denn jemand, der den Code verstehen oder reparieren will, wird erstmal dazu gezwungen, nachzugucken was die beiden typedefs sollen und dann die Klasse zu verstehen, und dann auch noch zu verstehen, was das Setzen der exception-Bits dann später im Rest des Codes bewirkt. Die einfache Logik, die im C-Programm sofort nachvollziehbar ist, ist bei der c++-Fassung weit verteilt und versteckt. In der Regel ist die Zeit, die zum Eintippen eines Stücks Code benötigt wird, nur ein Bruchteil der Zeit, die später zum Lesen, Verstehen, Debuggen und Ändern aufgebracht wird. Und da sind die von C++-Fanatikern propagierte Template-Orgien kontraproduktiv.
Abgesehen davon ist ein ein Armutszeugnis für C++, dass man trotz des gewaltigen Sprachumfangs trotzdem noch ständig einen Haufen Wrapper schreiben soll. Und das nicht nur für "alte" C-Bibliotheken, sondern auch für die STL selbst!
Und wieso lässt du eigentlich bei eof eine Exception werfen? Das mag sinnvoll sein, wenn man einen bestimmten Dateiinhalt erwarten kann, aber in dem Fall hier (Dateien Kopieren) führt es im besten Fall dazu, dass jedes mal mit einer ineffizienten Exception abgebrochen wird, im schlimmsten Fall wird das Ende der Datei unterschlagen.
Das könnte man natürlich leicht reparieren, aber dieser Fehler macht ein grundlegendes Problem unnötiger, selbstgebastelter Abstraktionen deutlich: Sie passen nicht so einfach auf alle Fälle. Und wenn man sie falsch verwendet, sind sie oft wesentlich schwerer zu debuggen. Soviel zum Thema Wiederverwendbarkeit.
Aber ich sag jetzt besser nichts mehr dazu, denn das bringt ohnehin nichts, solange die C++-Gläubigen hier nur am Thema vorbeidiskutieren, Argumente nicht verstehen und mit Unkenntnis- und Doppelmoral-Vorwürfen oder sonstwas um sich werfen wollen (oder einfach nur rumtrollen).
-
namespace invader schrieb:
Aber ich sag jetzt besser nichts mehr dazu
Das glaube ich nicht.
-
namespace invader schrieb:
[...]Und wieso lässt du eigentlich bei eof eine Exception werfen? Das mag sinnvoll sein, wenn man einen bestimmten Dateiinhalt erwarten kann, aber in dem Fall hier (Dateien Kopieren) führt es im besten Fall dazu, dass jedes mal mit einer ineffizienten Exception abgebrochen wird, im schlimmsten Fall wird das Ende der Datei unterschlagen.[...]
Eigentlich zeigt das nur, dass Du, wie ich bereits andeutete, keine Ahnung hast, wovon zu redest.
namespace invader schrieb:
[...schwall...]Aber ich sag jetzt besser nichts mehr dazu, denn das bringt ohnehin nichts, solange die C++-Gläubigen hier nur am Thema vorbeidiskutieren, Argumente nicht verstehen und mit Unkenntnis- und Doppelmoral-Vorwürfen oder sonstwas um sich werfen wollen (oder einfach nur rumtrollen).
Ist schon klar. Dein erster Post in diesem Thread zeigt ja schon, dass Du im höchsten Maße objektiv und unvoreingenommen bist. Auch wertende Aussagen benutzt Du so gut wie gar nicht.
Auch weiß natürlich nur ein C-Programmierer, was die Standardbibliothek tut. Ein C++-Programmierer hat hingegen keine Ahnung. Deshalb weiss er auch nicht, was bei den Standard-Streams die Exceptionbits bewirken.
Wie gut, dass Du ganz ohne Doppelmoral auskommst.
Es ist natürlich auch eine unglaubliche Obfuscation, ein Stück Code zu schreiben das überall wiederverwendet werden kann. Auch die Möglichkeit, Fehler immer auf die selbe Art behandeln zu können, erhöht die Unübersichtlichkeit nur.
Besser und klarer ist es natürlich, an möglichst vielen Stellen im Code eigene Fehlerbehandlungsroutinen zu schreiben. Noch besser mit möglichst viel Codewiederholung.Auch schmeißen die Firmen gerne Unmengen an Geld mit unproduktivem C++-Code raus. Es geht ja schließlich nicht darum, möglichst kostengünstig Software zu produzieren, sondern nur darum, möglichst "kuhl" und "obfuscated" zu sein.