Was wären C und C++ ohne Präprozessor?



  • Die Compiler (Parser) könnten auch um einiges schneller sein, wenn es den PP nicht in diesem Umfang gäbe.
    Und auch die IDE-Unterstützung wäre dann präziser (was manchmal für ein Mist bei IntelliSense bzw. sogar Visual Assist rauskommt...).

    C# hat hier (meiner Meinung nach 😉 einen guten Kompromiss gefunden (nur bedingte Kompilierung ist mittels #define möglich - keine Textersetzung).

    Klar habe ich mir auch schon selber Makros gebastelt, um Template-Klassen bzw. Methoden eleganter definieren bzw. aufrufen zu können, aber es besteht schon ein Unterschied ob man Makro-Funktionen bewußt einsetzt oder aber durch irgendwelche Headerdateien (evtl. vom SDK bzw. Fremdbibliotheken) komische Defines aufgezwungen bekommt (bzw. diese dann erst selber wieder per '#undef' abschalten muß).

    Aber bis C++ ein vernünftiges Modul-Konzept hat, wird es leider noch lange dauern...


  • Mod

    hustbaer schrieb:

    SeppJ schrieb:

    hustbaer schrieb:

    Versehentlich kann dir das nur passieren, wenn du dich an keinerlei etablierte Regeln hältst oder gigantisch viel Pech hast.

    Man muss sich also an Regeln halten, weil es gefährlich ist.

    Jetzt sind wir wieder ganz am Anfang: das Leben ist gefährlich. Get over it. 🙄

    Du kannst alles mit Assembler schreiben. Wozu also eine Hochsprache? Get over it. Ach, wozu Assembler. Einfach direkt in Maschinensprache schreiben. Da das ja voll ausreichend ist, ist es egal wie schlecht das zum Entwickeln ist. Jedenfalls sollte die Menschheit niemals auf irgendeinem Gebiet nach Erleichterungen streben.[quote]

    Entweder du kastrierst die Sprache, oder du erlaubst Dinge die "gefährlich" sind. Guck dir bloss mal Reflection in C# oder Java an. Ist auch "gefährlich", dafür kann man einiges damit machen.

    Ganz einfacher Vorschlag: defines die den Scope beachten? Metafunktionen die zur Compilezeit ausgewertet werden, so ähnlich wie Templatemetaprogrammierung, nur imperativ anstatt funktional*? Schon haste sehr viele der Probleme des Präprozessors gelöst und automatische Codegenerierung geht trotzdem noch. Aber nein, bloß keine Veränderung, das Leben könnte ja leichter werden 🙄 .

    *: Dies würde die Schwierigkeit natürlich auf den Compilerbau abwälzen. Aber darum geht es ja: Der Präprozessor war extrem einfach zu implementieren und man brauchte ihn um gewisse Probleme zu lösen. Aber die beste Lösung für den Entwickler der Anwendungsprogramme (nicht aus Sicht des Compilerentwicklers) ist er nicht.



  • *: Dies würde die Schwierigkeit natürlich auf den Compilerbau abwälzen. Aber darum geht es ja: Der Präprozessor war extrem einfach zu implementieren und man brauchte ihn um gewisse Probleme zu lösen. Aber die beste Lösung für den Entwickler der Anwendungsprogramme (nicht aus Sicht des Compilerentwicklers) ist er nicht.

    Das ist auch so ein Punkt der mir bei C++ innerhalb der letzten Monate, in denen ich angefangen habe mich mit Haskell zu beschäftigen, aufgefallen ist: Warum muss ich mir als Programmierer Gedanken um die Codegenerierung machen?
    Warum kann der Compiler nicht selber wissen, welche Datei er schon kompiliert hat?
    Warum muss ich darauf achten, dass Funktionen zumindest deklariert sind, bevor ich sie benutze? Der Kompiler könnte doch einfach nach der Definition in den eingebundenen Dateien suchen und erst wenn er sie überhaupt nicht findet einen Fehler ausgeben.
    Warum kann ich Templates nicht genauso einfach in .h und .cpp Dateien aufteilen, wie jede andere Klasse auch?

    Sind das alles Altlasten aus C oder nimmt das C++ Standardisierungskomitee hier Rücksicht auf die Kompilerhersteller, weil das so schwer zu implementieren ist?



  • Freed schrieb:

    Warum kann ich Templates nicht genauso einfach in .h und .cpp Dateien aufteilen, wie jede andere Klasse auch?

    Es hat ja mal einen Versuch mit dem Schlüsselwort export gegeben, das sich aber äusserst schlecht in die Sprache integriert hat und im Endeffekt mehr Nach- als Vorteile gebracht hat. Why We Can't Afford Export

    Freed schrieb:

    Sind das alles Altlasten aus C oder nimmt das C++ Standardisierungskomitee hier Rücksicht auf die Kompilerhersteller, weil das so schwer zu implementieren ist?

    Das ganze Konzept mit Aufteilung in Header- und Implementierungsdatei ist von C geerbt. Doch eine Umsetzung eines Modulsystems wie in anderen Sprachen ist in C++ nicht so einfach, weil die Sprache um einiges komplexer ist. Getrennte Deklarationen und Definitionen, ODR, verschiedene Speicherklassen (Linkage), Templates, Inline-Funktionen sind alles Dinge, die eng mit dem Header-System von C++ zusammenhängen.


  • Mod

    Freed schrieb:

    Sind das alles Altlasten aus C oder nimmt das C++ Standardisierungskomitee hier Rücksicht auf die Kompilerhersteller, weil das so schwer zu implementieren ist?

    Vermutlich beides. Auf diese Weise ist es jedenfalls ausreichend den Code genau einmal durchzugehen. Das macht den Compilerbau einfach und das Compilieren relativ schnell. Und das mit der Aufteilung der Templates nach .h und .cpp steht im Standard sogar drin, aber das kann nur kaum ein Compiler (soweit ich weiß nur einer), weil es ziemlich aufwändig zu implementieren ist. Daher wurde c++0x auch ausgiebiger mit den Compilerherstellern diskutiert als der Standard mit den Templates, so dass man nicht wieder Features zur Sprache hinzufügt, die nur schwer zu implementieren sind.

    edit: Zu langsam. Und im Gegensatz zu Nexus habe ich noch nicht einmal den schönen Link dabei 😞 .



  • SeppJ schrieb:

    Auf diese Weise ist es jedenfalls ausreichend den Code genau einmal durchzugehen. Das macht den Compilerbau einfach und das Compilieren relativ schnell.

    ja, das war zur Zeit der Erfindung von C vor ca. 39 Jahren ein gewichtiges Argument für den single-pass compiler.

    Inzwischen ist 2011, und Computer sind 3 Größenordnungen schneller geworden. Außerdem gibt es makefiles zum inkrementellen compilieren.



  • !rr!rr__ schrieb:

    Inzwischen ist 2011, und Computer sind 3 Größenordnungen schneller geworden. Außerdem gibt es makefiles zum inkrementellen compilieren.

    Und es gibt Sprachen, die die geforderten Annehmlichkeiten bieten. Warum also C oder C++ nutzen, wenn es was besseres gibt? Nur um rum meckern zu können?
    Ich für meinen Teil finde es so gut wie es ist. Ich weiß was passiert und brauch mir nicht um irgendeine Compilermagie Gedanken machen. Weil alles was passieren kann und soll habe ich selber in der Hand. Man kann damit Käse machen, aber wie sagte jemand mal, deswegen studiert man auch und lässt nicht jeden von der Straße basteln. Und ganz ehrlich, es würde mir kein ruhiges Gefühl bescheren, wenn die Steuerungssyteme in einem Flugzeug in irgendeinem Basic-Dialekt, von einem ungelernten auf der Strasse zusammen geklickt werden. Nur weil es ja geht.
    Ein Messer nutzt man auch, obwohl man sich damit schneiden könnte. Brot und Käse kann man ja auch geschnitten kaufen.



  • Und es gibt Sprachen, die die geforderten Annehmlichkeiten bieten. Warum also C oder C++ nutzen, wenn es was besseres gibt? Nur um rum meckern zu können?

    Ich denke das ist eher als konstruktive Kritik gemeint. Man kann auch ruhig mal ein Sprachdefizit nennen ohne damit gleich die ganze Sprache schlecht zu reden. Ein gutes Modulsystem, das ja lediglich die Codeorganisation beeinflussen würde und nicht irgendwelche anderen Spracheigenschaften, würde C++ sicherlich gut tun.



  • Nick Unbekannt schrieb:

    Weil alles was passieren kann und soll habe ich selber in der Hand.

    das ist je nach Anwendung ein Vorteil, aber nicht immer.

    ob z.B. wirklich jeder Programmierer bessere "custom" garbage-collection Routinen programmieren kann, als die teils durch jahrelange Erfahrung optimierten GCs, die manche andere Sprachen vorinstalliert haben?

    Die chronische Flut an patches auf Sicherheitslücken in manchen gängigen OS und mancher Alltags-Software ist auch nicht gerade ein unanfechtbarer Nachweis für die Überlegenheit dieses Ansatzes.

    Nick Unbekannt schrieb:

    Man kann damit Käse machen, aber wie sagte jemand mal, deswegen studiert man auch

    wieso "auch" ? ich dachte, man studiert nur, um Käse zu machen? 😮



  • !rr!rr__ schrieb:

    ob z.B. wirklich jeder Programmierer bessere "custom" garbage-collection Routinen programmieren kann, als die teils durch jahrelange Erfahrung optimierten GCs, die manche andere Sprachen vorinstalliert haben?

    Dafür gibt es in C++ die Standard-Library. Und gerade die GC wird von vielen als großer Nachteil angesehen.

    !rr!rr__ schrieb:

    Die chronische Flut an patches auf Sicherheitslücken in manchen gängigen OS und mancher Alltags-Software ist auch nicht gerade ein unanfechtbarer Nachweis für die Überlegenheit dieses Ansatzes.

    Was hat das mit der Sprache zu tun?

    !rr!rr__ schrieb:

    wieso "auch" ? ich dachte, man studiert nur, um Käse zu machen? 😮

    Das "auch" bezog sich nicht auf das Käse machen. Sondern, dass du studierst um kein Käse zu machen. Weil du nicht nur weißt das es funktionieren könnte, sondern auch warum. Und mit diesem Wissen kannst du auch die Fälle abdecken, wenn es mal nicht funktionieren sollte.

    Und wie schon angemerkt, wenn man andere Sprachen für geeigneter hält, was hindert einen daran diese zu nutzen? Ich würde auch nicht auf die Idee kommen alles in C++ umsetzen zu wollen. Das dauert einfach manchmal viel zu lange, zu kompliziert, zu Fehleranfällig. Dafür gibt es aber auch wieder andere Fälle, wo mir C++ ideal für erscheint. Und außer für die include Guards sehe ich auch keinen Grund, warum man unbedingt mit dem Präprozessor arbeiten müsste. Und dabei halten sich die Fehlerquellen in Grenzen. Sie unterschieden sich noch nicht mal von den Fehlerquellen, die in C/C++ möglich sind.



  • Ich würde mir ein Modulsystem wünschen. #include ist einfach nervig und das ganze geht auf Kosten der Compilezeit. Ansonsten finde ich den Präpro ein nützliches Werkzeug. Man muss sich halt über die Folgen bewusst sein. Aber gerade bei den Namespace-Gewurschteleien in C++-Headern kann der Präpro den Code leserlich machen

    zB

    #define IsSequence(X) boost::fusion::traits::is_sequence< X >::value
    
        template<typename BigfloatL, typename BigfloatR,
                 bool Lcompiletime = IsSequence(BigfloatL),
                 bool Rcompiletime = IsSequence(BigfloatR)>
        struct linear_expansion_sum;
    
    #undef IsSequence
    

    ist zB deutlich leserlicher als

    template<typename BigfloatL, typename BigfloatR,
                 bool Lcompiletime = boost::fusion::traits::is_sequence< BigfloatL >::value,
                 bool Rcompiletime = boost::fusion::traits::is_sequence< BigfloatL >::value>
        struct linear_expansion_sum;
    

    und vermeidet dank #undef die Nebenwirkungen.

    In einigen Fällen ist sogar Boost.PP ganz nützlich.


Anmelden zum Antworten