Programmazione orientata agli oggetti in ANSI-C. Il metodo respondsTo

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.

Scarica subito una copia gratis

Una risposta

  1. Avatar photo @Facebook 17 Settembre 2010

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend