respondsTo() deve cercare la descrizione di classe per un tag e restituire il corrispondente selettore. Di conseguenza, la descrizione di classe contiene solo puntatori ai metodi. Chiaramente l'entry del metodo in una descrizione di classe deve essere estesa:
typedef void (* Method) (); // for respondsTo() %prot struct Method { const char * tag; // for respondsTo() Method selector; // returned by respondsTo() Method method; // accessed by the selector }; % Class Object { ... Method respondsTo (const _self, const char * tag);
Method è un tipo semplice di funzione definito nel file di interfaccia per Object. Ogni metodo è memorizzato in una descrizione di classe come componente del tipo struct Method che contiene i puntatori al tag, al selettore e al metodo corrente. RespondsTo() restituisce un Method.
Dato questa visione generale, sono però necessari alcune variazioni. In Object.dc abbiamo bisogno di cambiare l'inizializzazione statica delle descrizioni delle classi Object e Class per usare struct Method
static const struct Class _Object = { { MAGIC, & _Class }, "Object", & _Object, sizeof(struct Object), { "", (Method) 0, (Method) Object_ctor }, { "", (Method) 0, (Method) Object_dtor }, { "differ", (Method) differ,(Method) Object_differ }, ... };
Il report -r in r.rep usa il report link in va.rep per generare un entry nella descriozne di classe per il file di rappresentazione. La nuova versione del report link è molto molto semplice:
% link // component of metaclass structure struct Method `method ;
Infine, il report init in c.rep e c-R.rep usa il metca-ctor-loop in etc.rep per generare il ciclo che dinamicamente riempie la descrizione di classe. Qui dobbiamo anche lavorare con i nuovi tipi:
% meta—ctor—loop // selector/tag/method tuples for `class `t while ((selector = va_arg(ap, Method))) `n `t { `t const char * tag = va_arg(ap, ` \ const char *); `n `t `t Method method = va_arg(ap, Method); `n `n `{%— `%link—it `} `t } `n % link—it // check and insert one selector/method pair `t `t if (selector == (Method) `method ) `n `t `t { `t if (tag) `n `t `t `t `t self —> `method .tag = tag, `n `t `t `t `t self —> `method .selector = selector; `n `t `t `t self —> `method .method = method; `n `t `t `t continue; `n `t `t } `n
Piuttosto che le coppie selettore – metodo specifichiamo ora le tuple composte da selettore – tag – metodo come argomenti per il costruttore della metaclasse. Questo deve essere costruito nel report init in c.rep. Qui invece troviamo la funzione di inizializzazione che Wc genera tramite ooc:
static const void * _Wc; const void * Wc (void) { return _Wc ? _Wc : (_Wc = new(WcClass(), "Wc", Object(), sizeof(struct Wc), wc, "line", Wc_wc, printFile, "wrap", Wc_printFile, printTotal, "quit", Wc_printTotal, (void *) 0)); }
Date le tuple selettore tag metodo nella descrizione di classe, respondsTo() è facile da scrivere. Grazie alla gerarchia delle classi, possiamo calcolare quanti metodo una descrizione di classe contiene e possiamo implementare respondsTo interamente nella classe Object, anche se per sua natura si occupa di classi arbitrarie:
% respondsTo { if (tag && * tag) { const struct Class * class = classOf(_self); const struct Method * p = & class —> ctor; // first int nmeth = (sizeOf(class) — offsetof(struct Class, ctor)) / sizeof(struct Method); // # of Methods do if (p —> tag && strcmp(p —> tag, tag) == 0) return p —> method ? p —> selector : 0; while (++ p, —— nmeth); } return 0; }
Il problema principale di questo approccio è che respondsTo() contiene esplicitamente il primo nome di metodo mai utilizzato e cioè ctor, che gli serve per calcolare il numero di metodi a partire dalla dimensione della descrizione di classe. Mentre ooc potrebbe ottenere questo nome dalla descrizione di classe di Object, potrebbe essere abbastanza complesso costruire un report per ooc in modo da generare respondsTo() con un approccio più generalistico.
ma uno non fa prima a programmare in c++?
Gaetano Lombardo @Facebook