Folgende Punkte sollen beim Code-Rework aber auch bei zukünftigen Implementierungen beachtet werden.

IDL-Schnittstelle

  • Wenn wir nicht ganz sicher sagen können, dass in der IDL-Schnittstelle voraussichtlich immer ein Einzelwert ausgetauscht wird, sollten wir vorsichtshalber lieber gleich eine struct nehmen, die zunächst nur diesen Einzelwert einhält. Dann kann man später relativ problemlos zusätzliche Elemente hinzufügen, ohne den Anwender-Code für den Zugriff auf die bereits existierenden ändern zu müssen. Man muss 'nur' alles neu übersetzen, da sich ja das Datenlayout ändert.

#if defined (...), #elif defined (...), #else, #endif

  • Bei Unterscheidungen mit Hilfe dieser Direktiven, sollen immer alle Möglichkeiten unter einem #if bzw. einem #elif aufgeführt werden. Im #else -Zweig soll immer ein Fehler gemeldet werden. Also zum Beispiel:
        #if defined (__x86__)
        ...irgendwas x86-spezifisches
        #elif defined (__powerpc__)
        ...irgendwas ppc-spezifisches
        #else
        #error Error: unknown platform
        #endif

#include <xyz.h>  oder  #include <cxyz>?

  • Immer die C++-Formulierung für Includes der Headerfiles der Standard C Library verwenden! Nur damit sind die Deklarationen im Namespace std, ansonsten haben sie keinen Namespace! Also z.B. nicht #include <stdlib.h> schreiben, sondern #include <cstdlib>.

    Hier alle Includes der Standard-C-Bibliothek in ihrer C++-Formulierung:

        <cassert>    <cctype>     <cerrno>     <cfloat>
        <ciso646>    <climits>    <clocale>    <cmath>
        <csetjmp>    <csignal>    <cstdarg>    <cstddef>
        <cstdio>     <cstdlib>    <cstring>    <ctime>
        <cwchar>     <cwctype> 
  • Für Strings sollte man im Normalfall <string> und nicht <cstring> includieren. Erstere sind die echten C++-Strings.

  • Allerdings gibt's nicht für jedes benutzte Include die passende C++-Version, z.B. für dlfcn.h oder unistd.h. Da sollten wir noch mal forschen, ob die überhaupt noch benutzt werden sollen/dürfen und ob es dafür passenden Ersatz für C++ gibt. Ist im Moment aber eher unwichtig!

Includes in Headerfiles

  • Alle in einem Modul nur intern benötigten Header-Dateien sollen in der Implementationsdatei (modul.[cc|cpp]) und nicht in der zugehörigen Header-Datei (modul.h[h]) includiert werden.

using namespace xyz;

  • Namespaces werden nicht komplett importiert, obige Formulierung, wie z.B. wie using namespace std;, ist verboten. Statt dessen muss man also entweder std::string x; schreiben, oder string explizit mit using std::string importieren.
  • Speziell für Default- und Therapie-USRs gilt (solange sie includiert werden):
    • Es gibt keine Deklarationen mit using... in den Default-und den Therapie-USRs.
    • Verwendeten Klassen, Methoden usw. dürfen nicht abhängig sein von Namespace-Deklaraionen (using...) in den inkludierenden Sources, z.B. mx-usrs.cc.
    • Alle verwendeten Klassen, Methoden usw. werden explizit mit dem notwendigen Namespace angegeben, also etwa ...DeviceAccess::AccData....

public, protected, private.

  • Auf public-Elemente einer Klasse kann jeder zugreifen.
  • Auf protected-Elemente einer Klasse kann nur aus einer Kind-Klasse oder von einer befreundeten (friend) Klasse aus zugegriffen werden.
  • Auf private-Elemente einer Klasse kann nur von einer befreundeten (friend) Klasse aus zugegriffen werden.
  • In der Regel sollen alle Attribute (Variablen) einer Klasse private und nur über Methoden schreib- bzw. lesbar sein. Deklarationen von befreundeten Klassen sollen weitgehend vermieden werden (siehe auch Tip 22 in (6)).

static const T  statt  #define

  • Wenn irgend möglich, zur Definition von Konstanten nur static const int MY_CONST = 37; benutzen statt eines #define MY_CONST 37.
  • Wo es sich nicht vermeiden läßt, auf strikte Klammerung achten! #define MY_CONST 3 + 2 und cout << 2 * MY_CONST ergibt 8 und nicht 10, wie man vielleicht erwarten würde, weil der Pre-Compiler nur dummen Textersatz macht, dadurch vor der eigentlichen Compilierung cout << 2 * 3 + 2 entsteht und dann eben Punkt- vor Strichrechnung gilt.
    Man muss also #define MY_CONST (3 + 2) schreiben, damit cout << 2 * (3 + 2) entsteht!

T& method()  oder  T* method()?

  • Prinzipiell sollte man, wo es möglich ist, Referenzen statt Pointer benutzen, also z.B. UsrSet& usrSet() statt UsrSet* usrSet(), womit man dann usrSet().findUsr(...) schreiben kann.
  • Man muss aber beachten, dass Referenzen nicht mehr geändert werden werden können! Ein einmal zugewiesener Wert ist fest, solange die Variable besteht.

T method(); throw(Exception);

  • Jede Methoden einer Klasse soll mit einer Info versehen werden, ob sie eine Exception wirft oder nicht.
  • Mit einer leeren Throw-Anweisung int f(float); throw(); kann man sagen, dass method() keine Exception wirft.
  • Wirft eine Methode (auch noch) eine andere als die deklarierte Exception, wird das zur Compile-Zeit nicht festgestellt. Man kann sich, um sicher zu gehen, nur mit einem zusätzlichen catch (...) in der aufrufenden Methode behelfen.

Exception-Text

  • Zumindest in Bibliotheks-Modulen soll als Exception-Text das Macro ACCDEV_ERRORLOCATION benutzt werden. Etwa so:
        throw AccDevException(ODA_ANERROR, ODA_OK, ACCDEV_ERRORLOCATION);

T method() const;

  • Methoden werden mit const deklariert, wenn sie nichts am Objekt (seinen Attributen) ändern.
  • Siehe auch Tip 3 in (6), besonders die Erörterung der bitweisen und der logischen Konstanz einer Methode (Elementfunktion) ab Seite 37.

new, delete

  • Alles, was mit new angelegt wurde, muss mit delete wieder freigegeben werden.
  • Dazu ein schöner Satz aus "Effektiv C++ programmieren": "[Es ist klar,] dass sie etwas falsch machen, wenn Sie Resourcen per Hand freigeben." Wie's besser geht, sagen die Tips 13, 14, 15 und 18 in (6).

auto_ptr, shared_ptr usw. als Funktionsparameter

  • Aufpassen muss man, wenn der Parameter einer Funktion ein auto_ptr, ein shared_ptr oder ähnliches ist. Siehe hier zur Beschreibung des Problems und hier für einen Lösungsvorschlag.
  • Ein shared_ptr wird zum Beispiel in accdevice.hh|.cc für die Callbacks benutzt.

pthreads

  • Bei Thread-Programmierung die Funktionen pthread_cleanup_push() und pthread_cleanup_pop() bedenken. Ist sowas wie das An- und Abmelden eines Exithandlers für Threads.

Socket-Programmierung

  • Hier sollte man darauf achten, nur noch Funktionen zu benutzen, die auch IPv6-fähig sind, so dass eine spätere Umstellung erleichtert wird. Einen einführenden Artikel mit dem Titel "Netzansatz" findet man in der iX 6/2007 ab Seite 138.

Pointer oder Referenz?

  • Wo es möglich ist, sollte man besser Referenzen auf Objekte statt Pointer darauf verwenden. Entsprechend braucht man dann auch Methoden, die statt eines Pointers auf ein Objekt eine Referenz zurück geben.

    Beachten muss man aber, dass eine einmal gesetzte Referenz nicht mehr zu verändern ist! Ist das nötig, muss man nach wie vor Pointer benutzen!

sndData.assign(n,T(0)) statt sndData.resize(n)

  • USRs sollten für Lese-Properties immer sndData.assign(n, T(0)) verwenden, um die Sendedaten (sndData oder auch outData) zu initialisieren, damit es keine void -Elemete darin gibt, mit denen Userface nix anfangen kann.

Property-Namen

  • Darauf achten, dass Namen von Properties auch weiterhin nur maximal 8 Zeichen lang sein dürfen!

String-Properties

  • Eigentlich müssen Strings, die eine Property liefert, immer die gleiche Länge haben, sonst kann man sie im alten System nicht interpretieren. Das gilt besonders für 'Strutures'. Nomenklaturen etwa, die kürzer als acht Zeichen sind, müssen also mit Blanks aufgefüllt werden.

    Für das neue System ist das allerdings unschön. Da verwendet man eigentlich Strings, die genau so lang sind wie nötig. Im Moment habe ich aber keine bessere Idee als auf der USR-Seite wirklich mit Blanks auf die notwendige Länge aufzufüllen und diese angehängten Blanks in der Anwendung (auf DeviceAccess-Seite) wieder wegzuschmeißen. Oder gibt's 'ne bessere Idee?

Code-Formatierung

  • Alle Quellen mit sehr persönlich geprägter Formatierung, sollen vor dem Commit mit AStyle reformatiert werden.
    astyle in.cc
    formatiert in.cc. Das Original wird in in.cc.orig aufbewahrt.
    astyle < in.cc > out.cc
    formatiert in.cc, lässt die Datei aber unberührt und schreibt das Ergebnis in out.cc.
  • In [x]emacs kann man auch einen Bereich selektieren und dann C++ -> Indent Line or Region klicken.

Tools

  • Alle Tools sollen mit einem Header versehen werden, der kurz erklärt, was das Tool tut, wie es aufzurufen ist, welche Besonderheiten es gibt und wer Änderungen daran gemacht hat. Auch ist es ziemlich sinnvoll, den Code selbst mit ein paar Kommentaren zu versehen!
Topic revision: r17 - 18 Aug 2011, UnknownUser
This site is powered by FoswikiCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding Foswiki? Send feedback