Quindi, lasciamo che sia il selettore a controllare gli oggetti che non si trovano nella classe corrente e lasciamo che i metodi linkati dinamicamente controllino a loro volta gli oggetti nella loro propria classe. Casts omette la chiamata a checks per i metodi che sono chiamati attraverso un selettore.
Ora dobbiamo modificare il selettore. Fortunatamente, sono tutti generati dal report init, ma ci sono comunque varie casistiche: i selettori con un risultato void non restituiscono il risultato del metodo corrente: i selettori con una lista variabile di argomenti devono passare un puntatore al metodo corrente. Init chiama il report selectors in etc.rep che a sua volta delega il lavor al report selector e a vari sottoreports. Ecco un esempio di un tipico selettore:
int differ (const void * _self, const void * b) { int result; const struct Class * class = classOf(_self); assert(class —> differ); cast(Object, b); result = class —> differ(_self, b); return result; }
Questo è generato dal report selector che si torva in etc.rep:
`%header { `n `%result `%classOf `%ifmethod `%checks `%call `%return } `n `n
I report result e return definiscono e restituiscono la variabile result, senza che il tipo da restituire sia void:
% result // if necessary, define result variable `{ifnot `result void `t `result result; `}n % return // if necessary, return result variable `{ifnot `result void `t return result; `}n
Il report ifmethod controlla se il metodo desiderato esiste:
% ifmethod // check if method exists `t assert(class —> `method ); `n
Dobbiamo essere un po' più attenti al report classOf: se un selettore recupera un metodo da Class possiamo fare affidamento solo su classOf() per produrre una descrizione di classe utile, ma per le sottoclassi dobbiamo andare a controllare:
`{if `meta `metaroot `t const struct `meta * class = classOf(_self); `n `} `{else `t const struct `meta * class = ` \ cast( `meta , classOf(_self)); `n `} `n
Il selettore delle superclassi è simile: eccone un esempio:
int super_differ (const void * _class, const void * _self, const void * b) { const struct Class * superclass = super(_class); cast(Object, b); assert(superclass —> differ); return superclass —> differ(_self, b); }
Ancora una volta, se non lavoriamo con Class abbiamo bisogno di controllare il risultato di super(). Ecco un report in etc.rep:
% super—selector // superclass selector `%super—header { `n `{if `meta `metaroot // can use super() `t const struct `meta * superclass = super(_class); `n `} `{else // must cast `t const struct `meta * superclass = ` \ cast( `meta , super(_class)); `n `} `n `%checks `t assert(superclass —> `method ); `n `t `{ifnot `result void return `} \ superclass —> `method \ ( `{() `_ `name `}, `{ifnot `,... ` , app `} ); `n } `n `n
Altri oggetti sono controllati con checks come se il selettore della superclasse fosse un metodo con linking statico.
Grazie ad ooc, e al meccanismo dei report abbiamo determinato uno standard di codifica “sulla difensiva” valido per tutti i metodi che andremo a implementare. Con il cambio di tutti i selettori e con la convenzione di usare %casts in tutti i metodi, controlliamo tutti gli oggetti passati come parametri.
Questo si riflette nella convenzione di usare classi nei tipi di ritorno dei nostri metodi. Per esempio in List.d
Object @ addFirst (_self, const Object @ element); addFirst() è stato illustrato nella sezione 7.7 e ritorna un void *. occ, tuttavia, genera struct Object * addFirst (void * _self, const void * element) { struct List * self = cast(List, _self); cast(Object, element); ... return (void *) element; }struct
Object è un tipo incompleto in un'applicazione. In questo modo il compilatore ansi c xontrolla che il risultato di una chiamata a addFirst() venga assegnato a void * (per cui si spera che il risultato venga in seguito comunque controllato) o che venga passato ad un metodo che si aspetta un void * he, secondo la nostra convenzione, sarà controllato tramite cast(). In generale, attraverso un uso attento e puntuale delle classi nei tipi di ritorno dei metodi, possiamo usare il compilatore Ansi C per controllare errori o incongruenze negli assegnamenti. Una classe è infatti molto più restrittiva di void *.