Newsgroup: it.comp.lang.c
Titolo: Puntatori a funzioni e dati esterni

Sto cercando di eliminare il più possibile da un vecchio codice c delle "const struct".
Gli obiettivi sono avere un codice più moderno e più flessibilità di configurazione caricando tabelle di dati da un database.
Il più è fatto, ma sono incappato un una struttura con un puntatore di questo tipo:
typedef void SKILL_FUN args( ( int sn, int level,CHAR_DATA *ch, void *vo ) );
 
Il resto della struttura è stato migrato o è in fase di riscrittura.
Come funziona adesso: quando viene richiesta una certa funzionalità, nella struttura si cerca la funzione corrispondente e si richiama quella.
Assodato che non posso certo mettere i puntatori a funzione in qualcosa di esterno, come potrei aggirare il problema?
 
Se può servire, il codice compila e funziona tanto su windows che su linux. I dati esterni arrivano da un db postgresql, ma questo è irrilevante, potrebbero essere anche file.
 
Qualcuno ha suggerimenti?

 
 
Bisognerebbe vedere il problema nello specifico, perché a livello
architetturale non mi sembra una gran soluzione.
 
In ogni caso, in C/C++ il solito "trucco" per chiamare funzioni a partire
dal loro nome (e avere quindi una sorta di reflection dei poveri) è creare
in compilazione (o in partenza) una hashmap che ha i nomi come chiavi e i
puntatori a funzione come valori. Inutile dire che non c'è niente di
automatico, ma con un po' di preprocessore puoi ottenere qualcosa di carino.
 
Ciao!

 Ciao!
 
In realtà ho anche un id della funzione, ma temo che il discorso non cambi: devo sembre avere una mappatura id, funzione. Un po' come è ora.
Valuto l'hashmap, che non è una soluzione che uso abitualmente e mi documento meglio.
Intanto grazie,
ciao!

 
 
Beh, se vuoi chiamare una funzione a partire da qualcosa che un puntatore a
funzione non è serve sempre una mappatura di qualche tipo, non si scappa.
 
Una tabella di hash conviene se hai tante funzioni e le chiami spesso,
altrimenti il solito trucco di preprocessore per riempire un'array di coppie
stringa(o id)/puntatore è più che sufficiente.
 
Ciao!


Secondo me la soluzione di adottare una specie di mappa nome => funzione 
è elegante e non la modificherei
Al massimo, se proprio vuoi, trasformi il tutto in una serie di if .. 
else if .. eccetera
 
 
if (strcmp(nome,"funz1") == 0) funz1(...)
else if (strcmp(nome,"funz2")  == 0) funz2(...)
else if ..
else
	perror(...)
;
 

 
 
Alla fine, visto che ho degli id numerici e che non ho buchi nella sequenza, terrò memorizzato un vettore con id -> puntatore. Almeno guadagno un po' sulla ricerca della funzione.


Puoi sempre se vuoi,visto che gli indici sono numerici, fare uno switch 
case.
L' efficienza è pressochè simile all' accesso al vettore ma comunque il 
vettore con puntatori va piu che bene
 
 
 


L'efficienza *non è* pressochè simile.
Mentre l'accesso al valore di un'array è una singola operazione, a meno di
ottimizzazioni uno switch case (a parte aumentare la complessità ciclomatica
di 1) deve fare i confronti ((n+1)/2 in media) e quindi ha una complessità
algoritmica superiore.
Non vedo perché mai scegliere uno switch al posto di un vettore, a meno che
non se ne possa proprio fare a meno.
 
Ciao!


Non vera, il compilatore trasforma gli swich case in un jump ad un 
indirizzo per preciso in base al valore
Quello che dici è vero per una catena di if .. else if ..
 

 Quello che dici è vero per una catena di if .. else if ..
 
 
Ti sbagli.
 
Quella che dici è un'ottimizzazione di base che il compilatore fa quando può.
Ad ottimizzatore staccato (o, anche a ottimizzatore attaccato quando non è
possibile) farà come ho detto io (per forza di cose).
 
Prova a compilare, ad esempio:
switch(v) {
case 0: /*...*/ break;
case 30: /*...*/ break;
case 10000: /*...*/ break;
}
vedrai che anche al massimo livello di ottimizzazione avrai dei compare.
 
Ciao!
 
P.S. Oltretutto, avere fiducia su quello che "normalmente" fanno i compilatori
quando esiste un'altra opzione più sicura, non è una bella scelta, no?


Si certo, ma nel caso trattato essendo i valori pochi e vicini tra loro 
puoi andare pressochè tranquillo
 
 
 
 
 
 

 puoi andare pressochè tranquillo
 
Sapevo che l'avresti scritto :)
Ci avevo pensato anche io, ma proprio per questo ho scritto il mio post
iniziale: nel caso in oggetto mi sembrava proprio il caso in cui gli id erano
non  sequenziali e invece sparsi; dato che si trattava di un porting mi ero
figurato, forse sbagliando, il porting di un applicativo basato su RPC o su
chiamate locali ad un "server di servizi" come si usava in C o in COBOL 20 o
30 anni fa.
In ogni caso, come spesso scrivo qui, meglio essere il più precisi possibile
senza prendere nulla per scontato piuttosto che rischiare di dare consigli
sbagliati a chi leggerà a distanza di qualche tempo per un problema simile.
Ciao!


Sono stato proprio io a dire a chi ha postato il problema che secondo me 
la mappa di puntatori era una soluzione elegante
Lo switch l' ho consigliato proprio perchè non sembravano piacere molto 
i puntatori
Ma anch' io preferisco la soluzione di mappa con puntatori
Inoltre con lo switch non si sa e dipende dalle ottimizzazioni e dal 
compilatori il range limite di trasformazione in una mappa od una serie 
di if else
Per range piccoli,come dovrebbe essere il caso trattato, si può quasi 
avere la certezza che uno switch verrà trasformato in una mappa di indirizzi
 
 

 puntatori a funzione come valori.
 
Soluzione che in C ha almeno due problemi:
 
- non esiste una hashmap "redy to use";
- i puntatori a funzione devono avere tutti la stessa
  signaure.
 
Secondo me, se le funzioni da chiamare non sono troppe
forse e' meglio usare un costrutto if - else.
 
C:\>iao Angelo

 
 
Poco male, il cast tra puntatori a funzione di firme diverse è sempre safe,
per cui si può dichiarare tutto void(*)(void) e fare i cast dove serve.
 
Ciao!

 forse e' meglio usare un costrutto if - else.
 
O uno switch se possibile
 
 
 
 
 

 
 
Io direi "degenere", "degenerato" è giusto ma mi suona un gran brutto! :D
Comunque semplicemente la funzione di hash è la funzione identità.
 
Per tutti, sempre per la cronaca, o per l'OP se ancora sta cercando:
-  su i sistemi SVr4 o POSIX.1-2001 compatibili ci sono le funzioni di libreria
hcreate/hsearch e simili, già pronte.
- non è che fare una tabella di hash in C sia difficile...
struct elem {
    int key;
    void(*fptr)(void);
    struct elem *next;
} htable[HASHSIZE];
 
e lo scheletro delle funzioni di lookup e insert
 
struct elem *lookup(int key)
{
    struct elem *p;
    for(p=htable[hash(key)]; p!=NULL; p=p->next)
        if (p->key==key)
            return p;
    return NULL;
}
void insert(int key, void(*val)(void))
{
    struct elem *p;
    if ((p=lookup(key))!=NULL) {
        p->fptr = val;
    } else {
        p = malloc(sizeof(*p));
        p->key = key;
        p->fptr = val;
        p->next = htable[hash[key]];
        htable[hash(key)] = p;
    }
}
 
Ovviamente con un po' di controlli in più e qualche aggiustata dove serve
(spero di averla scritta bene, non ho controllato).
 
Ciao!

 per cui si può dichiarare tutto void(*)(void) e fare i cast dove serve.
 
+1
 
-- 
-riko
http://www.enrico-franchi.org/
 
http://rik0-techtemple.blogspot.com/
 

 Io direi "degenere", "degenerato" è giusto ma mi suona un gran brutto! :D
No, hai ragione, mi sono espresso male

Certamente, sono d' accordo con quello che dici
Posix, quindi anche linux, implementa una hashtable anche se sul momento 
non me lo ricordavo.
Gia che siamo in tema, nel caso qualche profano di C leggesse queste 
righe, per loro vorrei solo aggiungere che tecnicamente quello che serve 
è una mappa.
E chiamare una mappa hashtable è tecnicamente errato in quanto una 
hashtable è una mappa ma una mappa non è necessariamente una hashtable 
in quanto per gli algoritmi interni non è  obbligatorio usare una 
funzione hash
 
 

 Per range piccoli,come dovrebbe essere il caso trattato
 
Si tratta di range piccoli.
 
Comunque, ad onor di cronaca, si tratta di una modernizzazione di un vecchio gioco testuale, nel caso specifico. L'esperienza si ribalta poi anche in ambito lavorativo; in ogni caso una buona palestra.