Programmazione orientata agli oggetti in ANSI-C. Strategia di implementazione

Sappiamo cosa deve fare ooc. Come scriviamo però il preprocessore? Per ragioni di efficienza, dovremmo basarci su piattaforme come lex o yacc, ma una prima implementazione è molto meno costosa se ci basiamo su un linguaggio di programmazione come awk o perl.

Se il preprocessore così creato funziona, avremo in mano la perfetta padronanda degli algoritmi e delle strutture dati necessarie, e allora sì che sarà poi semplice trascriverle o tradurle in una implementazione più efficiente: addirittura potremmo usare una utility che trasformi un preprocessore creato tramite awk in un preprocessore in C. Ma soprattutto, grazie ad una prima implementazione molto semplice possiamo verificare che la nostra idea sia fattibile e conveniente.

Ooc effettua il parsing delle descrizioni delle classi e costruisce un database. Dato il database, le opzioni a linea di comando decideranno cosa verrà generato. Come abbiamo già visto la generazione può essere basata su qualche tipo di pattern con parole da stamparsi direttamente e con parole da rimpiazzare con le informazioni recuperate dal databse. I nostri patterns contengono loop, così sempre che un pattern sia proprio una funzione awk con strutture di controllo e variabili.

Una prima implementazione di ooc funzionava proprio in questo modo, ma è stato provato che è difficile da modificare. C'è quindi un modo ancora più sofisticato: un linguaggio di report molto semplice con rimpiazzamento del testo, iterazioni e costrutti condizionali, che viene usato per rappresentare un pattern e che è interpretato da ooc per generare l'output.

L'implementazione viene spiegata più in dettaglio nell'appendice B. Il linguaggio di report è definito come parte del manuale di ooc nell'appendice C. Il linguaggi di report ha più o meno 25 rimpiazzamenti, 10 loop, due costrutti condizionali e un modo per chiamare un report come parte di un altro report. Ad esempio, ecco come i selettori dovrebbero essere generati:

`{if `newmeta 1
`{%—
`result `method ( `{() `const `type `_ `name `}, ) { `n
`t const struct `meta * class = classOf(self); `n
`%casts
`t assert(class —> `method ); `n
`t `{ifnot `result void return `} \
class —> `method ( `{() `name `}, ); `n
} `n
`}
`}

All'interno di reports, ooc trova tutte quelle parole interessanti che iniziano con un back quote; i gruppi iniziano con '{ e con '} e possono essere sia loop che costrutti condizioni; una chiamata a un report inizia con '%; tutte le altre parole che iniziano con un back quote sono rimpiazzati da informazioni provenienti dal database.

'{if prende le due parole successive ed esegue il resto del gruppo se le parole sono uguali. 'newmeta è rimpiazzato da 1 se ooc lavora con una descrizione di classe che definisce una nuova metaclasse. Di conseguenza, i selettori sono generati solamente per una nuova metaclasse.

'{%- è un loop che lavora su metodi dinamicamente linkati, 'method viene sostituito con il nome del metodo corrente. 'result è il tipo di risultato corrente.

'{() è un loop sui parametri del metodo corrente. Il significato di 'const, 'type e 'name dovrebbe essere pressochè ovvio: sono porzioni della dichiarazione dei parametri correnti. '- è un underscore se il parametro corrente è un oggetto della classe corrente. '} è un piccolo trucco: genera una virgola se c'è un altro parametro e termina il loop come qualsiasi altro token che inizia con '}.

'%casts chiama un altro report, casts che è responsabile per importare gli oggetti parametro. Per adesso, questo report appare come segue:

% casts // the %casts statement
`{() // import
`{if `_ _
`t `const struct `cast * `name = _ `name ; `n
`}
`}n
`{if `linkage % // for static linkage, need to check
`%checks
`}

Una linea che inizia con % precede i report in un file di report e introduce il nome del report. Il resto del report casts dovrebbe essere chiaro: 'cast si riferisce al nome della classe dell'oggetto parametro e 'linkage è il tipo di linkage del metodo corrente, ossia uno dei simboli di sezione nella descrizione di classe. Costruiamo una variabile locale per dereferenziare un parametro se esso è un oggetto della classe corrente. '}n è un'altro trucchetto: genera una nuova linea se tutto il precedente è stato generato per un gruppo.

'%casts è anche responsabile del controllo di qualsiasi oggetto fornito come parametro per un metodo con linkage statico. Poiché i selettori hanno un problema simile, usiamo un report separato chiamato checks che può essere chiamato da un report per generare selettori.

% checks // check all object parameters
`{()
`{ifnot `cast `
`t assert( `name ); `n
`}fi
`}n

Fino al prossimo capitolo, ci limitiamo a stare in guardia in relazione ai puntatori nulli chiamando assert(). Questo test richiede due oggetti. ' viene rimpiazzato da niente altro, ossia generiamo assert() se stiamo controllando un oggetto parametro da una classe arbitraria.

Due parole non sono ancora state discusse in dettaglio: 't genera un tab e 'n genera una carattere di andata a capo. Vogliamo generare programmi in C leggibili; di conseguenza dobbiamo monitorare attentamente quanto spazi bianchi stiamo generando. Possiamo quindi indentare i reprot basandoci sulla loro propria struttura nel gruppo, ooc non genererà spazi bianchi in eccesso e genererà una sola linea vuota per riga. 't deve essere usato per migliorare l'indentazione e 'n deve essere specificato per suddividere l'output su più linee.

Scarica subito una copia gratis

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend