1. Diese Seite verwendet Cookies. Wenn du dich weiterhin auf dieser Seite aufhältst, akzeptierst du unseren Einsatz von Cookies. Weitere Informationen

C++ im Wandel

Dieses Thema im Forum "Coders Talk" wurde erstellt von sheel, 5. April 2018.

  1. sheel

    sheel I love Asm Administrator

    Mal etwas Offtopic von mir...

    was halten die C++ - Kenner hier von der Richtung, in die die Sprache entwickelt wird?

    ...

    C++11 war ja im Großen und Ganzen wirklich nett. Aber imho bringt jede neue Standardversion (14, 17, das kommende 20...) mehr und mehr Unsinn - und es könnte ruhig mal wieder aufgehört werden, an der Sprache herumzudoktern.

    Zwei grandiose und komplett unnötige "Fails", die mir gerade einfallen, wären:
    a) Der relativ neue Variablentyp "byte". Ist es ein Byte groß und speichert Werte von 0 bis 255? Nein, in C++ ist beides anders.
    b) Es ist eine Änderung geplant, dass (unter bisher noch unbekannten Bedingungen) 4294967295 "kleiner" als 0 sein kann. Und zwar gerade "nicht" weil irgendwas zu signed konvertiert wird. Wtf.

    ...

    Und da ist natürlich auch noch das anscheinend unwichtige Problem, dass "C++ lernen" immer mehr zur Lebensaufgabe wird :D

    Von riesigen Umbauten wie Modulen fang ich wohl besser nicht an.
     
    Zuletzt bearbeitet: 5. April 2018
  2. cwriter

    cwriter Erfahrenes Mitglied

    Es ist ein zweischneidiges Schwert. Manche Dinge, die in Entwicklung sind, wären echt nett, z.B. transactional memory
    Anderes wiederum ist völlig sinnbefreit (std::iota, std::byte).
    Hö? Erinnert mich an die Leute, die sagen, dass abs() eine schlechte Funktion ist, weil int abs(int i) { return -i; } bei INT_MIN einen Overflow hat. Dass man das mit einem Rückgabewert von unsigned int beheben kann, ist da egal.
    Falls die Idee ist, -1 zu fangen: Dafür gibt es 0xffffffff.

    Ich denke, kein C++-Programmierer nutzt alles existente C++, aber ja: Wenn man Code lesen will, kommt man sehr schnell an noch nie Gesehenes. Hach, die unschuldige Einfachheit von C++89.

    Ich sehe eher ein Grundsatzproblem: C++ ist wirklich gross geworden. So gross, dass man es nicht für Low-Level einsetzen kann (neben anderen Problemen wie RTTI). Der Standard umfasst sehr viel nutzloses Zeug (wie std::byte), statt die Wurzel des Problems zu packen und z.B. Bit-Order festlegen zu können.

    Ich denke nicht, dass C++ zu einer schlechteren Sprache wird. Ich denke aber, dass dem Komitee nicht ganz klar zu sein scheint, in welche Richtung sie gehen wollen: Universalsprache oder Performancesprache.
    Grundsätzlich halte ich es mit neuen Features immer so: Sie sind gut, kann man sie benutzen (wie z.B. [[nodiscard]]). Sind sie sinnlos/schlecht, benutzt man halt etwas anderes.
    Nur einen C++-Compiler mit STL und Co. würde ich heutzutage nicht mehr selbst machen wollen. :p

    Ich hoffe, die kommen nicht. Die Cyclic Inheritence ist bei C++ ja noch schlimmer, da auch die Member nicht zyklisch sein dürfen. Das momentane System hat zwar durchaus Macken, aber es zwingt einen zu einigermassen sauberem Code.

    cwriter
     
    sheel gefällt das.
  3. Technipion

    Technipion Erfahrenes Mitglied

    Meine Meinung dazu ist leider auch noch nicht so ganz gefestigt. Ich habe beruflich viel mit der Datenverarbeitung in der Teilchenphysik zu tun, und dort kommen vor allem C, C++ und Python zum Einsatz. In letzter Zeit auch immer öfter Java. C ist imho DIE vielseitigste Sprache, die es im Moment gibt. Ausreichend low-level, ausreichend high-level, aber nicht zu kompliziert zu lesen und ziemlich leichtgewichtig. Aber natürlich hat jede Sprache ihre Anwendungen. Ich möchte auch Python nicht mehr missen müssen.
    Von C++ bin ich schon sehr lange ein großer Fan, für mich war es immer die "Erweiterung" von C (liegt ja gewisserweise im Namen). Trotzdem ist viel von dem alten Kram bis heute noch in C. Teilweise meckern die Distros wenn man nur ein int im Kopf einer for-Schleife definieren will :p
    Als dann C++11 rauskam war ich ziemlich happy darüber und habe mich auf die neuen Features gefreut. Auch wenn es ewig gedauert hat bis ich das doofe auto endlich gecheckt hatte. Aber der wichtigste Punkt war: Es tut sich noch was. Die Sprache lebt noch, und jemand arbeitet aktiv daran. Und naja, es wurde ehrlichgesagt auch Zeit. Also C++11 habe ich absolut begrüßt, und sehr gefallen hat mir die (nahezu) vollständige abwärtskompatibilität. (Sehr wichtig! Müssen sie unbedingt weiterhin einhalten!)
    Naja dann kam C++14 und habe mir so gedacht: Komm, das waren viele Änderungen auf einmal, und die wollen jetzt bloß noch etwas nachbessern... was soll's. Aber mittlerweile denke ich auch das Komitee will jetzt irgendwas überkompensieren. Alle 3 Jahre ist einfach zu oft. Vielleicht hätte man alle 5 oder 8 Jahre anstreben sollen.

    Obwohl wir alle dazu angehalten sind das "neue" C++11 zu benutzen, sind die meisten Quellcodes bunt durcheinander gewürfelt. Manche nutzen auto, manche nicht. Manche benutzen Verschiebesemantik, andere nicht. Und es gibt so viele Stile wie es Programmierer gibt (und gab). Richtiger Spaghetticode teilweise.

    Manchmal frage ich mich, ob das "neue" C++ (ist ja nicht neu, ist der de-facto Standard) nicht vielleicht im Geheimen den Konkurrenten (Java, Python, etc.) ein kleinwenig nacheifert. Wäre meines Erachtens schade. Auf jeden Fall müsste man eine klare Linie fahren.

    Gruß Technipion
     
    sheel gefällt das.
  4. sheel

    sheel I love Asm Administrator

    Danke schonmal für die Antworten ... :)

    Ja, es hat mit Underflow zu tun, mehr Details zu dem angeblichen Problem weiß ich aber auch nicht (und nein, eigentlich ist 0xffffffff nicht portabel :p)
    Nur scheint das Kommitee zu vergessen, dass C++ nicht Mathematik ist. Variablen haben eben Eigenschaften aufgrund ihrer Größe usw., C++ hat genaue Regeln zu impliziten Umwandlungen bei Operationen mit verschiedenen Typen usw., und die ganzen bisherigen Regeln mit einer Ausnahme für irgendeinen seltsamen Spezialfall zu durchbrechen ist ... :(
    Wie auch :D

    Was irgendwelche Microcontroller angeht war aber das erste C++ auch schon ein großer Schritt weg. C kann man sehr "verbiegen" - C++ hat aber ein paar Garantien im Standard die sich auch vielen Geräten einfach nicht erfüllen lassen.
    Btw., es gibt Bestrebungen in Richtung von vollen Compile-time Reflections (und evt. später auch für dynamischere Sachen Richtung virtual-Auflösung in Klassen usw.)
    Klingt derzeit nach einer von den besseren Änderungen (sehr sogar) - solang dafür nichts anderes grob kaputtgemacht wird.

    Jedes Mitglied in eine andere :D

    Zum Glück wird meistens hoher Wert auf Abwärtskompatibilität gelegt, ja ...

    Leider bin ich mir ziemlich sicher, dass das keiner mehr aufhalten kann. Auch wenn es ziemlichen Widerstand dagegen gibt (speziell außerhalb vom Komittee).
    ... aber, wie schon im vorderen Punkt, keiner zwingt einen zum Verwenden.
    Und beim Vergleichen vom Aufwand (Einlernen, Tools, Code) mit dem Nutzen ist die Möglichkeit jedenfalls da, dass das eine Totgeburt wird.Btw., federführend ist ein MS-Mitarbeiter. Natürlich. :D

    Warum :D

    Sicher gibt es manche "Blasen", wo man mit einer anderen Meinung virtull totgeprügelt wird, aber sonst ...
    Und bisher hat noch keines der nennenswerten neuen C++-Features irgendein anderes "vollständig" ersetzt. Es gibt immer Fälle, wo das alte besser (oder sogar das einzig richtige) ist. Und natürlich spielt auch der Stil eine große Rolle...

    ...und manche benutzen es nur dann, wenn es etwas einfacher/schneller/etc. macht. Imho ist genau das der Sinn von einem Werkzeug, und nicht die krampfhafte Verwendung wo immer es auch geht, auch wenn dadurch alles umständlicher wird.

    Naja, alle großen Sprachen beeinflussen sich irgendwie; das war schon immer so. Und persönlich seh ich auch kein Problem darin, die guten Teile von anderen Sprachen als Anregung zu nehmen wie man das eigene Ding machen könnte.
     
    Technipion gefällt das.
  5. cwriter

    cwriter Erfahrenes Mitglied

    Dann geht halt sowas wie INT_MAX/LONG_MAX und co. (Wobei: Zeig mir ein modernes System, das nicht auf two's-complement basiert...).
    Das Problem greift aber auch tiefer: CPUs werden nicht plötzlich beginnen, ihre Definition zu ändern. Die Sign/Overflow/Carry-Flags werden ja nach anderen Regeln gesetzt.

    Ja, aber manchmal vermisse ich auf Lowlevel echt Klassen oder zumindest namespaces. Alles mit defines vollzupflastern ist mir ein bisschen zu mühsam, und enums übernehmen ja auch den Elternscope. Mehr bräuchte es nicht, aber wenn man sich einmal durch einige Kerneldriver liest, bestehen die ersten 300 Zeilen aus defines, von denen man nicht genau weiss, zu welchem Registerfile sie jetzt gehören, dann wird immer mit Offsets gearbeitet (Registerfile_base + register_offset) usw. Und warum? Weil der C-Standard nicht stabil genug ist, um z.B. bitfields und Konsorten für so etwas verwenden zu können.
    Sowas wie das hier wäre echt etwas nettes: https://www.systems.ethz.ch/sites/default/files/file/aos2012/Reading/week3/TN-002-Mackerel.pdf
    Aber das kommt wohl nie in den Standard.

    Das wäre in der Tat sehr interessant.
    Herb Sutter oder Lavadej(?, finde ihn gerade nicht) ? Wobei, wenn VS das standardmässig einführt, dann wird es ziemlich sicher ein Standard, zumindest unter Windows.

    Ist zwar schon etwas länger her, aber: export (bzw. die Entfernung davon) :p

    Auto kann beissen. Wenn ich in irgendeiner Form auf den Typ angewiesen bin, dann nehme ich den expliziten Typ. Aber auto ist extrem praktisch bei Templates. Manchmal spart man sich damit schon 20 Zeichen lange Typen.


    cwriter
     
    Technipion und sheel gefällt das.
  6. sheel

    sheel I love Asm Administrator

    Gabriel Dos Reis
     
    Technipion und cwriter gefällt das.
  7. Technipion

    Technipion Erfahrenes Mitglied

    Ich glaube leider nicht, dass sich das jemals wieder ändern wird. Die Industrie hat ja überall ihre Finger drin. Das ist prinzipiell nichts schlechtes, solange der Herr MS Entwickler auch wirklich an einem Standard interessiert ist, und keinen Alleingang unternimmt (jopp, ich bin vertraut mit der Vergangenheit).

    Was z.B. die Geschichte mit auto angeht, da ziehe ich jetzt ganz dreist das Buch "Effective Modern C++" von Scott Meyers heran. Auf Seite 38 schreibt er: "auto variables have their type deduced from their initializer, so they must be initialized." Ist nur eines von vielen Beispielen, aber es gibt zumindest Motivatoren auto zu benutzen. Das macht natürlich "altes" C++ nicht ungültig, es ist lediglich eine neue Art Stil. Und wie schon gesagt, es gibt wohl so viele Stile wie es Programmierer gibt. Wir sind jedenfalls dazu angehalten. Unterm Strich ist es besser es gibt in einem größeren Projekt Vorschriften (auch wenn sie eigenartig sind), als dass jeder macht was er will. Meiner Hand sind deshalb schon solche Zeilen entsprungen:
    Code (C++):
    1. auto i = (int)0;
    Man kann sich natürlich darüber streiten...
    Wo auto definitiv ein Vorteil ist, ist bei den berüchtigten foreach-Schleifen (mittlerweile werden sie range-based for loops genannt, am Anfang hießen sie aber glaube ich alle noch foreach):
    Code (C++):
    1. langer_und_ermüdender_typ a;
    2. // fülle a
    3.  
    4. for (const auto& i : a) {
    5.     // benutze i
    6. }
    Ich hatte leider noch keine Zeit mich richtig in C++17 einzulesen (chronische Zeitknappheit :D), aber ich habe aufgeschnappt, dass da sogar folgendes gehen soll:
    Code (C++):
    1. std::map<foo, bar> test = { /* Zeugs */ };
    2.  
    3. for (const auto& [key, value] : test) {
    4.     std::cout << key << " = " << value << std::endl;
    5. }
    Damit sind wir dann ja schon verdammt nahe an der pythonischen Variante dran:
    Code (Python):
    1. a_dict = {'a': 1, 'b': 2, 'c': 3}
    2.  
    3. for key, value in a_dict.items():
    4.     print(key, '=', value)
    Wie ich schon sagte, ich begrüße es sehr dass aktiv an der Sprache entwickelt wird. Und obiger Code ist natürlich in C++ enorm (!!) schneller als in Python, dem Compiler macht niemand was vor. Und prinzipell finde ich es auch gut, dass das Komitee ständig nach neuen Funktionen sucht um die Sprache zu ergänzen - SOLANGE sie abwärtskompatibel bleiben. Ich glaube die Diskussion läuft eigentlich eher auf die Frage raus: Muss die Sprache alle 3 Jahre erneuert werden? Bzw. muss man dann mit Gewalt alle neuen Features (teilweise noch unausgereift) reindrücken?

    Wie ich schon sagte, ich glaube das Komitee eifert dann ein wenig Java/Python/etc. nach. In Python z.B. kommen sehr regelmäßig neue Versionen heraus (in Python3, Python2 wird nur zur Abwärtskompatibilität behalten - sehr lobenswert). Ist ja nichts schlechtes, aber braucht C++ das?

    Und warum hauen die soetwas wie std::byte rein? Es wirkt einfach nicht komplett durchdacht...
    Unser einziger Trost ist wohl, dass die Einführung eines std::byte nichts kaputt macht. Und imho braucht das Komitee nicht auf Teufel komm raus einen neuen Standard verabschieden, die Konkurrenz ist auch nicht schneller. Man bedenke nur, dass Java erst in Version 8 ein switch-Statement erhalten hat.
    Wie wir alle wissen, bleiben die meisten Menschen bei der Erlernung von C++ spätestens bei Templates hängen, und kommen gar nicht erst zu auto und co. Von daher finde ich es viel wichtiger den Neulingen die Konzepte von Clean Code beizubringen, als ihnen unbedingt C++17 Features einzutrichtern. Und "klassisches" C++ ist nicht schlechter, wenn es beherrscht wird.

    Ich freue mich aber immer über eine Diskussion über die Sprache, nur so entwickelt sie sich schließlich weiter!

    Gruß Technipion
     
    cwriter gefällt das.
  8. sheel

    sheel I love Asm Administrator

    Ich will ja auch nicht sagen, dass es schlecht ist - aber ja, eben keine MS-Features im IE6-Style (nur ein Beispiel von vielen)
    So wirklich solide, wie ich es gern hätte, ging auch bei mir noch nicht :D
    Ja
    Was da zB. indirekt sichtbar ist, was in C++ derzeit nicht möglich ist (denk ich zumindest): Strukturen/Klassen mit mehreren Memberwerten zu initialisieren und dabei immer den Membernamen anzugeben (statt ein paar Werte einfach so hinschreiben und "raten" ob die Reihenfolge passt).

    So wie zB. vector<bool> und ziemlich viel anderes... :rolleyes:

    Speziell Byte macht im Kontext vom C++-Standard ja eigentlich sehr viel Sinn - nur muss das Komitee sich endlich mal kollektiv klar werden, dass 99.9999% der Programmierer was anderes unter Byte verstehen.
    Für letzteres ist eine neue Arbeitsgruppe im Komittee geplant :D (oder vielleicht gibts es sie ja inzwischen schon...)
     
  9. cwriter

    cwriter Erfahrenes Mitglied

    Ich führe die Diskussion mal weiter, da ich das einfach loswerden muss:

    Ich habe mittlerweile Qt Creator 4.6rc installiert, wo mit Clang5.1 ein "besseres" Code Model vorhanden ist. "Besser", weil es 1.5GB RAM frisst (1GB mehr als mit dem klassischen Modell) und jede Code Completion einige Sekunden geht und einen Kern voll auslastet. Naja, ist ja alles noch Beta(TM).
    Jedenfalls gibt es jetzt so nette Codestyle-Warnings:
    upload_2018-4-21_12-33-15.png
    (m_serial_monitor ist ein std::unique_ptr).

    Hier sehe ich mittlerweile das grösste Problem von C++: Je neuer der Standard, desto mehr wird die Herkunft geleugnet. Ich habe echt nichts gegen Syntaxerweiterungen, die Boilerplate wegnehmen, wie die foreach-Loops.
    Aber irgendwie wird krampfhaft versucht, Pointer zu verstecken, eigentlich beginnend schon mit Referenzen in C++98(?), dann weiterführend mit auto_ptr, shared_ptr, unique_ptr, etc.
    Ich finde diese Hilfen sehr nett, gerade durch RAII kann man sich viele Kopfschmerzen ersparen, aber den Grundsatz "never use new/delete", der u.a. auf SO so dermassen beliebt ist, ist doch irgendwie falsch.

    Das artet dann in solchen Artikeln aus: https://www.quora.com/Why-is-C++-considered-a-bad-language
    (Wobei der nette Mensch vergisst, dass GCs dafür einfach irgendwann mal einspringen, und der Optimizer ziemlich gut ist für Function calls).

    Wie schon gesagt, was gebraucht wird, ist ein C, das syntaktisch einfacher/kürzer ist, namespacing/(Classes) besitzt, und eine Art Smartpointer hat, um Leaks zu vermeiden. Dazu noch ein bisschen Würze wie Typesafety, meinetwegen auch Templates. Das wäre dann ein C++. Exceptions meide ich persönlich wie die Pest, da es in der Regel zu sehr schlimmen Code führt (try-catches als control flow oder für Rückgabewerte (schauder)). CryptoPP spielt in dem Gruselkabinett recht weit vorne mit, mit Exceptions für falsche Schlüssel, self-deleting Parameter Pointers etc.

    An unserer Uni hatten wir kürzlich eine Code Review für Java-Code. Codefragmente, die mehr als 20 Zeilen lang waren, wurden als schlecht erachtet, da man mehr in Funktionen auslagern sollte. OOP wird nicht mehr als Ausweg für zu unübersichtlichen Code, sondern als Standard gelesen.
    Jedes mal, wenn ich sage, meine Klassen haben etwa 50 Funktionen und manche Funktionen sind 200 Zeilen lang heisst es, dass das schlechter Stil ist. Wenn ich in einer Gruppenarbeit eine Referenz auf ein Objekt übergebe, das eine State-Variable hat, die dann innerhalb der Funktionen verändert wird, kommt jemand daher und refactort das Ganze, sodass bei jedem Iterationsschritt (State-Change) ein Objekt erstellt wird, das den letzten State und den nächsten State hält, das dann als State übergeben wird, da das ja eher Funktional und daher besser sei. Da die Klasse aber auch den Kontext hielt, wird die Referenz dann einfach per Konstruktor an den Arbeiter übergeben. Somit macht man dasselbe quasi doppelt, kopiert sich und den Speicher zu Tode, aber es sieht funktionaler aus! Ziel erreicht, Performance ist ja eh egal.

    Wenn ich aber jeweils die privaten Projekte vergleiche, machen meine Projekte echte Arbeit (CPU-Emulation, Logik-Emulation) in einem annehmbaren Zeitrahmen (1 durchschnittliche ARMv6-Instruktion auf einem 7500U @ 2.7GHz in ~200ns, keine besonderen Performancemassnahmen), während die Fans von OOP und 2-Zeilen-Funktionen einfach ein paar Standardfunktionen zusammenkleben und Bilder ausgeben oder aus Prinzip mit JSON arbeiten.
    Ich will damit nicht sagen, dass ein aufgeräumter Stil schlecht ist, sondern, dass es auf den Anwendungsbereich ankommt. Man kann keine komplexen Bitoperationen in 5 Zeilen abschliessen; man kann nicht SmartPointer verwenden, wenn man innerhalb eines Scratch Buffers arbeiten will, etc. Wenn man aber ein PoC machen will, dann ist JSON legitim. Für Apps am Mobilnetz ist es mittlerweile Standard, danke auch für den Datenverbrauch.
    Aber irgendwie ist dieser "Nutz doch die riesige Standardbibliothek für alles" aus Java, C# und mittlerweile Rust ein erklärtes Ziel von C++.
    Und genau das, denke ich, ist das Problem des Komitees. Statt die Stärken von C zu betonen und die Schwächen auszumerzen, will man unbedingt modern sein und alles haben, was andere auch haben, ungeachtet dessen, was C so gut macht.
    Das artete dann auch in diesem Aprilscherz aus: https://www.heise.de/developer/artikel/No-New-New-Das-Ende-von-Zeigern-in-C-4009347.html

    Was mich aber wundert, ist der scheinbare Konsens auf dem OOP/Funktional/"Moderne Sprache"-Wahn. Kaum jemand jemand, mal von den Kernel-Entwicklern abgesehen, scheint den alten Stil zu verteidigen.
    Ich weiss nicht, ob es daran liegt, dass heutzutage niemand mehr direkt auf Hardware arbeitet (da ist mir oft selbst C zu ungenau, bzw. GCC mit seinen Register-readbacks after write) oder dass mittels IDEs das Nachverfolgen von Codepfaden einfacher ist, aber irgendwie missfällt mir diese Entwicklung. Aber wer weiss, vielleicht bin ich auch der einzige ¯\_(ツ)_/¯

    Gruss
    cwriter
     
  10. sheel

    sheel I love Asm Administrator

    Danke für die Fortführung :D
    ...
    Zu Stackoverflow und Pointer-verstecken zuerst ... hab da so eine kleine Theorie, die sich relativ gut hält
    • Leute kennen sich mit Pointern nicht aus. Bzw. dass die überwiegende Mehrheit der Menschen sich damit nicht auskennt ist sowieso klar, aber auch beim Rest sind viele dabei, die es zwar in normalen Fällen richtig anwenden können, sichd abei aber nicht so recht wohl fühlen
    • Daraus entsteht Angst - Angst etwas falsch zu machen, deswegen Probleme zu bekommen (alles von "Im Codereview blöd dastehen" bis zu "gefeuert werden" etc.). Und/oder sowas ist schon passiert und man sucht die Schlud bei der Sprache, statt bei sich.
    • Im Internet gibts bekannten Blog X, der sagt, Pointer sind schlecht. Dadurch fühlt man sich bestätigt, Pointer müssen weg.
    • Weitere Verstärker Richtung "Generation Schneeflocke" usw.
    • Und natürlich gibts noch die "Hipsters", die bei teilweise/ganz überlappenden Features nur das Neuere als die einzige Wahrheit akzeptieren.

    .... um fair zu sein, die Hipstereinstellung ist in offiziellen Sachen von Kommitee nicht zu finden (afaik). Pointer sind im Standard also sind sie erlaubt, Punkt. ... Aber da das Kommitee auch aus Leuten besteht, und immer wieder mal neue dazukommen die anfangen Proposals schreiben, wird das Ganze immer mehr unterwandert; von zB. solchen Leuten die auch diese Clang-Warnungen machen.

    Speziell SO betreffend, würde mir über die einfach nicht den Kopf zerbrechen. Zwar eine der größten "Communities" (haha, vergifteter gehts nicht mehr), aber im Durchschnitt auch eine der dümmsten (auch immer wieder mal mit öffentlichen Statistiken bestätigt).

    ...

    Zu diesem Quorapost, bei einem (C++-unabhängiger) Punkt muss ich dem sehr zustimmen: Krampfhafte Patternisierung ist böse (was sich übrigens wieder darauf zurückführen lässt, dass viele Leute OOP nie wirklich verstanden haben. So wie Pointer. usw.usw.).

    ...

    Zu Resourcenverbrauch, meine einfache Erklärung dazu, die Meisten mussten einfach noch nie was schreiben was schnell+speichersparend ist. In ihren Entwicklungsumgebungen war alles akzeptabel, kein Chef/Kunden/etc. haben sich über lLangsamkeit beschwert, usw ... und deshalb verstehen sie es nicht, warum man BequemesLangsamesFeatureX nicht als zwingenden Teil in die Sprache einbauen kann.

    (zu den Kunden - was nicht bedeuten soll, dass alles ok ist. Mein aktuelles Topproblem auf der Arbeit: Ein RestAPI-Ding, bei dem der einfachste Request 20sec braucht. Und im Idealfall würde ich gern für ca. jede Minute Arbeit einen Request starten und brauch das Ergebnis dann zum weitermachen. Es zu verbessern, oder sich beim Ersteller beschweren, ist leider beides explizit nicht genehmigt :rolleyes:
    ... Und da ist auch noch der bitte-mehr-als-16GB-Firefox :D
    ...)

    ...

    Außer den zwei Sachen bleibt aber nichts übrig :)
    ... wie immer gibts Situationen, wo es gut geeignet ist, und andere.

    (Die Exception für falsche Keys hört sich für mich gar nicht so schlimm an...)

    Jaa...das hab ich auch mal gehört ... zusammen mit der Aussage, dass man mehr als drei verschachtelte Level von Schleifen/if/... in der Realität nie sehen wird :D lol

    Auf die Frage, was man bei Funktionen mit 20 Parametern (bei der man die Parameterliste nicht ädnern kann!) und gleich am Anfang 200 Zeilen Validierung der Parameter, bevor es zum eigentlichen Sinn der Funktion kommt, tun soll, konnte mir bisher niemand von solchen Leuten eine überzeugende Antwort geben.
    meineFunktion_validateParam1_Part1(), meineFunktion_validateParam1_Part2(), meineFunktion_validateParam1_Part3()? Die Werte für die lokalen Variablen immer oorgendwie mit Referenzen mitübertragen? Die 60 validate-Funktionen dann in 3 weitere zu je 20 Funktionsaufrufen zusammenfassen, die drei wieder in einer, und die dann in der Hauptfunktion aufrufen? Und am besten jede Funktion noch mit einer eigenen Klasse?
    Wohl kaum.

    ...Erinnert mich an einen Kollegen, der tatsächlich jedes if-foo-then-error-4 in zwei (!) Klassen ausgelagert hat (eine für das if, eine mit den Fehlerdaten). Könnte ja wiederverwendbar sein. War es teilweise auch, aber natürlich keine Nettoersparnis an Zeilen und Arbeitszeit. Inzwischen hat sogar er eingesehen, dass es Unsinn ist.

    Hast du auch mal einen Grund bekommen? Also einen außer "ich mag es nicht" und/oder "RandomPersonImInternet mag es nicht"?
    Erinner sie doch bitte mal, dass wir nicht Modedesigner sind, sondern was praxistaugliches machen wollen.

    (Zum Niveau von "RandomPersonImInternet": Ein Artikel a la "Austrias [X] is the only thing worldwide that has [Y]" auf einer englischen Zeitungsseite. Ca. 50 Nutzerkommentare, die Hälfte davon waren Witze über Känguruhs. ... Ein paar andere Kommentare waren der Meinung, dass der Artikel nicht stimmt, weil auf den ganzen Bildern teilweise deutscher Text zu sehen war und man in Austria ja ganz offensichtlich nicht german spricht).

    ...
    du bis nicht allein :)
     
  11. cwriter

    cwriter Erfahrenes Mitglied

    Jo. Gerade heute wieder einen Kommentar auf eine Antwort gelesen:
    upload_2018-4-21_21-54-22.png

    Mhm, hatte erst gerade kürzlich die Pattern in einer Vorlesung. Da wurde z.B. ernsthaft gesagt, man solle das Factory-Pattern benutzen, um einfacheres Refactoring zu erlauben. Nur in einem einzigen Fall finde ich das zu bevorzugen: Wenn idiomatisch ein ::create() benutzt werden kann, das einige immergleiche Parameter des Basisklassenkonstruktors bindet.
    (Manche Pattern, wie das Visitor-Pattern bei ASTs, sind für einige wenige Anwendungsbereiche extrem gut geeignet).

    Aber selbst Wikipedia schreibt korrekt:
    Nur kommt das in den Vorlesungen nicht so rüber. Es heisst schlicht, Patterns seien das einzig Wahre, dann bekommt man in einer Prüfung irgendeinen 100-Zeilen-Code vorgesetzt und muss dann sagen, dass es schlecht ist, new String statt StringBuilderFactory().build().make() zu benutzen, weil letzeres ja so viel erweiterbarer sein soll.
    Das sind die Professoren, die ihre Semantischen Analyzer auch ohne Overflow arbeiten lassen, weil es ja so viel Sinn macht, einen möglichen Resultatbereich eines Integers von [0, \inf[ zu haben :confused:
    Hui, wird da Pi in die tausendste Stelle berechnet oder 1GB Daten durchsucht?

    Das ist zwar fast wieder eine andere Diskussion, aber wieso wären hier Returnvalues nicht zu bevorzugen (die meisten Funktionen von CryptoPP geben ohnehin void zurück)? Das Problem mit Exceptions ist ja gerade, dass sie irgendwann mal mitten in einem Code, der Libraryfunktionen benutzt, auftreten können. Catchen muss man die Exceptions ohnehin möglichst nahe am throw, sonst bekommt man sehr schnell einen Inconsistent State. Und ähnlich wie mit new, dessen throw ich wirklich fürchte, welcher eigentlich nicht nötig ist, da man auch auf nullptr prüfen kann, sehe ich keinen Grund für Exceptions, da man ja einfach einen bool/bitset/errnum zurückgeben könnte, der dann halt zu prüfen ist.
    Ich weiss schon, dass u.a. bei Java die Exceptions meistens recoverable sind, und ich sehe auch das Argument, dass es eher unerwartet ist, dass Fehler geschehen (eine sehr optimistische Einstellung). Aber warum wird dann eine Exception so lange automatisch rethrowt, bis es jemand handelt, und zerschiesst dabei alle Stacks davor? Warum wird nicht erzwungen, dass Exceptions nur eine Stufe durchdringen können, um es überschaubar zu halten? Bei einem Retry würde dann ja immer alles komplett neu aufgebaut, was auch wieder kostet. Und wenn etwas wirklich kaputtgehen kann, dann müsste das ja schon im Vorherein klar sein, und man könnte ein Callback setzen.

    Wobei das meines Wissens im Styleguide für Kernelcode ist. Wie sehr das durchgesetzt wird, weiss ich aber auch nicht, und i.d.R. sind auf den tieferen Stufen auch weniger Unterscheidungen nötig.

    20 Setter :)
    Immer beliebt ist der Spruch: "Wer 11 Parameter hat, hat vermutlich einen vergessen." Ja ne is klar. Das sind dann aber auch die Vögel, die 300 Werte in ein JSON-Objekt schreiben und dann 2 davon benutzen. Sind ja keine Parameter oder so. Oder sie machen es viel klüger und erstellen einzig für die Verwaltung der Parameter ein Objekt, selbstverständlich auf dem Heap, damit der ach so lahme Stack nicht benutzt werden muss. Beim ersten WinAPI-Code würden die Leute ja einen Schreikrampf kriegen...
    Ich meine, keiner mag viele Parameter, aber manchmal müssen sie hin und dann sieht es halt nicht sehr gut aus, aber meine Güte, diese Funktionen werden i.d.R. nicht als Interface gegeben, sondern intern benutzt.

    Doch, so oder so ähnlich machen das recht viele Leute.

    Ah ja, Wiederverwendbarkeit. Eigentlich nichts schlechtes, aber die Leute rollen immer erst die Strukturen aus und machen dann den eigentlichen Arbeitscode, sodass immer etwa 3 Wrapperklassen sinnlos vor sich hindümpeln. Sieht halt schon viel besser aus, wenn man keinen Code sieht :rolleyes: Ich halte es grundsätzlich eher konservativ: Solange es lesbar ist, ist alles ok. Wird es zu komplex, wird ausgelagert.

    "Wenn eine Funktion so viele Zeilen hat, macht sie zu viel gleichzeitig" (auch auf Klassen anwendbar, halt mit Member etc.).
    Und es ist wohl wahr, ich lagere nur dann aus, wenn es wirklich der Lesbarkeit hilft und Performance nicht allzu wichtig ist oder wenn ich dieselbe Funktionalität mehrmals brauche. Aber bei langen if-elses mit klarer Sortierung; warum sollte ich da gross auslagern? Dass langer Code per se schlecht sein soll, ist auch eine Folge von missverstandenem OOP (wie so vieles), aber leider hält sich das ja hartnäckig.

    Das ist, denke ich, eines der Probleme: Man schreibt immer nur Samplecode an den Bildungseinrichtungen, nie richtigen Code. Will man irgendwas komplexes machen, nimmt man dann die einfachste Bibliothek und fertig; man muss wenig Code schreiben. Entsprechend wird auch eher die Form statt die Funktion bewertet, und das zieht sich halt so durch.
    Letztens über Swift gelesen: Hat ja so geile Features wie return type overloads. Ja, man kann es sich denken:
    Code (Text):
    1. let a: Double = -(1 + 2) + -(3 + 4) + -(5)
    Das gibt allen Ernstes den Output "error: Expression too complex". Warum? https://www.cocoawithlove.com/blog/...es.html#errors-compiling-otherwise-valid-code
    In Kurzform: Durch die overloaded return types gibt es exponentielle Type-Inference-Komplexität. Aber hey: Immerhin zahlt man nur mit 20s compiletime bei solch einfachen Expressions für dieses tolle Feature, das keiner je vermisste. Und: Es sieht halt besser aus, da spielt Performance keine Rolle.

    Aber ich bin ja auch der Meinung, man dürfe
    Code (Text):
    1. //Für kurze statements (~< 10 LOC)
    2. if () {
    3.     stmt();
    4. }
    5.  
    6. //Für "normale" statements
    7. if()
    8. {
    9.     stmt();
    10. }
    11.  
    12. //Für 1 LOC statements
    13. if()
    14.     stmt();
    frei mischen und die Lesbarkeit ändere sich nicht wesentlich (oder verbessert sich sogar), aber viele beharren auf einem einheitlichen Stil. Das artet auch schnell darin aus, dass manche TABs verbieten wollen oder ein Leerzeichen nach "//" verlangen, aber ich verstehe auch, wenn jemand homogenen Code haben will. Wobei dann auch einfach pretty-print angeschmissen werden könnte...
    Das war sicherlich eine Übertreibung, aber diese Meinung ist sicher in der Minderheit (oder die anderen sind einfach lauter, kann ja auch sein).

    Eieiei, was für ein Rant. Aber muss ja auch ab und zu sein.

    Gruss
    cwriter
     
  12. ComFreek

    ComFreek Mod | @comfreek Moderator

    Gefühlt gibt es zwei Arten von Exceptions in einem gegebenen Kontext (dort wo sie auftreten):
    • Die, die recht nah behandelt werden. Wobei "nah" je nach Vorhandensein von Bibliotheken, Hilfsfunktionen/-klassen etc. variieren kann.
    • Die, die recht weit außen behandelt werden, weil sie fundamentale Fehler darstellen, z. B. eine NullPointerException, die zum Protokollieren des Fehlers, Anzeige einer Fehlermeldung und dann Termination der Applikation führt. Weit außen ist hier relativ zu sehen, das kann die main()-Funktion sein, ein Einstiegspunkt eines Threads oder auch die äußerste Methode einer Sandbox/Pluginumgebung. Beim letzten Fall hat ein externes Plugin zum Fehler geführt.
    IMO ist das Problem, dass der Übergang recht fließend sein kann. Dementsprechend lässt du dir Flexibilität beim Exception-Modell. Im Gegensatz dazu kann man das auch einschränken zu Checked und Unchecked Exceptions, siehe Java. Das erhöht auf erste Sicht erst einmal massig den Boilerplate-Code, an sich finde ich solch eine Trennung auf Typebene gar nicht mal so schlecht. Vielleicht könnte man beim Aufruf einer Funktion gleich sagen, für welche Exceptions man sich interessiert:
    Code (Java):
    1. try {
    2.   File file = openFile("non-existent.txt") with fatal FileNotFoundException, caught MissingPermissionException
    3. }
    4. catch (MissingPermissionException e) {
    5.   // Dass diese hier abgefangen wird, erzwingt der Compiler wegen obiger Zeile
    6. }
    Ist das Mitschleifen eines Callbackpointers auf eine ganz außen definierten Callbackfunktion nicht dasselbe wie das Erlauben der zweiten Art von Exceptions? Konzeptionell vollziehen sie beide dasselbe.

    Das hängt stark davon ab, ob man mit der Außenwelt, sei es Hardware oder ein externes Programm, agiert.

    Da C++ durchaus als High-Level-Sprache benutzt werden kann, sind solche Flexibilitäten wie bei Exceptions imo unumgänglich für die Sprachdesigner. Aber ja, für OS- oder hardwarenahe Programmierung möchte man darauf und auf die damit verbundenen Zusatzkosten eher verzichten.
     
  13. cwriter

    cwriter Erfahrenes Mitglied

    NullPointerExceptions sind aber doch ein Argument gegen Exceptions: Warum kann man nicht einfach prüfen, dass ein Wert nicht null ist, bevor man ihn verwendet? Oder mittels Contracts/Asserts verlangen, welche Vorgaben erfüllt sein müssen? Mit NullPointerExceptions sieht man ja nicht, wo die Ursache des Problems ist, sondern nur, wo die Symptome auftreten.
    Bei wirklich schlimmen Fehlern macht das Sinn, aber eine Funktion, die z.B. ein Netzwerkpaket erwartet und bei Timeout throwt, verstehe ich schlicht nicht. Warum nicht einfach die Anzahl gelesener Bytes zurückgeben, die dann halt 0 oder -1 sind? Warum braucht man eine Exception, die bis zur main() durchgereicht wird, wo dann ein String dargestellt wird? Ermuntert das nicht dazu, gar keine Exception mehr zu fangen und bei Problemen einfach gar nichts zu machen, da der Nutzer ja eine Nachricht bekommt? Und da z.B. Timeouts nicht allzu selten sind, warum macht man sich die Mühe von Objekten mit Strings, wo ein Integer den Fehler genausogut beschreiben kann?
    Aber diese Art von Fehler ginge ja recht gut mit einem Rückgabewert.
    Ob man jetzt
    Code (C):
    1. FILE* f = fopen("test.txt", "r");
    2. if(f == NULL) {
    3.     //"Exception" handler
    4. }
    5.  
    6. fclose(f);
    oder
    Code (Java):
    1. try {
    2.   File file = openFile("non-existent.txt");
    3. }
    4. catch (Exception e) {
    5.    //Exception handler
    6. }
    schreibt, kommt ja echt auf dasselbe raus. Also wieso verwendet man Exceptions für eine Stufe?

    Flexibilität ist immer Fluch und Segen. Wer erfahren ist, kann sehr interessante Konstrukte bauen. Wer es nicht ist, vertut sich aufgrund mangelnder Regeln schnell.
    Ich finde das einen der schlimmsten Fehler von Java. Seien wir ehrlich: Die allermeisten Leute lassen die IDE die nötigen Stubs erstellen und belassen es dabei. Man gewinnt nichts, hat mehr Code, und man verwirrt die Leute, warum einige Exceptions anders sind. C# hat checked exceptions ja auch entfernt. Hier ist wieder die Flexibilität (oder eher, dass man nicht wusste, was man wollte) ein Problem. Entweder man sagt, Exceptions sind schwere Fehler, die aber Recoverable sind und gefangen werden müssen, oder man sagt, Exceptions sind eine andere Form von Rückgabepfaden. Letzteres wäre aber Redundant mit Returnvalues (gut, syntaktisch vielleicht ein bisschen aufgeräumter), ersteres wird nicht gemacht, weil es schnell viel zu verbose würde.

    Das wäre sicherlich ein Ansatz.

    Callbacks würden das Stack-unwinding verhindern. Vielleicht wäre es besser, eine Strategie zu übergeben, wie z.B. bei Netzwerktimeouts eines aus {resend, keep_waiting, return} oder so.

    Nur, weil die meisten High-Level Languages Exceptions bieten, heisst das nicht, dass es eine gute Idee ist.
    Exceptions sind ja in erster Linie ein Hack für Bequemlichkeit.
    Z.B.:
    Code (C++):
    1. void f()
    2. {
    3.     if(!g())
    4.     {
    5.         //Handle this error
    6.     }
    7.     if(!h())
    8.     {
    9.         //Handle this error and revert what g() did if necessary
    10.     }
    11. }
    12. //Wird zu:
    13. void f() throws
    14. {
    15.     g();
    16.     h();
    17. }
    Soweit, so gut. Aber was wurde vergessen? Genau, "Revert what g() did". Bis heute gibt es keine klare Lösung für dieses Problem. Oft wird dann einfach ein check eingeführt, der zuerst prüft, ob weder g noch h fehlschlagen, und entsprechend abbrechen kann. Das wiederum funktioniert aber nur, wenn weder g noch h aus anderen Gründen fehlschlagen, die ein Check nicht zuverlässig prüfen kann, z.B. out-of-memory-errors. Und genau hier giessen Exceptions Öl ins Feuer: Die erste Variante geht nicht mehr, da durch eine Exception in einer Funktion der Rückgabewert umgangen wird, d.h. man muss dann Catchen. Aber damit hat man den Code ja wieder zu einem If-Else gemacht, das einfach stattdessen try-catch heisst:
    Code (C++):
    1. void f()
    2. {
    3.     try { g(); }
    4.     catch (...) {
    5.         //Handle this error
    6.     }
    7.     try { h(); }
    8.     catch(...)  {
    9.         //Handle this error and revert what g() did if necessary
    10.     }
    11. }
    Damit hat man also durch Exceptions genau gar nichts gewonnen und viele sich Nachteile wie Overhead und Runtimeunterstützungsnotwendigkeit eingebrockt.
    Eine echte Lösung wäre etwas wie Transactions:
    Code (C++):
    1. void g()
    2. {
    3. :onabort = {
    4.     //Auto-Revert
    5. }
    6. }
    7.  
    8. transactional void f() //If unrecoverable errors occur, call onabort for all previously executed functions in that scope
    9. {
    10.     g();
    11.     h(); //h() schlägt fehl -> g():onabort()
    12. }
    Damit wäre sichergestellt, dass nie ein inconsistent State erreicht wird, es ist idiomatisch (etwas ähnliches lässt sich mit RAII und einem Flag, ob bei Destruktoraufruf committed oder aborted werden soll, schon erreichen) und man hat sauberen Code. Also ist das eigentlich das, was Exceptions sein wollten, mit dem Unterschied, dass diese onabort in vielen Fällen vom Compiler automatisch generiert werden könnten (sofern jede in der Funktion aufgerufene atomare Operation auch ein onabort hat, der Rest folgt durch Induktion).

    Wie ich hoffentlich nun klarer ausgedrückt habe, sehe ich das Problem mit Exceptions nicht nur in der Performance, sondern auch in Redundanz, Inkonsistenz und Einladung zu schlechtem Code.

    Gruss
    cwriter