/* (str.h) */ #ifndef STR_H #define STR_H /* Die Klasse STRING wird abstrakt repraesentiert Dies ist die Schnittstelle der Klasse */ extern const void * STRING; /* Elementfunktionen der Klasse */ void setvalue ( void * self, char * chars); /* Stringzuweisung */ char * getvalue ( void * self); /* String auslesen */ char getindex ( void * self, int i); void setindex ( void * self, int i, const char c); /* Zugriff auf einzelne Zeichen */ #endif ________________________________________________________________ /* (main.c) */ #include <stdio.h> #include "new.h" #include "str.h" int main() { void * s = new( STRING ,1, "Hallo"); void * t = new ( STRING , 0); void * u = copy(s); printf ("%s =s\n", getvalue(s)); printf ("%s =t\n", getvalue(t)); setvalue(s,"nochmal Hallo"); printf ("%s =neues s\n", getvalue(s)); delete(s); delete(t); cout(u); printf(" =u\n"); setindex(u,0,'X'); cout (u); printf(" u nach setindex\n"); printf("Index 4 von u: %c\n", getindex(u,4)); delete (u); return 0; }
Die Implementierung von STRING:
#include <stdio.h> #include <stdarg.h> #include "str.h" #include "new.h" #include "error.h" /* Die Implementierung der Klasse STRING */ /* Deklarationen der Elementfunktionen von STRING */ static void * String_constructor (void * self, const int argnr, va_list * app); static void * String_destructor (void * self); static void * String_copy (const void * self); static void String_setvalue (void * self , char * chars); static char * String_getvalue (void * self); static void String_setindex (void * self, int index, const char c); static char String_getindex (void * self, int index); static void String_cout (const void * self); /* Definition der Klasse, orientiert sich an 'class.d' */ struct Class_Str { /* die grundlegenden Methoden, fuer alle Klassen gleich */ size_t size; void * (*constructor) (void * self, const int argnr, va_list * app); void * (*destructor) (void *self); void * (*copy) (const void * self); void (*cout) (const void *self); /* ab hier stehen die benutzerdefinierten Methoden */ void (*setvalue) (void * self, char * chars); char * (*getvalue) (void * self ); void (*setindex) (void * self, int index, const char c); char (*getindex) (void * self, int index); };
Die Deklarationen der statischen Funktionen am Anfang sind nur für den Compiler notwendig, sie haben sonst keine Bedeutung.
An dieser Stelle definieren wir jetzt unsere Klasse STRING.
STRING hat die in 'class.d' definierten Komponenten der
abstrakten Klasse CLASS (notwendig für Kompatibilität),
desweiteren besitzt die Klasse noch Methoden, um auf die Daten
zugreifen zu können.
struct String { const void * class; char * text; };
Die Definition der Objektstruktur. Ein Objekt zeigt auf seine Klasse und enthält seine eigenen Daten.
static const struct Class_Str _String = { sizeof(struct String), String_constructor, String_destructor, String_copy, String_cout, String_setvalue, String_getvalue, String_setindex, String_getindex };
Die Erzeugung der Klasse STRING. Eine Klasse wird implementiert, indem genau ein statisches Element von Class_Str erzeugt wird, das zur gesamten Programmlaufzeit erhalten bleibt. Initialisiert werden müssen hier vor allem der Konstruktor und die Komponente size, da ohne diese beiden Eigenschaften kein Objekt erzeugt werden kann. Die anderen Methoden könnten auch vom Konstruktor gesetzt werden; falls man sie nicht braucht (z.B. falls eine Klasse keine Ausgabefunktion benötigt), können sie auch mit 0 initialisiert werden (beachte: definiert werden müssen sie, sofern CLASS sie auch besitzt).
const void * STRING = & _String;
Die Erzeugung der Schnittstelle der Klasse STRING. Eine Klasse wird identifiziert über die Adresse ihres statischen Elements. Diese konstante Variable ermöglicht uns den Zugriff auf die Elementfunktionen der Klasse.
Die Elementfunktionen sind static, da sie nur innerhalb von str.c sichtbar sein müssen.
/* Realisierung der Elementfunktionen */ static void * String_constructor(void * _self,const int argnr,\ va_list * app) { struct String * self = _self; const char * text; if ( app != 0) text = va_arg( * app, const char *); else text=""; if ( self == 0 ) fatalerror(NULLPTR,1); self -> text = (char *) malloc (strlen(text)+1); if ( self -> text == 0 ) fatalerror(ALLOCMEM,1); strcpy (self -> text, text); return self; }
Der Konstruktor der Klasse STRING. Er stellt den Speicher für die Komponente .text zur Verfügung und kopiert den Initialisierungsstring dorthin, sofern app != 0.
static void * String_destructor(void * _self) { struct String * self = _self; if ( self == 0 ) return; free(self -> text); self -> text = 0; return self; } static void * String_copy(const void * _self) { const struct String * self = _self; if (self == 0) fatalerror(NULLPTR,1); if ( self -> class != STRING) fatalerror(TYPE,1); return new( STRING ,1, self -> text ); } static void String_cout(const void * _self) { const struct String * self = _self; if (self == 0) fatalerror(NULLPTR,1); if ( self -> class != STRING) fatalerror(TYPE,1); fprintf(stdout,"%s", self -> text); } static void String_setvalue(void * _self , char * chars) { struct String * self = _self; char * text; if ( chars == 0 ) return; if ( self == 0 ) fatalerror(NULLPTR,1); if ( self -> class != STRING) fatalerror(TYPE,1); text = (char *) calloc (strlen(chars)+1,sizeof(char)); if ( text == 0 ) fatalerror(ALLOCMEM,1); strcpy ( text, chars); free( self -> text ); self -> text = text; } static char * String_getvalue(void * _self ) { const struct String * self = _self; char * text; if ( self == 0 ) fatalerror(NULLPTR,1); if ( self -> class != STRING) fatalerror(TYPE,1); text = (char *) calloc (strlen(self -> text)+1, sizeof(char)); if ( text == 0 ) fatalerror(ALLOCMEM,1); strcpy(text, self -> text ); return text; } static void String_setindex(void * _self ,int _index,const char _c) { struct String * self = _self; if ( self == 0 ) fatalerror(NULLPTR,1); if ( _index >= strlen(self -> text) ) fatalerror("index overflow\n",1); if ( self -> class != STRING) fatalerror(TYPE,1); (self -> text) [ _index ] = _c; } static char String_getindex(void * _self ,int _index) { const struct String * self = _self; char c; if ( self == 0 ) fatalerror(NULLPTR,1); if ( _index >= strlen(self -> text) ) fatalerror("index overflow\n",1); if ( self -> class != STRING) fatalerror(TYPE,1); c = (self -> text) [ _index ]; return c; }
Die Elementfunktionen unterscheiden sich nicht besonders von
den Funktionen aus dem letzten Kapitel.
Interessant ist hier eigentlich nur der explizite Typ-cast:
unser Objekt ist vom Typ STRING, und wir müssen also
wieder casten, um _self dereferenzieren zu können.
Nun benötigen wir noch die Funktionen, die uns von außen den
Zugriff auf die Daten der Klasse geben. Die Grundidee bei der
Implementation besteht darin, nicht direkt auf die Daten zuzugreifen,
sondern über die Elementfunktionen der Klasse, die wir oben entworfen
haben. Das wird uns später bei der Vererbung nützlich sein.
(Man nennt das auch dynamische Bindung von Funktionen.)
/* Realisierung der exportierten Funktionen */ void setvalue( void * _self, char * chars) { const struct Class_Str ** self = _self; if (self == 0) fatalerror(NULLPTR,1); (*self) -> setvalue(_self, (void *) chars); } char * getvalue (void * _self) { const struct Class_Str ** self = _self; char * text; if (self == 0) fatalerror(NULLPTR,1); text = (*self) -> getvalue(_self); return text; } void setindex ( void * _self , int i, const char c) { const struct Class_Str ** self = _self; if (self == 0) fatalerror(NULLPTR,1); (*self) -> setindex(_self,i,c); } char getindex ( void * _self, int i) { const struct Class_Str ** self = _self; char c; if (self == 0) fatalerror(NULLPTR,1); c = (*self) -> getindex(_self,i); return c; }
Ein Testlauf:
cn@cicero:/home/cn/seminar/aktuell/kap2 > ls class.d complex.c kap2.tar new.c str.h compl.c error.c main.c new.h typescript compl.h error.h makefile str.c cn@cicero:/home/cn/seminar/aktuell/kap2 > make main gcc -c main.c gcc -c str.c gcc -c new.c gcc -c error.c gcc -o main main.o str.o new.o error.o cn@cicero:/home/cn/seminar/aktuell/kap2 > main Hallo =s =t nochmal Hallo =neues s Hallo =u Xallo u nach setindex Index 4 von u: o cn@cicero:/home/cn/seminar/aktuell/kap2 >