In C++ und Java sind Klassenmethoden auch von der Gültigkeit der Bezeichnung her in Klassen gekapselt. In C++ und Java handelt es sich um verschiedene Methoden, wenn gleichnamige Methoden verschiedene Argumente (Parameter) haben. In C muss der Name der Funktion (Subroutine) eindeutig sein, lediglich ein static-Konzept schafft Namensräume.
Daher muss man sich mit einem Bezeichnungsschemate, das gleichzeitig dem Durchblick durch die Software dient, die Zuordnung erkennbar werden lässt, helfen.
Im Folgenden werden Bezeichnungsschemata kurz vorgestellt und erläutert.
Grundform: | doSomething_Type(Type* ythis ...) |
Zusätzliche Parameter im Standardformat: | doSomething_is_Type(Type* ythis, int p1, String_c p2 ...) |
Zusätzliche Parameter als Referenzen zu Anwendertypen | |
Static-Methoden | doSomething_sType(...) |
doSomething_ParamType_s_Type(Type* ythis, ParamType* p1, String_c p2 ...) | |
const_Type() | |
const_r_Type() | |
constructor_Type() | |
doSomething_Type_V(...) | |
doSomething_Type(Type* ythis ...)
Das ist die Grundform einer Klassenmethode in C. Die Methode beginnt mit einem Verb, das aussagt, was die Methode ausführt. Die Kern-Bezeichnung der Methode kann länger sein, gemischte Groß/Kleinschreibung dient der Gliederung des Namens.
Danach folgt nach einem Unterstrich die Bezeichnung der Klasse (in C struct-Typ), der die Methode angehört. Dieser Name soll vollständig sein und kann auch wieder gemischt Groß/Kleinschreibung enthalten. Ein nachgestelltes _c bei den Typen der C_Runtime_Javalike soll aber nicht mitgeführt werden, dieses nachgestellte _c dient lediglich der Unterscheidung der struct-Typen von sonst gleichnamigen C++-Klassennamen.
doSomething_is_Type(Type* ythis, int p1, String_c p2 ...)
In C++ und Java kann man Methoden mit gleichen Namen aber unterschiedlichen Paramtern haben, das sind dort unterschiedliche Methoden. Häufig wird dabei von "überladen" gesprochen, allerdings wird dort nichts überladen. Der Compiler erkennnt die Paramtersignatur und bildet damit einen längeren internen Namen, was letztlich als Label für den Linker dient. In C muss man das "zu Fuß" machen. Dieser Nachteil ist allerdings dann wieder ein Vorteil, da in C klar erkennbar ist, welche Methode unter welchen Label beim Linken angesprochen wird.
Den gleichen Namen für verschiedene Methoden verwenden sollte man nur dann, wenn diese Methoden etwas unmittelbar vergleichbares ausführen. sich nur in der Art der Parameterübergabe unterscheiden.
Als Bezeichnungsschemata gilt folgendes:
Zwischen den Kern-Methodennamen und den Typnamen der zugehörigen Klasse wird ein Kürzel des Parametertyps geschrieben. Für Standard-Parametertypen sollen folgende Kleinbuchstaben verwendet werden:
b | boolean | Boolean-Typ, in C meist mit einem int realisiert, hier soll es auf die Bedeutung ankommen (wie boolean in Java) |
c | char | Zeichentyp in der tatsächlichen Bedeutung eines Zeichens, nicht als Umschreibung eines byte (wie char in Java) |
e | Enumtyp | ein enum-Typ |
f | float, double | Gleitkommatyp |
i | int, short | Integertyp: Die genaue Unterscheidung der Bitanzahl ist für Parameter einer Methode meist nicht sinnvoll. Daher soll auch hier keine Unterscheidung erfolgen. In der Regel ist es günstig, einen Ganzzahlwert als int zu übergeben. |
o | Object_c*, Object_r* | Referenz auf ein Object, das heißt eine beliebige Instanz, die allgemein angesprochen wird. |
l | int64 | Long-Typ wie long in Java, dort sind es 64 bit. Das ist nicht allzu üblich, daher ist die Sonderkennzeichnung geboten. |
s | String_c | Zeichenkette in der Form des String_c-Typs. |
z | const char* | 0-terminierte C-like-Zeichenkette ("z" von zero-terminated) |
Mit diesen Kürzeln sollen aber nur diejenigen Parameter aufgeführt werden, die tatsächlich unterschiedlich in mehreren Varianten der Methode mit gleichem Kernnamen sind, Beispiel:
void doSomething_Type(Type* ythis, String_c name); void doSomething_i_Type(Type* ythis, String_c name, int value);
Der Unterschied liegt im Beispiel im int-Parameter, nur das wird gekennzeichnet. Allerdings sollte man bei der Wahl der Bezeichnungen weitsichtig sein.
Weitere Einzel-Kleinbuchstaben können in Spezialfällen hilfreich sein.
doSomething_ParamType_s_Type(Type* ythis, ParamType* p1, String_c p2 ...) doSomething_ParamType_s_Type(Type* ythis, ParamType_r* p1, String_c p2 ...)
Wenn die Methode mit einem speziellen Parametertyp, der eine Klasse darstellt, gekennzeichnet ist, dann kann dieser Typ als Gesamtbezeichnung angegeben werden. Zu beachten ist, dass der Bezeichner nicht zu lang wird. 32 Zeichen sollte jeder compiler signifikant verarbeiten, mehr gegebenenfalls nicht. Im Beispiel ist dargestellt, dass die Art der Parametertypübergaben im Bezeichner keine Rolle spielen soll. ParamType_r
ist ein Typ, der eine Referenz auf ParamType
enthält.
Als Kurzform kann auch nur der Anfangsbuchstabe des Parametertyps geschrieben werden, wenn damit Eindeutigkeit erreicht wird. Das ist in den meisten Fällen der Fall, damit eine gute und einfache Regel. Beispiele:
constructor_R_Thread(Thread* ythis, Runnable* pRunnable); constructor_Rs_Thread(Thread* ythis, Runnable* pRunnable, String* name);
const_Type()
Das ist die Bezeichnung einer Konstante des angegebenen Typs. Die Konstante kann Parameter haben. Hier gilt das gleiche wie bei Methoden. Die Konstante ist immer mit einem define umgesetzt, da es sich um eine sogenannte immediate-Konstante handelt, beispielsweise
typedef struct MyClass_t{ Object_c object; int a; float b} MyClass_c; #define const_MyClass() {const_r_Object(&reflection_MyClass); 1; 0.0F; }
In diesem Beispiel ist die Klasse MyClass von Object abgeleitet, daher befindet sich die Konstantendefinition für Object innerhalb des defines, diese ist adäquat definiert was letztlich zu geschachtelten geschweiften Klammern führt. Die Variable a wird im Beispiel mit 1 vorbesetzt.
#define const_i_MyClass(INTVAL) {const_r_Object(&reflection_MyClass); (INTVAL); 0.0F; }
Hier wird wie bei Methoden ein abweichender Parameter zwischen const und dem Klassentyp angegeben.
const_r_Type()
Die Klassen der C_RuntimeJavalike basieren auf Object, die Basisklasse aller Klassen Object beinhaltet einen Zeiger auf die sogenannten Reflections, dieser beinhaltet auch eine Tabelle der virtuellen Methoden. Das r deutet darauf hin, das der Zeiger auf die Reflectiondaten bei der Konstantendefinition übergeben wird. Das ist genau dann notwendig, wenn keine Konstante der Klasse selbst, sondern eine Konstante einer abgeleiteten Klasse erzeugt werden soll, innerhalb derer dann diese Konstante eingebettet ist. Beispiel dazu:
#define const_ri_MyClass(REFLECTION,INTVAL) {const_r_Object(REFLECTION); (INTVAL); 0.0F; }
constructor_Type()
Hiermit wird eine Methode aufgerufen, die dem Konstruktor in C++ oder Java entspricht. Diesen gibt es ebenfalls auch noch in der typischen Form
constructor_r_Type()
insbesondere für den Aufruf des Konstruktors einer Basisklasse innerhalb des Konstruktors der abgeleiteten Klasse.
doSomething_Type_V(...)
Das "V" steht für eine realisierte virtuelle Methode. Der Anwender soll diese nie direkt aufrufen, sondern die Methode der Klasse ohne "V". Diese ruft dann die virtuelle Methode auf. Dabei ist es von der Instanz der Daten abhängig, ob es gegebenenfalls eine virtuelle Methode einer abgeleiteten Klasse aufgerufen wird.