Dieses Kapitel beschreibt den vom LinuxCNC-Team bevorzugten Quellcode-Stil.
1. Keinen Schaden anrichten
Wenn Sie kleine Änderungen am Code in einem anderen als dem unten beschriebenen Stil vornehmen, beachten Sie den lokalen Kodierungsstil. Schnelle Wechsel von einem Kodierungsstil zum anderen beeinträchtigen die Lesbarkeit des Codes.
Checken Sie niemals Code ein, nachdem Sie ihn mit einem Hilfsprogramm wie "indent" bearbeitet haben. Die durch die Einrückung verursachten Änderungen an Leerzeichen erschweren es, den Revisionsverlauf der Datei zu verfolgen.
Verwenden Sie keinen Editor, der unnötige Änderungen am Leerraum vornimmt (z. B. 8 Leerzeichen durch einen Tabstopp in einer ansonsten nicht geänderten Zeile ersetzt oder Zeilen umbricht, die ansonsten nicht geändert werden).
2. Tabstopps
Ein Tabstopp entspricht immer 8 Leerzeichen. Schreiben Sie keinen Code, der nur bei einer abweichenden Einstellung des Tabstopps korrekt angezeigt wird.
3. Einrückung
Verwenden Sie 4 Leerzeichen pro Einrückungsebene. Die Kombination von 8 Leerzeichen in einem Tabulator ist zulässig, aber nicht erforderlich.
4. Setzen von Klammern
Setzen Sie die öffnende Klammer zuletzt auf die Linie, und setzen Sie die schließende Klammer zuerst:
if (x) { // macht irgendetwas der Situation Angepasstes }
Die schließende Klammer steht in einer eigenen Zeile, es sei denn, es folgt eine Fortsetzung derselben Anweisung, z. B. ein while in einer do-Anweisung oder ein else in einer if-Anweisung, wie hier:
do { // etwas Wichtiges } while (x > 0);
und
if (x == y) { // tue das eine } else if (x < y) { // tue das andere } else { // tue ein Drittes }
Durch diese Klammerung wird auch die Anzahl der leeren (oder fast leeren) Zeilen minimiert, wodurch eine größere Menge an Code oder Kommentaren auf einmal in einem Terminal mit fester Größe sichtbar ist.
5. Benennung
C ist eine spartanische Sprache, und so sollte auch Ihre Namensgebung sein. Anders als Modula-2- und Pascal-Programmierer verwenden C-Programmierer keine niedlichen Namen wie DiesIstEineTemporaereZaehlvariable. Ein C-Programmierer würde diese Variable tmp nennen, was viel einfacher zu schreiben und nicht im Geringsten schwieriger zu verstehen ist.
Beschreibende Namen für globale Variablen sind jedoch ein Muss. Eine globale Funktion foo zu nennen, wäre ein offensichtliches, schweres Vergehen.
GLOBALE Variablen (die nur verwendet werden, wenn man sie wirklich braucht) müssen beschreibende Namen haben, ebenso wie globale Funktionen. Wenn Sie eine Funktion haben, um die Anzahl der aktiven Benutzer zu zählen, so sollte sie count_active_users() oder ähnlich heißen, nicht cntusr().
Den Typ einer Funktion in den Namen zu kodieren (so genannte ungarische Notation) ist hirnrissig - der Compiler kennt die Typen ohnehin und kann sie überprüfen, und es verwirrt den Programmierer nur. Kein Wunder, dass Microsoft fehlerhafte Programme macht.
LOCAL-Variablennamen sollten kurz und prägnant sein. Wenn Sie einen zufälligen ganzzahligen Schleifenzähler haben, sollte er wahrscheinlich i heißen. Der Name loop_counter ist unproduktiv, wenn keine Gefahr besteht, dass er missverstanden wird. In ähnlicher Weise kann tmp so gut wie jede Art von Variable sein, die für einen temporären Wert verwendet wird.
Wenn Sie befürchten, Ihre lokalen Variablennamen zu verwechseln, dann haben Sie ein anderes Problem, das als Funktions-Wachstums-Hormon-Gleichgewichts-Syndrom bezeichnet wird. Siehe nächstes Kapitel.
6. Funktionen
Funktionen sollten kurz und knapp sein und nur einen Zweck erfüllen. Sie sollten auf einen oder zwei Bildschirme voller Text passen (die ISO/ANSI-Bildschirmgröße beträgt 80x24, wie wir alle wissen) und nur eine Aufgabe erfüllen, und diese gut.
Die maximale Länge einer Funktion ist umgekehrt proportional zur Komplexität und zum Grad der Einrückung dieser Funktion. Wenn Sie also eine konzeptionell einfache Funktion haben, die nur aus einer einzigen langen (aber einfachen) Fallanweisung besteht, bei der Sie viele kleine Dinge für viele verschiedene Fälle tun müssen, ist es in Ordnung, eine längere Funktion zu haben.
Wenn Sie jedoch eine komplexe Funktion haben und Sie vermuten, dass ein weniger begabter Anfänger nicht einmal versteht, worum es in der Funktion geht, sollten Sie sich um so mehr an die Höchstgrenzen halten. Verwenden Sie Hilfsfunktionen mit aussagekräftigen Namen (Sie können den Compiler bitten, die Funktion inline einzubinden, wenn Sie es für leistungsrelevant halten, und er wird es wahrscheinlich besser machen, als Sie es getan hätten).
Ein weiteres Maß für die Funktion ist die Anzahl der lokalen Variablen. Sie sollte 5-10 nicht überschreiten, oder Sie machen etwas falsch. Überdenken Sie die Funktion, und teilen Sie sie in kleinere Teile auf. Ein menschliches Gehirn kann in der Regel problemlos 7 verschiedene Dinge im Auge behalten, alles darüber hinaus verwirrt es. Sie wissen, dass Sie brillant sind, aber vielleicht würden Sie in zwei Wochen gerne auch noch verstehen, was Sie getan haben.
7. Kommentieren
Kommentare sind gut, aber es besteht auch die Gefahr, dass man zu viel kommentiert. Versuchen Sie NIEMALS, in einem Kommentar zu erklären, WIE Ihr Code funktioniert: es ist viel besser, den Code so zu schreiben, dass die Arbeitsweise offensichtlich ist, und es ist Zeitverschwendung, schlecht geschriebenen Code zu erklären.
Im Allgemeinen sollen Ihre Kommentare sagen, WAS Ihr Code tut, nicht WIE. Ein umrahmter Kommentar, der die Funktion, den Rückgabewert und den Aufrufer beschreibt und über dem Hauptteil steht, ist gut. Versuchen Sie auch, Kommentare innerhalb eines Funktionskörpers zu vermeiden: Wenn die Funktion derart komplex ist, dass Sie Teile davon gesondert kommentieren müssen, sollten Sie vielleicht den Abschnitt Funktionen noch einmal lesen. Sie können kleine Kommentare machen, um auf etwas besonders Kluges (oder Hässliches) hinzuweisen oder zu warnen, aber versuchen Sie, Übertreibungen zu vermeiden. Stellen Sie die Kommentare stattdessen an den Anfang der Funktion und sagen Sie, was sie tut und möglicherweise WARUM sie es tut.
Wenn Kommentare in der Art von /* Fix me */ (engl. für "Reparier mich") verwendet werden, dann beschreiben Sie bitte auch, warum etwas repariert werden muss. Wenn eine Änderung an dem betroffenen Teil des Codes vorgenommen wurde, entfernen Sie entweder den Kommentar oder ergänzen Sie ihn, um anzugeben, dass eine Änderung vorgenommen wurde und getestet werden muss.
8. Shell-Skripte & Makefiles
Nicht jeder hat die gleichen Werkzeuge und Pakete installiert. Einige Leute benutzen vi, andere emacs - Einige wenige vermeiden es sogar, eines der beiden Pakete zu installieren und bevorzugen einen leichtgewichtigen Texteditor wie nano oder den in Midnight Commander integrierten.
gawk versus mawk - Auch hier wird nicht jeder gawk installiert haben, mawk ist fast ein Zehntel so groß und entspricht dennoch dem POSIX AWK Standard. Wenn ein obskurer gawk-spezifischer Befehl benötigt wird, den mawk nicht zur Verfügung stellt, wird das Skript für einige Benutzer nicht funktionieren. Das Gleiche würde für mawk gelten. Kurz gesagt, verwenden Sie lieber den allgemeinen awk-Aufruf als gawk oder mawk.
9. C++-Konventionen
C++-Codierungsstile sind wiederholt Anlass für hitzige Debatten (ähnlich wie der Streit zwischen emacs und vi). Eines ist jedoch sicher: Ein gemeinsamer Stil, der von allen an einem Projekt Beteiligten verwendet wird, führt zu einheitlichem und lesbarem Code.
Namenskonventionen: Konstanten entweder aus #defines oder aus Enumerationen sollten durchgehend in Großbuchstaben geschrieben werden. Begründung: Erleichtert das Erkennen von durch den Präprozessor substituierten Ausdrücke im Quellcode, z.B. EMC_MESSAGE_TYPE.
Klassen und Namensräume (engl. namespace) sollten den ersten Buchstaben eines jeden Wortes groß schreiben und Unterstriche vermeiden. Begründung: Identifiziert Klassen, Konstruktoren und Destruktoren, z.B. GtkWidget.
Methoden (oder Funktionsnamen) sollten den obigen C-Empfehlungen folgen und nicht den Klassennamen enthalten. Begründung: Beibehaltung eines gemeinsamen Stils für C- und C++-Quellen, z.B. get_foo_bar().
Hingegen sind Boolesche Methoden leichter zu lesen, wenn sie keine Unterstriche und ein is Präfix verwenden (nicht zu verwechseln mit Methoden, die einen Booleschen Wert manipulieren). Begründung: Kennzeichnet den Rückgabewert als TRUE oder FALSE und nichts anderes, z.B. isOpen, isHomed.
Verwenden Sie NICHT Not in einem booleschen Namen, es führt nur zu Verwirrung, wenn Sie logische Tests durchführen, z.B. isNotOnLimit oder is_not_on_limit sind BÖSE.
Bei Variablennamen sollte die Verwendung von Großbuchstaben und Unterstrichen vermieden werden, außer bei lokalen oder privaten Namen. Die Verwendung von globalen Variablen sollte so weit wie möglich vermieden werden. Begründung: Stellt klar, was Variablen und was Methoden sind. Öffentlich (engl. public): z.B. axislimit Privat: z.B. maxvelocity_ .
9.1. Spezifische Namenskonventionen für Methoden
Die Begriffe get und set sollten verwendet werden, wenn direkt auf ein Attribut zugegriffen wird. Begründung: Gibt den Zweck der Funktion oder Methode an, z. B. get_foo set_bar.
Für Methoden, die boolesche Attribute beinhalten, ist set & reset vorzuziehen. Begründung: Wie oben. z. B. set_amp_enable reset_amp_fault
Bei rechenintensiven Methoden sollte "compute" als Präfix verwendet werden. Begründung: Zeigt an, dass die Methode rechenintensiv ist und die CPU beansprucht. z.B. compute_PID
Abkürzungen in Namen sollten nach Möglichkeit vermieden werden - eine Ausnahme bilden die Namen lokaler Variablen. Begründung: Klarheit des Codes. z.B. Pointer wird gegenüber ptr bevorzugt compute wird gegenüber cmp bevorzugt und genauso wird compare wiederum gegenüber cmp vorgezogen.
Aufzählungen und anderen Konstanten kann ein gemeinsamer Typname vorangestellt werden, z. B. `enum COLOR { COLOR_RED, COLOR_BLUE }; `.
Übermäßiger Gebrauch von Makros und Defines sollte vermieden werden - Die Verwendung einfacher Methoden oder Funktionen ist vorzuziehen. Begründung: Verbessert den Debugging-Prozess.
Include-Anweisungen Header-Dateien müssen am Anfang einer Quelldatei eingefügt werden und dürfen nicht über den gesamten Textkörper verstreut sein. Sie sollten nach ihrer hierarchischen Position innerhalb des Systems sortiert und gruppiert werden, wobei die Dateien der unteren Ebene zuerst eingebunden werden sollten. Include-Dateipfade sollten NIEMALS absolut sein - verwenden Sie stattdessen das Compiler-Flag -I zur Erweiterung des Suchpfades. Begründung: Header befinden sich nicht auf allen Systemen an der gleichen Stelle.
Bei Zeigern und Referenzen sollte das Referenzsymbol neben dem Variablennamen und nicht neben dem Typnamen stehen. Begründung: Verringert die Verwirrung, z.B. float *x
oder int &i
.
Implizite Tests auf Null sollten mit Ausnahme von booleschen Variablen nicht verwendet werden, z.B. if (spindle_speed != 0)
, NICHT if (spindle_speed)
.
In einem for()-Konstrukt dürfen nur Anweisungen zur Schleifenkontrolle enthalten sein, z.B. sum = 0; for (i = 0; i < 10; i++) { sum += value[i]; } ` +
NICHT: `for (i=0, sum=0; i<10; i++) sum += value[i];
.
Ebenso müssen ausführbare Anweisungen in Konditionalen vermieden werden, z.B. ist if (fd = open(file_name))
böse.
Komplexe bedingte Anweisungen sollten vermieden werden - führen Sie stattdessen temporäre boolesche Variablen ein.
Klammern sollten in mathematischen Ausdrücken reichlich verwendet werden - Verlassen Sie sich nicht auf den Vorrang von Operatoren, wenn eine zusätzliche Klammer die Dinge klarer darstellen würde.
Dateinamen: C++-Quelltexte und -Header verwenden die Erweiterungen .cc und .hh. Die Verwendung von .c und .h ist für einfaches C reserviert. Header sind für Klassen-, Methoden- und Strukturdeklarationen, nicht für Code (es sei denn, die Funktionen sind inline deklariert).
10. Python-Codierungsstandards
Verwenden Sie den PEP 8-Stil für Python-Code.
11. Comp-Codierungs-Standards
Im Deklarationsteil einer .comp-Datei beginnen Sie jede Deklaration mit der ersten Spalte. Fügen Sie zusätzliche Leerzeilen ein, wenn sie zur Gruppierung verwandter Elemente beitragen.
Im Code-Teil einer .comp-Datei ist der normale C-Codierungsstil zu beachten.