L'interfaccia di dato astratto in Point.h contiene le seguenti linee:
extern const void * Point; /* new(Point, x, y); */ void move (void * point, int dx, int dy);
Possiamo riciclare i file di new.? Illustrati in un altro articolo con la differenza di eliminare molti metodi e di aggiungere draw() in new.h.
void * new (const void * class, ...); void delete (void * item); void draw (const void * self);
La descrizione di tipo struct Class in new.r dovrebbe corrispondere alle dichiarazioni dei metodi in new.h
struct Class { size_t size; void * (* ctor) (void * self, va_list * app); void * (* dtor) (void * self); void (* draw) (const void * self); }
Il selettore draw() viene implementato in new.c il quale rimpiazza i selettori quali differ() introdotti nella sezione 2.3. draw() viene codificato nello stesso stile del differ()
void draw (const void * self) { const struct Class * const * cp = self; assert(self && * cp && (* cp) —> draw); (* cp) —> draw(self); }
Dopo queste considerazioni preliminare, possiamo occuparci del vero lavoro, ossia di scrivere praticamente il file Point.c che altro non è che l'implementazione di punti. Ancora una volta, lavorando in maniera orientata agli oggetti ci ha aiutato ad identificare precisamente ciò che dobbiamo fare: dobbiamo decidere sulla rappresentazione e sull'implementazione di un costruttore, un distruttore, del linkage dinamico di draw() e del metodo invocato staticamente move(). Se ipotizziamo di lavorare in un ambiente bidimensionale, basato sulle coordiante cartesiane, possiamo quindi scegliere la facile rappresentazione:
struct Point { const void * class; int x, y; /* coordinates */ };
Il costruttore deve inizializzare le coordinate .x e .y – cosa che è praticamente la solita routine
static void * Point_ctor (void * _self, va_list * app) { struct Point * self = _self; self —> x = va_arg(* app, int); self —> y = va_arg(* app, int); return self; }
Non avremo bisogno di un distruttore perchè non abbiamo risorse da reclamare prima che entri in gioco la funzione delete(). In Point_draw() stampiamo le coordinate correnti in un modo che pic può comprendere:
static void Point_draw (const void * _self) { const struct Point * self = _self; printf("\".\" at %d,%d\n", self —> x, self —> y); }
Questo codice seguente invece si prende cura di tutti i metodi linkati dinamicamente e possiamo definire il descrittore di tipo, dove un puntatore nullo rappresenta il distruttore (che non esiste)
static const struct Class _Point = { sizeof(struct Point), Point_ctor, 0, Point_draw }; const void * Point = & _Point;
move() non è linkato dinamicamente, così omettiamo static per esportarlo da Point.c e non precediamo il suo nome con il nome della classe Point
void move (void * _self, int dx, int dy) { struct Point * self = _self; self —> x += dx, self —> y += dy; }
Ecco conclusa la implementazione dei punti in Point.? assieme al supporto per il dynamic linkage in new.?.