Codestil

Wenn mehrere Programmierer zusammenarbeiten, muss jeder Quelltext für jeden gleich gut lesbar sein. Das ist nur zu verwirklichen, wenn man sich auf gemeinsame Regeln einigt und diese anschließend auch einhält. Der Compiler akzeptiert einen Code in vielen Stilen, darum ist der hier beschriebene Stil sicherlich nicht besser oder schlechter als andere Stile, jedoch sollte dieser für das Dedupe-Projekt verwendet werden.

Sofern möglich wird die Stil-Entscheidung begründet.

Sprache

Als Sprache zur Implementierung und Dokumentation wird Englisch verwendet.

Einrückungen

Tabs oder Spaces?

In einem Quelltext dürfen keine Tabulatorzeichen auftauchen. Eingerückt wird ausschließlich mit Spaces. Zur Einrückung von Blöcken werden 2 Leerzeichen verwendet.

Grund: Tabulatorzeichen sind in der Regel 8 Zeichen breit. Das ist in der Regel jedoch zuviel. Daher haben viel Entwickler die Breite auf 4 oder 2 Zeichen eingestellt. Durch die Unterschiede passt die Einrückung jedoch häufig nicht mehr. Aus diesem Grund werden ausschließlich Spaces zum Einrücken verwendet.

Indizierung von Arrays

Wie bei Funktionen stehen die Klammern beim Namen des Arrays und der Index wird von den Klammern durch Leerzeichen abgetrennt.

array[ i ];

Zeiger

Der Dereferenzierungsoperator (*) liegt am Namen der Variable an, also

char *c, *d;

und nicht

char* c, * d;

Grund: Der Operator gehört zur Variable und zum Typ. Bei der Verwendung der des Ziels des Zeigers wird ebenfalls diese Schreibweise verwendet.

geschwungene Klammern

Geschwungene Klammern stehen immer untereinander, außer bei tabellarischer Programmierung.

Grund: Dadurch ist der Bereich eines Blocks leichter erkennbar, vor allem wenn der Editor zusammengehörende Klammern hervorhebt.

Beispiel:

int main( int argc, char ** argv )
{
  if( argc == 1 )
  {
    printf( "Kein Argument uebergeben.\n" );
    return 1; 
  }
 
  ...
 
  return 0;
}

Einzelne Befehle müssen nicht geklammert werden, dürfen aber, wenn es die Übersichtlichkeit erhöht (z.B. verschachtelte If-Abfragen)

  if( value == 1 )
    print( "Wert ist 1\n" );

Tabellarische Programmierung

Kurze (einzeilige) Befehle dürfen tabellarisch geschrieben werden.

switch( char c )
{
  case 'a': { doSomethingForA(); break; }
  case 'b': { doSomethingForB(); break; }
  default : { doNothing();       break; }
}

Grund: Verwendet man diese Technik richtig kann die Lesbarkeit erhöht werden. Man sollte aber keinesfalls zu viel Code in eine Zeile schreiben, da sonst genau das Gegenteil bewirkt wird. Insbesondere sollten sich die Zeilen ähneln, so dass auch wirklich eine tabellarische Darstellung gegeben ist, z.B. dass grundsätzlich die gleiche Funktion mit unterschiedlichen Argumenten aufgerufen wird.

Funktionen / Kontrollstrukturen

Klammern liegen an den Namen der Funktion bzw. der Kontrollstruktur an und halten Abstand zu den Parametern.

function( parameter );

Endende Klammern fallen jedoch zusammen:

if( function( parameter ))

Castings

Bei Castings liegen die Klammern an den Typ an und stehen mit einem Leerzeichen Abstand vor der Variablen.

(char *) c
printf( "Wert: %d\n", (unsigned short) value );

Grund: die anliegenden Klammern unterscheiden sich optisch von üblichen Funktionsaufrufen oder Konstrukten (if, while, for)

Für C-Castings gibt es jedoch in C++ keine Notwendigkeit mehr. Es sollte grundsätzlich überhaupt nicht gecastet werden. Falls Castings erforderlich sind, sollte es in einer gut beschriebenen Klasse gekapselt werden, es sollten die C++-Casts verwenden (static_cast, const_cast, dynamic_cast, reinterpret_cast) und die Verwendung sollte zunächst im Forum abgeklärt werden, ob dies überhaupt erforderlich ist.

Kommentare

Längere Blöcke von Kommentaren vor dem Code sind Einzeilern daneben vorzuziehen.

Unterteilungen

Längere Algorithmen werden durch Kommentare aufgeteilt:

{
  int x, y, z;
 
  /******************************************************************/
 
    /* Preparations
     * It's necessary to....
     */
 
 
  ...
 
 
  /******************************************************************/
 
    /* Here we to the following
     *
     * Regard that...
     */
 
  ...
 
    /* Hint for the following code block */
 
  ...  
 
  /******************************************************************/
 
    /* Tidy up and return results */
 
  ...
  return result;  
}

Kommentare, die ein oder wenige nachfolgende Codeblöcke beschreiben, werden einfach eingeschoben („Hinweis auf den nächsten Block“), ist der Algorithmus so textreich, können gewissermaßen Kapitelstrukturen mit einer abgrenzenden Kommentarzeile voneinander getrennt werden („“).

Leerzeilen

Leerzeilen trennen abgeschlossene Befehlspakete. Sie trennen ebenfalls Variablendeklarationen und return-Anweisung vom den eigentlichen Anweisungen ab.

int function()
{
  int i;
 
  doSomething( &i );
 
  return i;
}

Namensräume

using sollte nur verwendet werden wenn der Inhalt eines Namensraums in einem Codestück sehr häufig verwendet wird.

Grund: Verwendet man es zu oft zerstört dies den Sinn von Namensräumen, nämlich den Code in Bereiche aufzuteilen und Namenskollisionen zu vermeiden.

Jeder Header sollte am Anfang einen beschreibenden Kommentar enthalten. Guards werden nach dem Muster

ORG_PROGGEN_NAMESPACE_CLASS_H

gebildet. Also z.B.:

#ifndef ORG_PROGGEN_DEDUPE_HASH_H
#define ORG_PROGGEN_DEDUPE_HASH_H
// Code...
#endif

Namensgebung

Dateien

Dateien werden immer klein geschrieben.

Typen

Typen beginnen mit einem Großbuchstaben und jedes weitere Wort im Namen hat ebenfalls einen großen Buchstaben.

class MyDummyClass
{
// ...
};

Symbolenamen

Objektorientierte Symbole (zu einer Klasse zugehörig) werden groß mit Camelcase geschrieben, das gilt für Konstanten, Variablen, wie auch für Methoden. Statische Elemente und lokale Variablen werden klein geschrieben.

int const globalConstant;
int globalVariable;
int globalFunction( int value );
 
class MyDummyClass
{
public:
  int const Constant;
  int       Variable;
 
 
  int AddMethod( int summand, int summand2 );
};

SVN-Repository

Das Anlegen von neuen Verzeichnissen und Änderungen in der bestehenden Ordnerstruktur sollten im Forum abgeklärt werden.

Const-Correctness

Const-Correctness ist grundsätzlich zu beachten und eigene Klassen sind so restriktiv wie möglich zu formulieren:

class MyWrapperClass
{
private:
  DataClass Value;
 
public:
  MyWrapperClass( DataClass const & value ) : Value( value ) {}
 
  DataClass const & GetData() const;
};  

Das Schlüsselwort const wird grundsätzlich nachgestellt:

int const ic;
int const *icp;
char const *str;
char const * const strc;

also nicht, wie in C noch üblich oder von vielen Compilern weiterhin angezeigten:

const int ic;
const int *icp;
const char *str;
const char * const strc;

Grund: Sind die Zeiger konstant, so muss const hinter dem Stern geschrieben. Selbiges für Methoden, die von konstanten Objekten gerufen werden dürfen (siehe oben GetData() const). Nur beim Datentyp darf const vorangestellt werden, da sich diese Schreibweise in C in einer Zeit etabliert hat, bevor es Const-Correctness gab. Heutzutage sollte const konsequent hinter dem Part stehen, den es als konstant markiert.

goto

goto sollte vermieden werden. Das heißt, dass goto verboten ist, sofern nicht aus verschachtelten Schleifen gesprungen werden muss.

Grund: Mit goto ist es möglich quer durch den Code zu springen. Dies führt zu unleserlichen und schwer wartbaren Programmen.