Programmazione orientata agli oggetti in ANSI-C. Implementazione della superclasse – Name – 2

Dato un metodo generale di ricerca e inserimento, possiamo facilmente gestire la nostra tabella dei simboli. Per prima cosa dobbiamo confrontare una chiave rispetto agli elementi della tabella.

static int cmp (const void * _key, const void * _elt)
{ const char * const * key = _key;
const struct Name * const * elt = _elt;
return strcmp(* key, (* elt) —> name);
}

Come chiave, passeremo alla funzione solo l'indirizzo di un puntatore al testo del simbolo di input. Gli elementi della tabella sono strutture di tipo Name e noi ci occuperemo solo del loro componente .name.

Cercare o inserire elementi significa chiamare binary() con gli appositi parametri. Poiché non conosciamo il numero di simboli in anticipo, dobbiamo però essere sicuri che ci sia sempre spazio per espandere la tabella.

static struct Name ** search (const char ** name)
{ static const struct Name ** names; /* dynamic table */
static size_t used, max;
if (used >= max)
{ names = names
? realloc(names, (max *= 2) * sizeof * names)
: malloc((max = NAMES) * sizeof * names);
assert(names);
}
return binary(name, names, & used, sizeof * names, cmp);
}

NAMES è la costante definita con l'iniziale allocazione degli elementi della tabella; ogni volta che eseguiamo, raddoppiamo la dimensione della tabella.
Search() prende l'indirizzo del puntatore del testo da trovare e restituisce l'indirizzo della entry della tabella.

Se il testo non viene trovato nella tabella, binary() ha provveduto ad inserire la chiave – ossia solo il puntatore al testo, non la struct Name – nella tabella. Questa strategia viene attuata per rendere più semplice la gestione di screen(), che costruisce un nuovo elemento della tabella se un identificare dall'input è veramente sconosciuto.

int screen (const char * name)
{ struct Name ** pp = search(& name);
if (* pp == (void *) name) /* entered name */
* pp = new(Var, name);
symbol = * pp;
return (* pp) —> token;
}

Screen() lascia che sia search() ad analizzare il simbolo di input. Se il puntatore al testo del simbolo è inserito all'interno della tabella dei simboli, dobbiamo rimpiazzarlo da una entry che descrive il nuovo identificatore.

Per screen() un nuovo identificatore deve essere una variabile. Ipotizziamo che ci sia una descrizione di tipo Var che conosce come costruire strutture Name che descrivono le variabili e lasceremo quindi che new() faccia il resto. In ogni caso, lasceremo che symbol punti alla entry della tabella dei simboli e restituiremo il suo valore di .token.

void install (const void * np)
{ const char * name = ((struct Name *) np) —> name;
struct Name ** pp = search(& name);
if (* pp != (void *) name)
error("cannot install name twice: %s", name);
* pp = (struct Name *) np;
}

Install() è un po' più semplice. Accetteremo un oggetto di tipo Name e lasceremo che search() lo trovi nella tabella dei simboli. Si suppone che Install() abbia a che fare solo con i nuovi simboli, così dovremmo sempre essere in grado di inserire l'oggetto al posto del suo nome. Altrimenti, se search() trovasse davvero un simbolo... saremmo nei guai!

Scarica subito una copia gratis

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend