Programmazione orientata agli oggetti in ANSI-C. Codifica standard -2

Programmazione orientata agli oggetti in ANSI-C. Codifica standard

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 *.

STAMPA     Tags:,

Leave a Reply