Um die Quellcodes einheitlich und lesbar zu gestalten, müssen die in diesem Dokument festgelegten Richtlinien für die Gestaltung verwendet werden.
Dieses Dokument gliedert sich in folgende Abschnitte:
Es sind die C/C++-Compiler von Microsoft und Borland zulässig. Es dürfen darum keine automatischen Registervariablen verwendet werden und die Daten müssen byteweise ausgerichtet werden.
Wenn nicht von vorhandenen Prototypen anders verlangt, müssen immer die Datentypen aus der Headerdatei typedefs.h verwendet werden. Folgende Datentypen sind definiert:
Definierte Datentypen | ||
Typname | Größe in Bytes | Notiz |
SInt8_t | 1 | |
UInt8_t | 1 | |
SInt16_t | 2 | |
UInt16_t | 2 | |
SInt32_t | 4 | |
SInt32_t | 4 | |
Char_t | 1 | |
Byte_t | 1 | |
Boolean_t | 1 | Werte sind TRUE/FALSE. Definition kann zwischen verschiedenen C-Compilern variieren |
Data_t | - | void-Zeiger |
Flags_t | 2 | Für jede Art von Flags zu verwenden |
String_t | - | Zeiger auf eine Zeichenkette. Definiert als const char* |
HEnum_t | 4 | Handle für Iterationen. |
TimeCode_t | 4 | Enthält einen Zeitcode in Sekunden seit dem 1. Jänner 1901 |
In den Source-Codes können für Testzwecke Debug-Makros eingebaut werden. Es sind allerdings nur die Makros aus der Datei debug.h gültig. Die dazugehörige Library trägt den Namen chlibdb.dll (bzw. die Version für GUI-Tracing namens 'chlibdb_gui.dll').
Diese Makros müssen gegebenenfalls in den Quelltextdateien definiert werden. Die Definition muß vor der ersten #include-Anweisung stattfinden.
__MIDL_PASS__
Dieses Makro muß bei einem MIDL-Compiler-Lauf definiert sein.
__BUILD_LIBRARYS__
Muß bei der Erstellung der Bibliotheken (Quellcode für API-DLL's) definiert sein. Hiermit wird das Makro API_EXPORT korrekt expandiert.
__RPC_PROCEDURES__
Wenn eine Sourcecodedatei vom MIDL-Compiler erzeugte Headerdateien einbindet, muß dieses Makro definiert sein.
Alle Teile des Quellcodes (insbesondere Variablen- und Funktionsnamen) sind grundsätzlich in Englisch zu verfassen. Einzige Ausnahme sind Kommentare, die auch in Deutsch verfaßt werden können.
Im Quellcode dürfen keine Tabulatorzeichen gespeichert werden. Diese Editoroption muß deaktiviert werden. Der Editor muß die Tabulatorschritte mit Leerzeichen auffüllen. Die Tabulatoreinrückung (mit Leerzeichen gefüllt) beträgt 3 Zeichen.
Ein Modul enthält verschiedene Abschnitte. Unter einem Abschnitt versteht man z.B. Include-Dateien, Globale Variablen, Funktionsprototypen.
Abschnitte werden folgendermaßen begonnen:
/*s--------------------------------------------------------------------- Abschnittname ----------------------------------------------------------------------*/
Umfangreichere Abschnitte (z.B. ab jetzt die Implementation einer bestimmten Klasse) werden folgendermaßen deklariert:
/*m--------------------------------------------------------------------- *********************** Abschnittname ************************ ----------------------------------------------------------------------*/
Bei Blöcken wird immer zwei Zeichen eingerückt. Dies entspricht folgender Darstellung:
for( i=0; i<x; i++ ) { if( i==y ) { // // Kommentar // ... } }
Wenn die Header-Datei von C benutzt werden kann, müssen die geeigneten Schutzmechanismen (sentries) für die Prototypen eingefügt werden:
#if defined (__cplusplus) extern "C" { #endif// // Funktionsprototypen //#if defined (__cplusplus) } #endif
Die Implementationen von Funktionen haben folgende Form:
Rückgabewert Funktionsname ( Typ param1, Typ param2 ) { ... Code ... }
Funktionsnamen beginnen grundsätzlich mit Großbuchstaben. Einzelne Worte innerhalb des Funktionsnamens sind durch einen großen Anfangsbuchstaben zu kennzeichnen. z.B. SelectListboxString. Innerhalb von Funktionsnamen kein _ verwenden.
Der Rückgabewert einer Funktion muß immer vom Typ Retcode_t sein. Ausnahmen sind lokale Funktionen (siehe Funktionsprototypen).
Jede Funktion muß einen Funktionskopf mit einer Funktionsbeschreibung verfügen:
/*f-------------------------------------------------------------------- Funktionsname Description: (1) Return: (2) Called functions (Module/Name): (3) Memory allocation: (4) Author: (5) ---------------------------------------------------------------------*/
(1) Kurze Beschreibung der Vorgänge in der Funktion. Besonderheiten (Algorithmen, ev. Auswirkungen auf globales Programm) müssen hier vermerkt werden.
(2) Alle möglichen Rückgabewerte werden hier aufgelistet. Bei Bedarf ist zu vermerken, wie dieser Rückgabewert zustande gekommen ist.
(3) Eine Liste aller aufgerufenen Funktionen in der Form Modul/Name. Als Modul gilt eine Quellcodedatei oder bei Produkten von Fremdherstellern der Name der Bibliothek. Werden C++-Funktionen aufgerufen, muß auch die Klasse vermerkt werden (z.B. TEdit::GetText)
(4) Hier wird vermerkt, ob die Funktion Speicher allokiert und wenn ja, wo dieser wieder freigegeben wird.
(5) Der Name des Autors.
Ein gültiger Funktionskopf sieht folgendermaßen aus:
/*f-------------------------------------------------------------------- BuildMessageDataForModules Description: Erzeugt eine Nachrichtendatenstruktur, die den Weiterleitungs- modulen direkt übergeben werden kann. ACHTUNG: Es wird Speicher reserviert, der extern freigegeben werden muß. Return: Bei Erfolg RC_SUCCESS, sonst - RC_NOT_ENOUGH_MEMORY, wenn nicht genügend Speicherplatz vorhanden ist. - RC_NO_DATA, wenn die Nachricht nicht in der Datenbank verzeichnet ist. - Rückgabewert von IMBGetApplication Called functions (Module/Name): apiserver.cpp/GetMessage apiserver.cpp/RPCGetCustomerInformation apiserver.cpp/InitializeReceiverData imdb.dll/IMDBFreeMemory imdb.dll/IMDBGetApplication C++/new C++/delete[] Win32/lstrcpy Memory allocation: Dynamischer Speicher wird reserviert und über mMessage->data zurückgegeben. Diese Speicher muß vom Aufrufer wieder freigegeben werden. Author: Alexander Berger ---------------------------------------------------------------------*/
Der Funktionsprototyp benötigt keinen Kopf.
Prototypen von Funktionen werden immer in folgender Form angegeben:
Rückgabewert Funktionsname( Typ param1, Typ param2, Typ param3, Typ param4, Typ param5 );
Lokale Funktionen sind als solche zu kennzeichnen. Dies wird vom Makro LOCAL übernommen, welches dem Funktionsprototyp vorangestellt wird (z.B. LOCAL Rückgabewert Funktionsname(...); ).
Parameter müssen immer in folgender Anordnung angegeben werden:
Die Anzahl globaler Variablen sollte möglichst gering gehalten werden. Wenn eine globale Variable benötigt wird, sollte sie als globaler Zeiger auf ein Objekt statt als globale Instanz definiert werden. Zur Laufzeit wird dann das Objekt dynamisch zugewiesen und wieder freigegeben.
Der Code von Headerdateien wird innerhalb eines #if-Präprozessorstatements gestellt:
#ifndef __FILENAME_H__ #define __FILENAME_H__...Code...#endif // __FILENAME_H__
Es muß immer der Dateinamen eingesetzt werden.
Werden Headerdateien mittels #include eingebunden, muß zwischen Standard-Headerdateien und Projekt-Headerdateien unterschieden werden. Bei Standarddateien wird der Dateinamen zwischen Klein-Größer-Zeichen geschrieben, bei Projektdateien in Hochkomma. Außerdem dürfen Include-Anweisungen nur zu Beginn einer Datei stehen (bzw. als erste Abschnitt nach dem Header)
z.B.
/*s--------------------------------------------------------------------- Include Files ----------------------------------------------------------------------*/ #include "prjfile.h" #include <stdfile.h>
Kommentare sollten an jede sinnvolle Stelle gesetzt werden und den folgenden Programmablauf erklären, aber nicht wiederholen. Kommentare besitzen mindestens 3 Zeilen und haben folgendes Aussehen:
// // Kommentar... //
oder
// // Kommentar Zeile 1 // Kommentar Zeile 2 //
Kommentare werden immer bis zum aktuellen Block eingerückt (siehe dazu Beispiel in Abschnitt Blöcke).
Wenn mehrere Deklarationen mit Zeigern und Referenzen vorhanden sind, müssen diese folgendermaßen formuliert werden:
UInt32_t* a; UInt32_t* b; UInt32_t* c;
und nicht
UInt32_t* a, * b, * c;
und ganz bestimmt nicht so:
UInt32_t* a, * b, * c;
Jedes Quellcodemodul beginnt mit folgendem Header:
/*h----------------------------------------------------------------------Copyright (c)1998 Cynex. All rights reserved.$Archive: $$Revision: $$Author: $$Date: $Compiler: (1)Contents: (2)$NoKeywords: $------------------------------------------------------------------------*/
Folgende Teile sind vom Programmierer auszufüllen:
(1) Der benötigte Compiler für diese Datei. Spezielle, compilerspezifische Eigenschaften werden ebenfalls hier vermerkt. z.B. Borland C++ (ab Version 4.0).
(2) Kurze Beschreibung des Inhalts der Source-Code Datei.
Die restlichen Einträge werden von der Versionkontrolle errweitert.
Eine in die Versionskontrolle archivierte Datei würde folgenden Header besitzen:
Beispiel:
/*h-----------------------------------------------------------------------Copyright (c)1998 Cynex. All rights reserved.$Archive: /PC/Systemkern/Quellcode/Imagine-Header/cynex.h $$Revision: 3 $$Author: Alexb $$Date: 30.08.96 17:47 $Compiler: C/C++Contents: General definitions for all Cynex projects. This header includes several other files$NoKeywords: $------------------------------------------------------------------------*/
Zeiger und Referenzen gehören zu den Typen, nicht zur Variablen. Also:
int* j;double& xAlias = x;
und nicht
int *j;double &xAlias = x;
Unter Datentypen sind einfache Typen, zusammengesetzte Typen und Klassen zusammengefaßt. Die Bezeichnung beginnen mit großem Anfangsbuchstaben. Besteht die Bezeichnung aus mehrere Wörtern, beginnt jedes Wort wiederum mit einem Großbuchstaben. Das Zeichen _ sollte innerhalb der Names nicht benutzt werden.
Außerdem besitzen Datentypen je nach Verwendung bestimmte Endungen:
Datentypendungen | ||
Datentyp | Endung | Beispiel |
Einfacher Datentyp | _t | UInt16_t |
Zusammengesetzter Datentyp | _s | Component_s |
Klassen | _c | SerialCommunication_c |
Funktionen, die von einer DLL exportiert werden, müssen ein definiertes Format vorweisen:
Jede exportierte Funktion muß als Rückgabewert den Typ Retcode_t benutzen. Die möglichen Fehlercodes sind in der Headerdatei errdefs.h definiert.
Makros bestehen nur aus Großbuchstaben. Sollte sich ein Makronamen aus mehreren Wörtern zusammensetzen, muß zur Trennung das Zeichen _ verwendet werden. Globale Makros (z.B. Compilerdefinitionen) müssen vorne und hinten zwei _ besitzen (z.B. __MIDL_PASS__).
Die Ressourcen eines Programms müssen durch IDs bezeichnet werden. Folgende Tabelle zeigt die Präfixe der ID-Definitionen für verschiedenen Ressourcetypen:
Beginn der Bezeichner für Ressourcen | |
Ressourcentyp | Präfix |
Menütasten-Kürzel | IDA_ |
Bitmap oder DIB | IDB_ |
Cursor | IDC_ |
Dialog | IDD_ |
Schriftart | IDF_ |
Symbol | IDI_ |
Menü | IDM_ |
String | IDS_ |
Versions-Info | IDV_ |
Hilfe | IDH_ |
Benutzerdefiniert | IDU_ |
Befehle | CM_ |
Variablen beginnen mit kleinem Anfangsbuchstaben und müssen immer den Zweck beschreiben (also keine Variablennamen wie x oder a). Bei zusammengesetzten Namen wird ein neues Wort mit Großbuchstaben begonnen (außer natürlich der erste Buchstabe). Das Zeichen _ darf nicht zur Trennung der Worte verwendet werden.