Kompilierzeitprobleme mit modularer Programmierung



  • Vor einiger Zeit habe ich hier gefragt, welche Vorteile mehrere Übersetzungseinheiten bringen würden. Eine Antwort war dabei kürzere Kompilierzeit.

    In letzter Zeit hab ich dieses Konzept der modularen Programmierung (mehrere CPP-Dateien, Aufteilung von Klassen in Header und Implementierung) durchgesetzt. Von der angeblichen Compilezeitreduktion hab ich allerdings nichts gespürt. Im Gegenteil.

    Da ich mit verhältnismässig wenigen Dateien (je etwa 20 .h/.cpp) arbeite und viele Dateien einander benötigen und folglich pro Modul relativ viele Header eingebunden werden, braucht jedes Modul jetzt fast so lange wie der gesamte Code vorher (!). Das bedeutet, totale Kompilierzeit ist um die 20 Sekunden.

    Und bevor jemand mit Designproblemen kommt: Ich habe mir das überlegt, ich benötige diese Header wirklich (es handelt sich um ein Spiel, wo z.B. Spielelemente wie Gegner, Waffen, Spieler etc. einander gegenseitig beeinträchtigen und dementsprechend die Klassen gegenseitig bekannt machen müssen).

    Das Problem dabei ist, dass man auch kaum Zeit einsparen kann, wenn z.B. nur eine Übersetzungseinheit geändert wird, weil sich die Header in sehr vielen Modulen auswirken.

    Naja, die Übersichtlichkeit ist vielleicht ein Vorteil, aber diese lange Kompilierzeit ist ziemlich demotivierend. Ist das immer so, oder einfach Pech in meinem Falle, weil viele Abhängigkeiten bestehen?

    Und wird durch die Aufteilung auf mehrere CPP-Dateien der Code bzw. die ausführbare Datei stark grösser?



  • Hallo

    Zunächsteinmal kann man die Abhängigkeiten von Headern untereinander sehr wohl reduzzieren, indem man Forward Declarartions benutzt. Sinnvoll eingesetzt macht man dann im besten Fall nur noch Implementationen von Headern abhängig.

    Wenn du natürlich ständig an zentralen Headerdateien etwas änderst, dann ist es kein Wunder das fast alle anderen Übersetzungseinheiten auch neu kompiliert werden müßen. In Headerdateien sollte nur das nötigste stehen, eben was als feststehendes Interface nach außen hin bekannt sein muß.

    bis bald
    akari



  • Außerdem gibt es bei einigen Compilern die Option, Headerdateien vorzukompilieren; wenn du das richtig einsetzt (und akaris Hinweis befolgst), sollte sich deine Kompilierzeit deutlich reduzieren.



  • Nexus schrieb:

    Das Problem dabei ist, dass man auch kaum Zeit einsparen kann, wenn z.B. nur eine Übersetzungseinheit geändert wird, weil sich die Header in sehr vielen Modulen auswirken.

    Wenn du nur eine Übersetzungseinheit (.cpp-Datei) änderst, musst du auch nur diese neu kompilieren. Nur wenn du einen Header änderst, musst du alle abhängigen ÜEn neu kompilieren.

    Nexus schrieb:

    Naja, die Übersichtlichkeit ist vielleicht ein Vorteil, aber diese lange Kompilierzeit ist ziemlich demotivierend. Ist das immer so, oder einfach Pech in meinem Falle, weil viele Abhängigkeiten bestehen?

    Das ist kein Pech, sondern etwas, dass du durch Design beheben kannst (auch wenn du das nicht hören willst).

    Wenn du ein ordentliches Design hast, sollten die Schnittstellen der Klassen schon längst feststehen. Mit dem Pimpl-Idiom kannst du die Implementierung von der Schnittstelle trennen. Damit musst du nur noch dann mehrere ÜEn kompilieren, wenn sich eine Schnittstelle ändert. Und das sollte nicht passieren (s.o.).

    Nexus schrieb:

    Und wird durch die Aufteilung auf mehrere CPP-Dateien der Code bzw. die ausführbare Datei stark grösser?

    Nein.



  • Danke für eure Antworten.

    @ akari:
    Was Forward Declarations betrifft: Da hab ich auch einige drin. Jedoch impliziert das immer, dass man als Member nur Zeiger oder Referenzen haben kann. Das führt dazu, dass ich für jede kleine Klasse, die mehrfach vorkommen kann, einen eigenen Kopierkonstruktor, Destruktor und Zuweisungsoperator implementieren muss - was meiner Ansicht nach nicht gerade optimal ist. Zudem hat man mit Zeigern eh viele Probleme, also wenn man etwas gerade so gut mit Stackvariablen erledigen kann, tut man das meiner Ansicht nach besser.

    Und in den Headerdateien hab ich eigentlich auch nur das Nötigste (eben die Klassendefinition selber + eventuelle globale Funktions- bzw. Variablendeklarationen).

    @ audacia:
    Aber das nützt doch auch nur, wenn man die Headerdateien nicht ändert. Bei einem Spielaufbau kommt es relativ häufig vor, dass man merkt "ah da muss ich noch eine Funktion hinzufügen, und dort fehlt ein spezifischer Konstruktor"...

    @ MFK:
    Wie gesagt, bei meinem Design sehe ich keine grossen Möglichkeiten zur Optimierung mehr (zu Vorwärtsdeklarationen siehe oben).
    Und weshalb wird die ausführbare Datei nicht stark grösser? Wird der Code vom Linker wieder schön zusammengeführt und vereinheitlicht? Sorry da kenn ich mich nicht sehr genau aus...

    Wie macht ihr das eigentlich? #include -Direktiven fast nur in CPP-Dateien?



  • Nexus schrieb:

    Jedoch impliziert das immer, dass man als Member nur Zeiger oder Referenzen haben kann. Das führt dazu, dass ich für jede kleine Klasse, die mehrfach vorkommen kann, einen eigenen Kopierkonstruktor, Destruktor und Zuweisungsoperator implementieren muss - was meiner Ansicht nach nicht gerade optimal ist. Zudem hat man mit Zeigern eh viele Probleme, also wenn man etwas gerade so gut mit Stackvariablen erledigen kann, tut man das meiner Ansicht nach besser.

    Hast du dir das Pimpl-Idiom mal angesehen?

    Nexus schrieb:

    Aber das nützt doch auch nur, wenn man die Headerdateien nicht ändert. Bei einem Spielaufbau kommt es relativ häufig vor, dass man merkt "ah da muss ich noch eine Funktion hinzufügen, und dort fehlt ein spezifischer Konstruktor"...

    Das klingt danach, als hättest du angefangen zu programmieren, bevor das Design fertig war. Das sollte nicht "relativ häufig" vorkommen, sondern ziemlich selten. Wenn du natürlich Planung und Implementierung "gleichzeitig" machst, solltest du dich über erhöhte Kompilierungszeiten nicht wundern.

    Nexus schrieb:

    Und weshalb wird die ausführbare Datei nicht stark grösser? Wird der Code vom Linker wieder schön zusammengeführt und vereinheitlicht?

    Erklär doch mal, warum du glaubst, dass der Code überhaupt nennenswert größer werden sollte. Von Inline-Funktionen mal abgesehen, steckt doch jede Funktion in genau einer Übersetzungseinheit.



  • Nexus schrieb:

    Wie macht ihr das eigentlich? #include -Direktiven fast nur in CPP-Dateien?

    Sollte nicht durch saubere Trennung der Klassen mit Schnittstellen vermieden werden, dass du in Headerdateien andere Header einbindest?

    In der Headerdatei sollte doch dann nur die Klasse als Schnittstelle stehen und bei der Implementation der Klassenmethoden (in einer cpp Datei) sollte dann auf andere Header verwiesen werden. Sonst bindest du den anderen Header immer mit ein, wenn du eine bestimmte Headerdatei einbindest, die andere Headerdateien includiert.

    Also so weit ich weiß, sollte man #include-Direktiven möglichst nur in cpp-Dateien verwenden. Sonst mal das Design überdenken...



  • MFK schrieb:

    Hast du dir das Pimpl-Idiom mal angesehen?

    Ich hab es mir einmal angeschaut (auf en.wikibooks.org), aber irgendwie bin ich nicht so richtig davon überzeugt. Erstens müsste ich dann für jede Klasse noch eine separate mit dem Body erzeugen, und zweitens ist es gerade bei meinen Anwendungen nicht so extrem tragisch, wenn man als Anwender etwas sieht, das eigentlich zur Implementation gehört. Aber der Vorteil der Kompilierungsverkürzung ist natürlich schon vorhanden.

    MFK schrieb:

    Das klingt danach, als hättest du angefangen zu programmieren, bevor das Design fertig war. Das sollte nicht "relativ häufig" vorkommen, sondern ziemlich selten. Wenn du natürlich Planung und Implementierung "gleichzeitig" machst, solltest du dich über erhöhte Kompilierungszeiten nicht wundern.

    Nein, das grobe Design stand eigentlich schon vorher. Ich weiss nicht, ob du selber schon Spiele programmiert hast, aber es kann schon vorkommen, dass man mitten in der Programmierung merkt, dass man noch ein neues Spielelement hinzufügen will, welches das halbe Spielkonzept über den Haufen wirft. Aber du hast Recht, häufig geschieht das eigentlich nicht.

    MFK schrieb:

    Erklär doch mal, warum du glaubst, dass der Code überhaupt nennenswert größer werden sollte. Von Inline-Funktionen mal abgesehen, steckt doch jede Funktion in genau einer Übersetzungseinheit.

    Naja, ich war mir nur nicht sicher, weil ich mich eben nicht so gut auskenne bei der Codegenerierung und Linkage... Aber dann brauche ich mir ja keine Gedanken darum zu machen.

    #include in Header schrieb:

    Also so weit ich weiß, sollte man #include-Direktiven möglichst nur in cpp-Dateien verwenden. Sonst mal das Design überdenken...

    Ich meine nur, wenn man z.B. eine Klasse hat, die als Member Objekte von anderen Klassen besitzt, dann kann man die benötigten Klassen doch gerade in dieser Headerdatei einbinden, oder?

    Aber die ganze Thematik scheint mir doch schwerwiegender, als ich zu Beginn dachte. Früher hatte ich immer eine CPP-Datei, und alles andere in Header ausgelagert. Da brauchte ich mir weder Gedanken um Linker-Mehrfachdefinitionen, noch um erhöhte Kompilierzeiten, spezielles Einbinden von Headern oder Pimpl-Idioms zu machen. Klar war es vielleicht nicht das beste Design, aber immerhin kam man noch zum Programmieren 😉

    Es ist nicht so, dass ich den Vorteil der modular-objektorientierten Programmierung nicht sehe. Ich kann durchaus nachvollziehen, dass man bei wirklich grossen Projekten (z.B. in der Softwareentwicklung) grossen Nutzen davon hat, aber mir persönlich scheint es eher mehr Probleme zu bereiten...



  • Trotzdem kann ich bei dir nicht nachvollziehen, warum du bei modularer Programmierung größere Kompilierzeiten hast als bei einer Datei.
    Denn bei Änderung an einer Datei müßte ja der ganze Code neu kompiliert werden (und dies jedesmal, anstatt nur einige wenige kleinere Dateien).

    Welchen Compiler setzt du denn ein, evtl. solltest du mal - wie vorgeschlagen - vorkompilierte Headerdateien einsetzen?

    Vllt. könntest du ja mal dein Klassendesign exemplarisch hier aufzeigen, d.h. die Header und deren Includes.

    Aus

    Das führt dazu, dass ich für jede kleine Klasse, die mehrfach vorkommen kann, einen eigenen Kopierkonstruktor, Destruktor und Zuweisungsoperator implementieren muss - was meiner Ansicht nach nicht gerade optimal ist.

    schließe ich, daß du noch einigen Nachholbedarf beim Design und deren Anwendung in C++ hast, denn das Verwenden von eingebetteten Objekten führt ja zu Kopien, so daß keine Eindeutigkeit der Objekte mehr gewährleistet ist.

    Ein gutes Design führt oftmals insgesamt zu kleinerem Code (trotz expliziter Implementierung bestimmter Methoden).

    Da ich 5 Jahre in einer Computerspiele-Firma gearbeitet habe, kenne ich mich mit den Problematiken bei großen Spieleprojekten sehr gut aus.



  • Th69 schrieb:

    Trotzdem kann ich bei dir nicht nachvollziehen, warum du bei modularer Programmierung größere Kompilierzeiten hast als bei einer Datei.
    Denn bei Änderung an einer Datei müßte ja der ganze Code neu kompiliert werden (und dies jedesmal, anstatt nur einige wenige kleinere Dateien).

    Ja, aber wie gesagt gibt es bei mir relativ viele Module, die voneinander abhängen (z.B. Spieler - Gegner, Spieler - Game, etc.)... Siehe dazu unten das Design.

    Th89 schrieb:

    Vllt. könntest du ja mal dein Klassendesign exemplarisch hier aufzeigen, d.h. die Header und deren Includes.

    Gut möglich, dass das Design so nicht gut ist, aber damit hab ich eben noch nicht so grosse Erfahrung (bzw. das ist mein erstes Spiel, wo ich richtig modular programmiere und aufs Design achte).

    Also es handelt sich um ein Jump'n'Run. Ich muss noch erwähnen, dass die Klassen Enemy und Weapon noch nicht implementiert sind, ich hab sie jedoch so im Design vorgesehen.

    Zuerst mal die Vererbungshierarchie:

    [b]Master[/b] (verwaltet alles)
            im Moment noch alles statische Member,
             soll jedoch noch Singleton werden
            sozusagen global, von überall zugreifbar
    
               [b]Surface[/b] (Oberfläche)
                         |
       +----------+------+-------+--------+
       |          |              |        |
    [b]MainMenu[/b]   [b]InfoMenu[/b]   ..    [b]Game[/b]    [b]Editor[/b]
    
             [b]Object[/b] (Spielelemente,                      [b]Tile[/b]
          die Kollision berücksichtigen)           (Bausteine der Welt)
                     |
      +--------------+---------------+
      |              |               |
    [b]Player[/b]         [b]Enemy[/b]          [b]Weapon[/b]
                            (abgefeuerte Waffen)
    

    Zusätzlich noch einige weniger relevante Klassen.

    Hier die Membertypen von wichtigen Klassen:

    Master
    --> Surface* (Basisklassenzeiger (Polymorphie))
    --> Grafikklassen (Fenster, Event, etc.), die überall benötigt werden
    
    Game
    --> Player
    --> Enemy
    --> Weapon
    --> Tile
    

    Nun ist es so, dass Kollisionsabfragen in Object passieren, d.h. da werden schon mal die Klassen Tile und Game (aktuelle Oberfläche) benötigt. Bei den Klassen Player , Enemy und Weapon muss ich wahrscheinlich eine Dreiecksabhängigkeit einrichten, weil sich diese Klassen jeweils gegenseitig beeinträchtigen können.
    Game selber benötigt natürlich alle Klassen, die Typen der Member sind.
    Was ein Problem darstellt, ist die Tatsache, dass z.B. Player indirekt über die Basisklasse Object auf Game Zugriff haben muss, um z.B. Kollisionsabfragen mit den Tiles zu handhaben. Ich habe das mit einem Zeiger gelöst, jedoch hab ich momentan das Problem, dass eine Vorwärtsdeklaration nicht ausreicht, weil ein dynamic_cast<Game*>(Surface*) durchgeführt werden muss. Aber das lässt sich evtl. dadurch lösen, dass beim Erstellen der Player -Instanz ein Zeiger auf Game übergeben wird. Was wiederum nicht geht, weil im Konstruktor von Game ja auch Player konstruiert wird, und der this -Zeiger von Game zu diesem Zeitpunkt noch auf die Basisklasse Surface verweist... 🙄

    Grundsätzlich wäre fast alles mit Vorwärtsdeklarationen und Zeigern als Member zu lösen. Was ich jedoch unnötig finde, weil auf ich eigene Speicherverwaltung lieber verzichte, wo sichs vermeiden lässt (und wie gesagt auf The Big Three). Oder ist das die gängige Variante (Pimpl scheint mir arg übertrieben für mein verhältnismässig kleines Spiel)?

    Dazu:

    Th89 schrieb:

    Aus

    Das führt dazu, dass ich für jede kleine Klasse, die mehrfach vorkommen kann, einen eigenen Kopierkonstruktor, Destruktor und Zuweisungsoperator implementieren muss - was meiner Ansicht nach nicht gerade optimal ist.

    schließe ich, daß du noch einigen Nachholbedarf beim Design und deren Anwendung in C++ hast

    Das will ich auch nicht bestreiten 😉
    Aber was ist denn daran so falsch?
    Und was meinst du genau mit folgender Aussage?

    Th89 schrieb:

    denn das Verwenden von eingebetteten Objekten führt ja zu Kopien, so daß keine Eindeutigkeit der Objekte mehr gewährleistet ist.



  • Nexus schrieb:

    Was Forward Declarations betrifft: Da hab ich auch einige drin. Jedoch impliziert das immer, dass man als Member nur Zeiger oder Referenzen haben kann. Das führt dazu, dass ich für jede kleine Klasse, die mehrfach vorkommen kann, einen eigenen Kopierkonstruktor, Destruktor und Zuweisungsoperator implementieren muss - was meiner Ansicht nach nicht gerade optimal ist.

    Warum sollte man wegen Forward Declaration mehr Kopierkonstruktor, Destruktor und Zuweisungsoperator implementieren müssen? Thema Smartpointer...

    shared_ptr<T> kann für einen unvollständigen Typ T verwendet werden:

    class X; // forward declaration
    shared_ptr<X> pX; // "X *"
    

    Zum Dereferenzieren und Freigeben von pX muss X aber vollständig bekannt sein - boost prüft das und wirft andernfalls eine Compile-Fehlermeldung.

    Nexus schrieb:

    Wie macht ihr das eigentlich? #include -Direktiven fast nur in CPP-Dateien?

    In meinen privaten Projekten: Ja.
    In Projekten an der Arbeit: Kommt auf den Codestil an, auch wenn ich immer versuchen werde, soviel wie möglich in den Source zu ziehen.

    cu André



  • Nexus schrieb:

    Master (verwaltet alles)
    im Moment noch alles statische Member,
    soll jedoch noch Singleton werden
    sozusagen global, von überall zugreifbar

    Hmm. Das würde ich schon mal anderst lösen. Mach da lieber ein Objekt draus, dass du dann direkt in der main initialisierst und hald einen Zeiger, oder eine Referenz an die restlichen Klassen übergibst, damit du darauf zugreifen kannst.

    Fände ich sauberer, als da mit Singletons zu arbeiten.


  • Mod

    drakon schrieb:

    Nexus schrieb:

    Master (verwaltet alles)
    im Moment noch alles statische Member,
    soll jedoch noch Singleton werden
    sozusagen global, von überall zugreifbar

    Hmm. Das würde ich schon mal anderst lösen. Mach da lieber ein Objekt draus, dass du dann direkt in der main initialisierst und hald einen Zeiger, oder eine Referenz an die restlichen Klassen übergibst, damit du darauf zugreifen kannst.

    Fände ich sauberer, als da mit Singletons zu arbeiten.

    Das hättest du anders zitieren sollen. So macht es den Eindruck, als wollte Nexus sich als Dichter betätigen.



  • camper schrieb:

    Das hättest du anders zitieren sollen. So macht es den Eindruck, als wollte Nexus sich als Dichter betätigen.

    😃
    Entweder ich verstehe dich falsch, oder du hast ein Smylie vergessen. 😉



  • drakon schrieb:

    Fände ich sauberer, als da mit Singletons zu arbeiten.

    Ich finde das hört sich schon ziemlich nach Singleton an. Überall Zeiger rumzureichen finde ich da viel viel ekliger.

    Nexus schrieb:

    Grundsätzlich wäre fast alles mit Vorwärtsdeklarationen und Zeigern als Member zu lösen.

    Mach das bloß nicht. Ist imo unsinning, genauso mit Smartpointern. Ich würde nie "reine" Membervariablen gegen Kompilierzeit eintauschen.


  • Mod

    drakon schrieb:

    camper schrieb:

    Das hättest du anders zitieren sollen. So macht es den Eindruck, als wollte Nexus sich als Dichter betätigen.

    😃
    Entweder ich verstehe dich falsch, oder du hast ein Smylie vergessen. 😉

    Du irrst, indem du recht hast.



  • camper schrieb:

    drakon schrieb:

    camper schrieb:

    Das hättest du anders zitieren sollen. So macht es den Eindruck, als wollte Nexus sich als Dichter betätigen.

    😃
    Entweder ich verstehe dich falsch, oder du hast ein Smylie vergessen. 😉

    Du irrst, indem du recht hast.

    😕



  • camper schrieb:

    Das hättest du anders zitieren sollen. So macht es den Eindruck, als wollte Nexus sich als Dichter betätigen.

    Dass man es als Gedicht erkennen könnte, war eigentlich nie meine Absicht - aber meine stichwortartige Beschreibung kommt da halt schon nahe 😉

    Badestrand schrieb:

    drakon schrieb:

    Fände ich sauberer, als da mit Singletons zu arbeiten.

    Ich finde das hört sich schon ziemlich nach Singleton an. Überall Zeiger rumzureichen finde ich da viel viel ekliger.

    Nexus schrieb:

    Grundsätzlich wäre fast alles mit Vorwärtsdeklarationen und Zeigern als Member zu lösen.

    Mach das bloß nicht. Ist imo unsinning, genauso mit Smartpointern. Ich würde nie "reine" Membervariablen gegen Kompilierzeit eintauschen.

    Puh, endlich mal jemand, der das Ganze so sieht wie ich 😉
    Was das Zeiger-Rumreichen betrifft, wäre das wirklich total eklig, da dann jede Funktion, die entweder Eingaben oder Bildschirmausgaben handhabt, noch einen zusätzlichen Parameter hätte (oder jede Klasse noch einen zusätzlichen Zeiger). Zum Teil wären auch noch ganz andere Funktionen betroffen, weil z.B. auch eine Instanz einer Zufallsgeneratorklasse in Master steht. Das würde sich hinten und vorne nicht lohnen.

    Naja, ich hab mich einfach gewundert; wenn man beginnt, modular zu programmieren, sind plötzlich wieder Unmengen "Hilfsmittel" erforderlich, dass man es nur schafft, nicht schlechtere Voraussetzungen als bei nicht-modularer Programmierung zu haben... Wahrscheinlich nehm ich doch lieber die längere Kompilierzeit in Kauf...

    Ach ja, noch was: Gibt es so was wie meine Masterklasse häufig in der Praxis? Ich finde es sehr praktisch, da ich in main() gerade mal zwei Anweisungen habe. Das Unschöne ist vielleicht, dass Master nur statische Member und Methoden hat, aber wie gesagt mach ich da vielleicht noch ein Singleton draus.



  • Nexus schrieb:

    Naja, ich hab mich einfach gewundert; wenn man beginnt, modular zu programmieren, sind plötzlich wieder Unmengen "Hilfsmittel" erforderlich, dass man es nur schafft, nicht schlechtere Voraussetzungen als bei nicht-modularer Programmierung zu haben... Wahrscheinlich nehm ich doch lieber die längere Kompilierzeit in Kauf...

    Was du aber bedenken solltest: In der Anfangsphase wo man viele Interfaces aendert und oft nur mit mockups herumwirft und jedweder code total instabil (aus aenderungssicht - nicht bug-sicht) ist, sind mehrere uebersetzungseinheiten natuerlich doof, da so das linken laenger dauert.

    sobald du aber einen stand erreicht hast wo ein teil des codes interface-stabil ist, sinken die compiletimes rapide. denk einfach nur daran, dass du ja einen grossteil der dateien nachdem sie einmal fertig sind kaum noch anfasst. am anfang, wo du aber jede datei dauernd aenderst - ists natuerlich etwas stoerend.

    und auch aktuell, also trotz grossem recompile kann ich mir nicht vorstellen dass die compilezeiten soviel groesser sind. denn alles was mehr gemacht wird sind mehrmals header parsen -> und das laesst sich mit precompiled headers sogar umgehen.

    wenn du compilezeiten extrem senken willst, dann schau dir das pimpl-idiom an. ich bin kein grosser fan davon, wenn es aber um reduzierung von abhaengigkeiten geht ist es einfach super.

    20 sekunden recompile deutet auf entweder einen sehr langsamen rechner hin oder auf viel code. und bei viel code: warum aenderst du die zentralen stellen dauernd?



  • und bei viel code: warum aenderst du die zentralen stellen dauernd?

    :p

    Weisst du, was mir da manchmal passiert?
    Ich schaue mal ein wenig im Core, ob alles stimmt usw. Und aus reiner Gewohnheit mache ich da auch aus dem Reflex raus ctrl+s.. Und ich bemerke meinen Fehler auch sogleich beim drücken. *grml*
    Lösung: ctrl+F7 und mal was trinken gehen. 😃



  • drakon schrieb:

    und bei viel code: warum aenderst du die zentralen stellen dauernd?

    :p

    Weisst du, was mir da manchmal passiert?
    Ich schaue mal ein wenig im Core, ob alles stimmt usw. Und aus reiner Gewohnheit mache ich da auch aus dem Reflex raus ctrl+s.. Und ich bemerke meinen Fehler auch sogleich beim drücken. *grml*
    Lösung: ctrl+F7 und mal was trinken gehen. 😃

    dann ist dein editor komisch... wenn es keine aenderung gibt, sollte er die datei nicht modifizieren, auch bei einem ctrl+s nicht...

    aber klar, sowas passiert mal dass man aus versehen eine zentrale datei unnoetigerweise "aendert" - aber das ist hoffentlich ausnahme und nicht regel 😉


Anmelden zum Antworten