Wieso gibt es keine gute Programmiersprache?



  • dachschaden schrieb:

    5cript schrieb:

    Mein code ist alles andere als rumgesaut. Sobald mein code compiliert ist er Fehlerfrei.

    Harharhar.

    Viel zu lernen du noch hast.

    Schonmal Haskell programmiert?

    Stateless programming, invarianzfreie Programmierung usw.
    Oder Boost Spirit. Sobald die Grammatik kompiliert ist sie nur selten falsch.
    Für parallele Programmierung gibt es auch Mittel, sodass einem die Hölle ersparrt bleibt.

    Viel zu lernen DU noch hast!



  • 5cript schrieb:

    Schonmal Haskell programmiert?

    Nein, ich verwende keine funktionalen Programmiersprachen. Von dem, was ich mitbekommen habe, besitzen diese den namensgebenden Anspruch, alles funktional lösen zu wollen. Ohne Seiteneffekte oder State. Und dann verrenken sie sich, da doch sowas wie State reinzubringen, oben draufgepappt, weil's auf echten Maschinen nun mal nicht anders geht. Über Monads, wo ich bereits einige Leute zu befragt habe und niemals eine einleuchtende Erklärung bekommen habe, warum. Eingabe, Verarbeitung, Ausgabe, es ist so einfach mit dem State, und sie wollen es aber so kompliziert machen.

    https://xkcd.com/1312/

    Mit solchen Spielereien befasse ich mich nicht. Und ohnehin ging es hier auch nicht um Haskell, sondern im Kontext von C++.



  • 5cript schrieb:

    invarianzfreie Programmierung

    Nie gehört. Link!



  • dachschaden schrieb:

    5cript schrieb:

    Schonmal Haskell programmiert?

    Nein, ich verwende keine funktionalen Programmiersprachen. Von dem, was ich mitbekommen habe, besitzen diese den namensgebenden Anspruch, alles funktional lösen zu wollen. Ohne Seiteneffekte oder State. Und dann verrenken sie sich, da doch sowas wie State reinzubringen, oben draufgepappt, weil's auf echten Maschinen nun mal nicht anders geht. Eingabe, Verarbeitung, Ausgabe.

    https://xkcd.com/1312/

    Mit solchen Spielereien befasse ich mich nicht. Und ohnehin ging es hier auch nicht um Haskell, sondern im Kontext von C++.

    Diese "Spielereien" haben so wunderbare Konsequenzen, die einem bewusst sein sollten. Und in C++ kann man sehr wohl auch diese Prinzipien anwenden und davon profitieren. C++ kann alles sein. Wer C++ auf objektorientierung, generisches und prozedurales beschränkt, kann auch nicht von den Einflüssen profitieren aus anderen Gebieten.



  • hustbaer schrieb:

    5cript schrieb:

    invarianzfreie Programmierung

    Nie gehört. Link!

    Ich meinte "keine Invarianten". Invarianten oder "stille Gegebenheiten" salopp ausgedrückt sind Fehlerquellen, die Kapselung benötigen. Zum Beispiel, wenn man ein Counter mitführt für die Anzahl Elemente eine Liste.
    Wenn man sowas vermeiden kann, kann man auch keine Kapselungsfehler machen oder Sachen vergessen.

    EDIT: OOP ist Modellierung mit State. Der Versuch state zu bändigen. Aber das kann man auch anders lösen.



  • 5cript schrieb:

    Diese "Spielereien" haben so wunderbare Konsequenzen, die einem bewusst sein sollten.

    Tolle Konsequenzen, die die Maschine darunter so weit weg abstrahieren wollen. Und dann geben sie doch klein bei beim State.

    5cript schrieb:

    Und in C++ kann man sehr wohl auch diese Prinzipien anwenden und davon profitieren. C++ kann alles sein.

    Stimmt. Sollte aber nicht.

    Das ist ja auch mein Hauptkritikpunkt an C++. Dass mir Leute sagen, dass ich Sachen dann doch lieber in C++ machen soll. Und dann mach ich Sachen in C++, und die Leute sagen mir, dass man das so nicht macht. Sie verkaufen es mir als Erweiterung, aber inzwischen ist es eine eigene Sprache mit Mitteln, die mir so überhaupt nicht gefallen will mit ihren Paradigmen, die von jedem erwartet werden.

    Ich fange nicht an, C-Code in C++ runterzuschlunzen. Da spare ich mir und meinen Mitmenschen die Nerven. Und ich fange auch nicht an, stateless-Paradigmen in objektorientierte Programmiersprachen reinzubringen. Das ist wie wenn ich eine Fliege mit einem Topf erschlage - kann man machen, aber dann schaut jeder einen an wie einen Geistesgestörten.



  • Das ist warum ich C++ liebe.
    Jedes Problem benötigt sein eigenes Werkzeug.
    Und das Werkzeug, dass ich nicht verbiegen muss um etwas zu realisieren bietet meistens die einfachste, lesbarste Lösung, die am wenigsten Fehlerpotential hat, weil man es vollständig überblicken kann, oder die Mittel einem die Arbeit vollständig abnehmen.
    **
    Deswegen C++. Weil man nicht versucht wie ein "geistesgestörter" (zitat) versucht quadrate in dreieckige Formen zu quetschen, weil man zb nur OOP hat, oder nur Funktionale Programmierung. usw usw.**



  • 5cript schrieb:

    Deswegen C++. Weil man nicht versucht wie ein "geistesgestörter" (zitat) versucht quadrate in dreieckige Formen zu quetschen, weil man zb nur OOP hat, oder nur Funktionale Programmierung. usw usw.

    Finde ich nicht. Ich finde, C++ gibt dir die Möglichkeit, die dreckigen Details dahinter nicht mehr betrachten zu müssen. Wie bei RAII. Oder bei Exceptions. Ja, ja, wir wissen alle, wie viel Code die beiden sparen - im eigentlichen Anwendungscode. Also das, was du siehst, nicht, was daraus wird.

    Wobei, im Fall von RAII stimmt das nicht so ganz. Da siehst du die Nachteile auch im Code. Dass ich, wenn ich nicht will, dass ein Konstrukoraufruf für ein eventuell benutztes Objekt teuer werden, den eigentlichen Initialisierungscode in eine eigene Funktion packen müsste. Aber damit könnte ich noch leben.

    Aber Exceptions - nicht falsch verstehen, ich habe auch kein besseres Rezept hier außer alles auf Basis von Fehlercodes zurückgeben. Da muss ich den Heap nicht bemühen. Fehlercodes sind einfach, Exceptions sind komfortabel. Ich hätte gerne was, was beides ist. Gibt es nicht, also verkneife ich mir den Komfort und lasse mir lieber überall da, wo ein Fehlercode zurückgegeben wird, eine Warnung ausgeben, wenn ich nicht darauf prüfe.

    Das sind Werkzeuge, mit denen arbeiten Programmierer halt. Aber ich habe lieber Werkzeuge, die mir nicht verheimlichen, was unten drunter passiert. Die versuchen, ihre Sachen so gut wie möglich zu machen, ohne mir unnötigen Komfort zu geben, nur weil ich 'ne faule Socke bin. Sonst landet man irgendwann hier. Da war ich oft. Möchte ich nach Möglichkeit auch vermeiden.

    Ich habe lieber eine manuelle, haltbare Plastikfliegenklatsche, die sich leicht reinigen lässt und deren einziger Zweck das Klatschen von Fliegen und anderem Getier ist, als ein Edelstahlungetüm, das ich auch zum Kochen verwenden kann.



  • dachschaden schrieb:

    5cript schrieb:

    Deswegen C++. Weil man nicht versucht wie ein "geistesgestörter" (zitat) versucht quadrate in dreieckige Formen zu quetschen, weil man zb nur OOP hat, oder nur Funktionale Programmierung. usw usw.

    Finde ich nicht. Ich finde, C++ gibt dir die Möglichkeit, die dreckigen Details dahinter nicht mehr betrachten zu müssen.

    Ich denke nicht dass sich das beides wiederspricht/ausschließt.



  • 5cript schrieb:

    Ich denke nicht dass sich das beides wiederspricht/ausschließt.

    1. "widersprechen"
    2. Ich kann also RAII deaktivieren? Wusst' ich gar nicht.



  • dachschaden schrieb:

    5cript schrieb:

    Ich denke nicht dass sich das beides wiederspricht/ausschließt.

    2. Ich kann also RAII deaktivieren? Wusst' ich gar nicht.

    Was hat das damit zu tun?

    Please note that object-oriented programming is not a panacea. "OOP" does not simply mean "good" - if there are no inherent hierarchical relationships among the fundamental concepts in your problem then no amount of hierarchy and virtual functions will improve your code

    http://www.stroustrup.com/bs_faq.html#oop

    Das ist das wovon ich rede.

    Das hat doch überhaupt keine Schnittmenge damit, ob ich jetzt RAII benutze oder nicht. Das ist eine völlig andere Ebene. Ein Pattern, keine Paradigma.



  • 5cript schrieb:

    Was hat das damit zu tun?

    5cript schrieb:

    Ich denke nicht dass sich das beides wiederspricht/ausschließt.

    RAII widerspricht dem ganz gewaltig. Ich will ein Objekt auf dem Stack haben, aber es nur dann initialisieren, wenn ich es auch verwende. Weil die Initialisierung teuer ist. In C++ sagen sie mir dann: geht nicht, pack' deinen Code in eine eigene Funktion, und gut ist. Da habe ich dann drei Calls, wo ich nur zwei bräuchte: einmal Konstruktor, einmal Destruktor (automatisch, gegen die kann ich nichts machen), und den eigentlichen Initialisierungscode.

    In C habe ich dagegen zwei - einen *_init-Call und einen *_free-Call.

    Kann ich in C++ dem Compiler sagen, dass er nur ein bisschen Speicherbereich auf dem Stack reservieren soll für ein Objekt, und Initialisierung und Freigabe komplett mir überlassen soll? Wenn ja, will ich nichts gesagt haben. Wenn nein, dann widerspricht dies nicht nur deiner Aussage, dass Automatisierung und Details verstecken dem Effizenzmaximierungsanspruch nicht widersprechen - sie widerspricht auch der oft gebetsmühlenartig hervorgebrachten Behauptung, dass du in C++ nur für das zahlst, was du auch nutzt.

    Ich sage nicht, dass C++ deswegen schlechter oder besser ist. Ich sage, dass C einfacher ist und dir mit (gut gemeinten) Automatismen nicht auf den Wecker geht. Dass C++ deswegen komfortabler ist. Ich stehe C++ nicht komplett feindlich gegenüber; ich hätte manchmal gerne selbst Template-Support in C.

    Aber C++ kommt zu dem Preis von Exceptions (die ich auch schon dann habe, wenn ich nur die STL verwende, von Third-Party-Libs mal gar nicht angefangen), und zu dem Preis von automatischen Konstruktionen und Freigaben. Für ersteres kann ich auch einfach irgendwelchen anderen C-Libs verwenden, die keine Exceptions kennen, und auch selbst keine schreiben, aber das letztere, das kriege ich nicht aus der Sprache weg.

    Und selbst, WENN ich es hinkriegen würde, würde man mich für geistesgestört halten, weil ich zum Suppekochen den Topf dann doch links liegen lasse und sie in der Dose warmmache. Daneben die Fliegenklatsche für die Fliegen. "Warum machst du hier denn keine Exception, das wäre ja so viel einfacher ...".

    NEIN. Wäre es nicht. Es wäre komfortabler. Einfacher ist es, den Heap in Ruhe zu lassen.

    Und dieses "wir drücken jetzt mal Paragidmen durch, die wir in anderen Sprachen, die sich selbst kastriert haben, ja auch erfolgreich bewehrt haben" - genau das bewirbst du hier mit der anfänglichen Debatte über Haskell.

    EDIT: Rechtschreibfehler.



  • dachschaden schrieb:

    Kann ich in C++ dem Compiler sagen, dass er nur ein bisschen Speicherbereich auf dem Stack reservieren soll für ein Objekt, und Initialisierung und Freigabe komplett mir überlassen soll? Wenn ja, will ich nichts gesagt haben.

    Ja, ich glaube es gibt kaum etwas, das in dieser wahnwitzigen Sprache nicht geht:

    #include <iostream>
    #include <type_traits>
    
    struct Objekt
    {
    	Objekt(int value) : value{ value } {}
    	void print() const { std::cout << value << std::endl; }
    	int value;
    };
    
    auto main() -> int
    {
    	// Reserviere Speicher für Objekt auf Stack.
    	typename std::aligned_storage<sizeof(Objekt), alignof(Objekt)>::type objekt_auf_stack;
    	// Erstelle Objekt.
    	auto& objekt = *(new (&objekt_auf_stack) Objekt{ 5 });
    	// Verwende Objekt.
    	objekt.print();
    	// Zerstöre Objekt.
    	objekt.~Objekt();
    }
    

    Da stellt sich allerdings die Frage, ob man das so wirklich braucht.
    Warum nicht das Objekt erst in einem Scope deklarieren bei dem man weiss, dass es benötigt wird? ( if (objekt_benoetigt) { Objekt objekt{ 5 }; } )

    dachschaden schrieb:

    Wenn nein, dann widerspricht dies nicht nur deiner Aussage, dass Automatisierung und Details verstecken dem Effizenzmaximierungsanspruch nicht widersprechen - sie widerspricht auch der oft gebetsmühlenartig hervorgebrachten Behauptung, dass du in C++ nur für das zahlst, was du auch nutzt.

    Nun, man bezahlt auch das, was man bestellt - auch wenn man das 5-Gänge-Menü nicht isst.
    Das ist in C auch so, wenn man *_init() aufruft, und das Objekt dann nicht verwendet.

    dachschaden schrieb:

    Aber C++ kommt zu dem Preis von Exceptions (die ich auch schon dann habe, wenn ich nur die STL verwende, von Third-Party-Libs mal gar nicht angefangen), und zu dem Preis von automatischen Konstruktionen und Freigaben. Für ersteres kann ich auch einfach irgendwelchen anderen C-Libs verwenden, die keine Exceptions kennen, und auch selbst keine schreiben, aber das letztere, das kriege ich nicht aus der Sprache weg.

    Mir fällt es etwas schwer diese CTOR/DTOR-Kritik nachzuvollziehen, da diese meines erachtens das mir mit Abstand wichtigste Feature von C++ sind.
    (Würde man mich zusammen mit C auf eine einsame Insel verbannen, und ich dürfte mir nur ein Feature von C++ mitnehmen, dann wäre es dieses ;))
    Hast du vielleicht ein konkreteres Beispiel, wo man angenehmer ohne Kostruktoren/Destruktoren untewegs ist?
    Meine Erfahrung ist nämlich eher, dass die ganzen goto error/goto cleanup ein ziemlicher Krampf sind, wenn man den Code korrekt und leckfrei hinbekommen will.

    Finnegan



  • dachschaden schrieb:

    ...

    Ich verstehe überhaupt nicht wohin du argumentierst...? 😕

    Ich sehe C++ als Monster. Die überladenste Sprache überhaupt, die überhaupt alles kann. Und ich bin einer derjenigen, die das sehr positiv sehen.

    Aber Finnegan scheint ja zu klären, was an mir grad vorbeigeht.



  • auto& objekt = *(new (&objekt_auf_stack) Objekt{ 5 });
    

    Damit baue ich mir doch nur wieder einen Zeiger auf dem Stack, der dann aber wieder auf Speicher im Heap verweist, oder?

    Finnegan schrieb:

    Da stellt sich allerdings die Frage, ob man das so wirklich braucht.
    Warum nicht das Objekt erst in einem Scope deklarieren bei dem man weiss, dass es benötigt wird? ( if (objekt_benoetigt) { Objekt objekt{ 5 }; } )

    Weil ich meinen Code gerne beieinanderhalten würde?

    void foo(int bar)
    {
        irgendein_objekt;
    
        mach_was();
    
        if(bar)
        {
            initialisiere_objekt();
        }
        else
        {
            mach_noch_was();
        }
    
        mach_noch_mehr();
        und_noch_mehr();
    
        if(bar)
        {
            mach_was mit_objekt();
            mach_noch_mehr_mit_objekt();
        }
    
        und_noch_ein_bisschen_mehr();
        if(bar)
        {
            gib_objekt_frei();
        }
        return;
    }
    

    Hier gibt es eine Menge Code, die im Grunde gleich bleibt, und nur in einigen Fällen muss man was mit dem Objekt machen. Ein eigener Scope bedeutet viel redundanter Code. Das C-Modell erlaubt es mir, selbst zu bestimmen, was ich mit meinem Objekten mache, ohne mich verrenken zu müssen.

    Finnegan schrieb:

    Nun, man bezahlt auch das, was man bestellt - auch wenn man das 5-Gänge-Menü nicht isst.
    Das ist in C auch so, wenn man *_init() aufruft, und das Objekt dann nicht verwendet.

    Siehe oben.

    Finnegan schrieb:

    Mir fällt es etwas schwer diese CTOR/DTOR-Kritik nachzuvollziehen, da diese meines erachtens das mir mit Abstand wichtigste Feature von C++ sind.

    Ich sage nicht, dass es falsch ist. Nur ich hätte gerne schon die Kontrolle darüber, wann ich meine Objekte initialisiere und freigebe, ohne dass ich auf den Heap wechseln muss.

    Deswegen hätte ich ja gerne Templates in C. Die, die es nicht kümmert, können dann C++ verwenden, und die, die es kümmert, bleiben bei C. Templates sind nur teuer für den Compiler, damit kann ich leben.

    Finnegan schrieb:

    Hast du vielleicht ein konkreteres Beispiel, wo man angenehmer ohne Kostruktoren/Destruktoren untewegs ist?

    Ich habe sogar zwei aus meinem Hinterkopf:
    Das erste ist eine Liste mit auf Windows allozierten Pages, weil Windows kein VirtualReAlloc kennt. Deswegen habe ich ein Backup-Objekt auf dem Stack, welches ich dann befülle, wenn ich keine Erweiterungen mehr reservieren und woanders einen Speicherblock holen muss. Aufgrund der Struktur wird das Objekt an verschiedenen Stellen befüllt und ausgelesen, aber die Kosten für die Initialisierung (die immer mindestens eine einzelne Page für die größere Page-Liste ist) müsste ich in C++ immer mit mir tragen, wenn ich neuen Speicher reservieren will. Ich hätte dann einen Kernel-Call mehr drin, als notwendig wäre.

    Das zweite ist eine Funktion für einen ELF-Parser, die Program Header und Section Header-Metadaten rausextrahieren soll. Was der Nutzer will, wird über den Parameter angegeben. Weil ich aber sicherstellen will/muss, dass die Originaldaten nicht teilweise überschrieben werden, und dann bemerkt die Funktion einen Fehler und bricht mit halbgaren Informationen ab, übergebe ich meine eigenen, auf dem Stack angelegten "Parameter", in die geschrieben werden. Und am Ende müssen diese Daten auch noch mal aufbereitet werden (relative Offsets in absolute Zeiger, basierend auf bestimmten Feldern in der Dateistruktur).

    Finnegan schrieb:

    Meine Erfahrung ist nämlich eher, dass die ganzen goto error/goto cleanup ein ziemlicher Krampf sind, wenn man den Code korrekt und leckfrei hinbekommen will.

    Sprechende Labels können hier schon relativ helfen:

    if(!object_1_init())
        goto LABEL_ERROR_OBJECT_1_NO_INIT;
    
    if(!object_2_init())
        goto LABEL_ERROR_OBJECT_2_NO_INIT;
    ...
    

    Am Ende hast du dann oft nur eine Stelle, wo du wirklich aufpassen musst.



  • dachschaden schrieb:

    auto& objekt = *(new (&objekt_auf_stack) Objekt{ 5 });
    

    Damit baue ich mir doch nur wieder einen Zeiger auf dem Stack, der dann aber wieder auf Speicher im Heap verweist, oder?

    Nein. Das Objekt liegt auf dem Stack. std::aligned_storage<>::type ist letztendlich nur ein char[sizeof(Objekt)] -Array (Stack) mit korrektem Alignment für das Objekt.
    Bei dem new handelt es sich um ein placement new, welches das Objekt direkt in diesem char-Array konstruiert.
    objekt ist hier lediglich eine Referenz auf dieses Objekt ( new gibt Objekt -Pointer auf Stack zurück, Referenz wird mit dem dereferenzierten Pointer initialisiert).
    Das Ganze sollte äquivalent zur C-variante mit einem struct auf dem Stack und separater init() -Funktion sein.

    dachschaden schrieb:

    Finnegan schrieb:

    Da stellt sich allerdings die Frage, ob man das so wirklich braucht.
    Warum nicht das Objekt erst in einem Scope deklarieren bei dem man weiss, dass es benötigt wird? ( if (objekt_benoetigt) { Objekt objekt{ 5 }; } )

    Weil ich meinen Code gerne beieinanderhalten würde?

    Es stimmt schon, dass wenn man mit Scopes arbeitet, sich manchmal ein wenig mehr gedanken über die Abarbeitungsreihenfolge machen muss:

    void foo(int bar)
    {
        mach_was();
    
        if(!bar)
        {
            mach_noch_was();
        }
    
        mach_noch_mehr();
        und_noch_mehr();
    
        if (bar)
        {
            irgendein_objekt;
            mach_was mit_objekt();
            mach_noch_mehr_mit_objekt();
        }
    
        und_noch_ein_bisschen_mehr();
        return;
    }
    

    mach_was() , mach_noch_mehr() und und_noch_mehr() sind wohl unabhängig vom Objekt, da man sie auch aufrufen kann,
    wenn das Objekt nicht existiert (=nicht initialisiert wurde). Daher sollte man sie frei verschieben können, solange sie ihre relative Reihenfolge beibehalten.
    Falls doch eine Abhängigkeit vom Objekt besteht, ist tatächlich manchmal redundanter Code die einfachste Lösung, meiner Erfahrung nach lässt sich jedoch
    der meiste Code so formulieren, dass die Scopes auf natürliche Weise die gewünschte Lebenszeit des Objekts begrenzen.

    Für die wenigen Sonderfälle, wo sich das nicht elegant auflösen lässt, hilft entweder eine zusätzliche Status-Variable, oder man erstellt das Objekt zur Not
    eben auf dem Heap mit explizitem delete / unique_ptr<>.reset() oder verwendet meine oben vorgeschlagene Stack- new -Variante.
    Ich glaube diese Fälle sind allerdings derart selten, dass sie nicht wirklich als überzeugendes Argument gegen Konstruktoren/Destruktoren taugen.

    dachschaden schrieb:

    Finnegan schrieb:

    Hast du vielleicht ein konkreteres Beispiel, wo man angenehmer ohne Kostruktoren/Destruktoren untewegs ist?

    Ich habe sogar zwei aus meinem Hinterkopf:

    Virtuellen Speicher verwalten klingt schon sehr low-level. In solchen Fällen würde ich wahrscheinlich auch auf ein simples struct mit einer expliziten
    Initialisierungsfunktion zurückgreifen, oder nur sehr leichtgewichtige Klassen entwerfen.
    Für solche Spezialfälle hindert einen C++ übrigens nicht daran eine Klasse ohne expliziten Konstruktor/Destruktor zu
    schreiben (=default) und es genau so zu handhaben wie du es in C machen würdest.
    Meine Vorschläge wie u.a. das mit dem placement new bezogen sich übrigens auch nur auf Klassen, deren Konstruktoren du nicht selbst kontrollierst.

    dachschaden schrieb:

    Sprechende Labels können hier schon relativ helfen:

    if(!object_1_init())
        goto LABEL_ERROR_OBJECT_1_NO_INIT;
    
    if(!object_2_init())
        goto LABEL_ERROR_OBJECT_2_NO_INIT;
    ...
    

    Am Ende hast du dann oft nur eine Stelle, wo du wirklich aufpassen musst.

    Nimms mir nicht übel, aber ich bevorzuge ein simples return und den Aufräumcode an einer einzigen (!) zentralen Stelle im Destruktor,
    anstatt de facto für jede Funktion, wo ich das Objekt verwende einen eigenen "Destruktor" zu schreiben 😉

    Finnegan



  • Finnegan schrieb:

    mach_was() , mach_noch_mehr() und und_noch_mehr() sind wohl unabhängig vom Objekt, da man sie auch aufrufen kann,
    wenn das Objekt nicht existiert (=nicht initialisiert wurde).

    Das war schnell dahingetippter Beispielcode. In der Realität habe ich foo womöglich drei verschiedene Objekte übergeben (Hauptobjekt, Anzahl der Elemente, und Puffer oder so was ähnliches), und die übergebe ich dann auch den Unterfunktionen.

    Finnegan schrieb:

    Falls doch eine Abhängigkeit vom Objekt besteht, ist tatächlich manchmal redundanter Code die einfachste Lösung

    Danke für die Bestätigung, dass ich in solchen Fällen schlechter programmieren muss. Ich bleibe bei C, da habe ich mehr Kontrolle über alles und muss den Quellcode nicht runterziehen.

    Finnegan schrieb:

    Für die wenigen Sonderfälle, wo sich das nicht elegant auflösen lässt, hilft entweder eine zusätzliche Status-Variable, oder man erstellt das Objekt zur Not eben auf dem Heap mit explizitem delete / unique_ptr<>.reset() oder verwendet meine oben vorgeschlagene Stack- new -Variante.
    Ich glaube diese Fälle sind allerdings derart selten, dass sie nicht wirklich als überzeugendes Argument gegen Konstruktoren/Destruktoren taugen.

    Und deswegen hätte ich nur einfach gerne Templates in C, und den ganzen anderen Kram könnt ihr behalten. Deal? 🙂

    Finnegan schrieb:

    Virtuellen Speicher verwalten klingt schon sehr low-level. In solchen Fällen würde ich wahrscheinlich auch auf ein simples struct mit einer expliziten
    Initialisierungsfunktion zurückgreifen, oder nur sehr leichtgewichtige Klassen entwerfen.

    Sag an, genau das ist ja der Fall. Und trotzdem habe ich beim Leichtgewicht noch ein paar Kernel-Calls. Was aber immer noch besser als der Standard ist, was auf Windows bedeutet, sich den Cache und möglich auch den TLB zu ruinieren, weil man jetzt Daten von A nach B kopieren muss, weil der alte Speicher nicht ausreicht.

    (Und auf den TLB kann ich schon keine Rücksicht mehr nehmen, weil Windows im Gegensatz zu Linux die Infrastruktur verkackt hat - aber das ist ein anderes Thema).

    Finnegan schrieb:

    Für solche Spezialfälle hindert einen C++ übrigens nicht daran eine Klasse ohne expliziten Konstruktor/Destruktor zu
    schreiben (=default) und es genau so zu handhaben wie du es in C machen würdest.

    Wird ein leerer Konstruktoraufruf wegoptimiert?



  • dachschaden schrieb:

    Wird ein leerer Konstruktoraufruf wegoptimiert?

    Das macht wohl noch der schlechteste C++-Compiler.



  • Bin ich der einzige, der sich fragt, ob jemand der Diskussionsteilnehmer tatsächlich C++ in der Arbeit für ein großes Projekt verwendet?



  • Finnegan schrieb:

    dachschaden schrieb:

    Kann ich in C++ dem Compiler sagen, dass er nur ein bisschen Speicherbereich auf dem Stack reservieren soll für ein Objekt, und Initialisierung und Freigabe komplett mir überlassen soll? Wenn ja, will ich nichts gesagt haben.

    Ja, ich glaube es gibt kaum etwas, das in dieser wahnwitzigen Sprache nicht geht:

    #include <iostream>
    #include <type_traits>
    
    struct Objekt
    {
    	Objekt(int value) : value{ value } {}
    	void print() const { std::cout << value << std::endl; }
    	int value;
    };
    
    auto main() -> int
    {
    	// Reserviere Speicher für Objekt auf Stack.
    	typename std::aligned_storage<sizeof(Objekt), alignof(Objekt)>::type objekt_auf_stack;
    	// Erstelle Objekt.
    	auto& objekt = *(new (&objekt_auf_stack) Objekt{ 5 });
    	// Verwende Objekt.
    	objekt.print();
    	// Zerstöre Objekt.
    	objekt.~Objekt();
    }
    

    Versus:

    #include <iostream>
    
    struct Objekt
    {
    	Objekt(int value) : value{ value } {}
    	void print() const { std::cout << value << std::endl; }
    	int value;
    };
    
    int main()
    {
    	Objekt obj(5);
    	obj.print();
    	return 0;
    }
    

    No other comment included!


Anmelden zum Antworten