Come fa un oggetto a raggiungere il suo delegato? Quando un oggetto Filter viene costruito riceve il delegato come un argomento. La descrizione di classe Filter.d definisce i tipi di funzioni per le possibili funzioni di callback e i componenti oggetto per mantenere i puntatori:
typedef void (* flagM) (void *, void *, char); typedef int (* nameM) (void *, const void *, const char *); typedef int (* fileM) (void *, const void *, const char *, FILE *); typedef int (* lineM) (void *, const void *, const char *, char *); typedef int (* wrapM) (void *, const void *, const char *); typedef int (* quitM) (void *, const void *); % Class Filter: Object { Object @ delegate; flagM flag; // process a flag nameM name; // process a filename argument fileM file; // process an opened file lineM line; // process a line buffer wrapM wrap; // done with a file quitM quit; // done with all files const char * progname; // argv[0] char ** argv; // current argument and byte char * buf; // dynamic line buffer unsigned blen; // current maximum length % int mainLoop (_self, char ** argv); const char * argval (_self); const char * progname (const _self); int doit (_self, const char * arg); %}
Sfortunatamente, l'ANSI C non permette di utilizzare typedef per definire un header di funzione, ma una classe client come Wc può ancora usare il tipo di funzione per assicurarsi che le sue funzioni di callbacks corrispondano alle aspettative di Filter.
#include "Filter.h" % Wc wc { // (self, filter, fnm, buf) %casts assert((lineM) wc == wc); ….
L'asserzione è banalmente vera ma un buon compilatore ANSI C segnalerà un mismatch di tipo se lineM non corrisponde al tipo di wc():
In function `Wc_wc’: warning: comparison of distinct pointer types lacks a cast
Non abbiamo ancora visto come filter conosca come chiamare wc() per analizzare una linea di input. Filter_ctor() riceve l'oggetto delegato come argomento e può impostare i componenti interessanti per filter.
% Filter ctor { struct Filter * self = super_ctor(Filter(), _self, app); self —> delegate = va_arg(* app, void *); self —> flag = (flagM) respondsTo(self —> delegate, "flag"); ... self —> quit = (quitM) respondsTo(self —> delegate, "quit"); return self; }
Il trucco consiste in un metodo staticamente linkato respondTo() che può essere applicato a qualsiasi Object. Prende un oggetto e un argomento di ricerca e restituisce un puntatore ad una funzione adatta se l'oggetto ha un metodo linkato dinamicamente corrispondente all'argomento di ricerca.
Il puntatore alla funzione restituito potrebbe essere un selettore o il metodo stesso. Se optiamo per il metodo, evitiamo che il selettore chiami quando la funzione di callback viene chiamata. Tuttavia, evitiamo anche di controllare i parametri, operazione compiuta dal selettore. E' meglio essere conservativi piuttosto che pentirsi dei danni fatti, di conseguenza respondsTo() restituisce un selettore.
Progettare un argomento di ricerca è più difficile. Poiché respondsTo() è un metodo generale per tutti i tipi di metodi, non possiamo effettuare il controllo sui tipi a tempo di compilazione, ma abbiamo già mostrato come i delegati possono auto proteggersi. In relazione al controllo sul tipo possiamo ancora permettere che respondsTo() cerchi il selettore di cui si aspetta la restituzione, ossia l'argomento di ricerca potrebbe essere proprio il selettore desiderato. I nomi dei selettori, comunque, sono parti del name space globale di un programma: se cerchiamo un selettore abbiamo implicitamente ristretto la ricerca alle sottoclassi della classe dove il selettore è stato introdotto. RespondsTo() usa una stringa come argomento di ricerca.
Il problema ora consiste nell'associare una stringa con un metodo linkato dinamicamente. Da un punto di vista strettamente logico, si può fare in una delle seguenti occasioni: quando il metodo è dichiarato nel file della descrizione di classe oppure quando viene implementato all'interno del file di implementazione. Entrambe le occasioni determinano un lavoro da compiere per ooc perchè l'associazione fra la stringa e il metodo deve essere memorizzata nella descrizione di classe in modo che respondsTo la possa trovare lì direttamente. La descrizione di classe, tuttavia, è costruita da ooc. Usiamo a scopo illustrativo una estensione dalla sintassi molto semplice:
% WcClass: Class Wc: Object { ... %— line: int wc (_self, const Object @ filter, \ const char * fnm, char * buf); wrap: int printFile (_self, const Object @ filter, \ const char * fnm); quit: int printTotal (_self, const Object @ filter); %}
in un file di descrizione di classe come Wc.d un tag può essere specificato come un'etichetta che precede un metodo linkato dinamicamente. Di default, il nome del metodo verrebbe usato come tag. Un'etichetta vuota sopprime un tag – in questo caso respondsTo() non può trovare il metodo. I tags si applicano ai metodi linkati dinamicamente, ossia essi sono ereditati. Per rendere le cose più flessibili, un tag può anche essere specificato come label in un header di metodo nel file di implementazione. Questo tag è valido solo per la classe corrente.
io uso il c++
Danjel Pacuku @Facebook