Newsgroup: it.comp.lang.c
Titolo: Stack e tipo di dati generico

Come rendere generiche questa struttura e le due funzioni qui sotto?
Ho sentito che bisogna utilizzare void* ma come?
Il tutto mi servirebbe per utilizzare le stesse funzioni per tipi
diversi... sopratutto mi servirebbe poter mettere nello stack una
serie di stringhe e poi prenderle ad ogni esecuzione della pop.
struct strstack
{
	int data;
	struct strstack *pprox;
};
 
typedef struct strstack stack;
typedef stack *pstack;
void stackpush(pstack *punta, int dinfo)
{
	pstack nuovop;
	nuovop = malloc(sizeof(stack));
	if(nuovop != NULL)
	{
		nuovop->data = dinfo;
		nuovop->pprox = *punta;
		*punta = nuovop;
	}
	else
		printf("Memory error.\n");
	return;
}
int stackpop(pstack *punta)
{
	pstack ptemp;
	int valx;
 
	ptemp = *punta;
	valx = (*punta)->data;
	*punta = (*punta)->pprox;
	free(ptemp);
	return (valx);
}
Vi ringrazio anticipatamente.

 serie di stringhe e poi prenderle ad ogni esecuzione della pop.
Devi riconcepire l'oggetto di tipo stack_item perche' abbia, oltre alle 
informazioni per ritrovare l'elemento successivo (o il blocco di elementi 
successivo) un puntatore a void come unico dato.
Dopo di che le funzioni di inserimento/cancellazione lavoreranno 
semplicemente su questo puntatore, ignorando cosa ci sta' dietro. Questa 
parte e' relativamente facile da fare.
Ti presento 2 possibili funzioni push e pop, non complete (ma penso si 
notasse :P):
<code>
    int stack_push(stack s, void * oggetto)
    {
        alloca_lo_spazio_per_un_nuovo_item(s);
        s->top.data = oggetto;
        aggiorna_situazione_stack(s);
        if (tutto_oki())
            return 1;
    }
    int stack_pop (stack s, void ** e)
    {
        stack_item si;
        controlli_del_caso(s);
        *e = s->top.data;
        aggiorna_situazione_stack(s);
        if (tutto_oki())
            return 1;
    }
</code>
Noterai che sono molto simili a quelle che hai postato.
Esempio di funzionamento:
<code>
    char * prova = "stringa", *ris;
    void * tmp;
    stack_push(s, prova);
    stack_pop(s, &tmp);
    ris = tmp;
    puts (ris);
</code>
Uso il puntatore tmp per semplificare l'esempio e non dover usare cast 
espliciti.
Ovviamente nella funzione chiamante devi sapere esattamente qual'e' il tipo 
di dato memorizzato nello stack.
Puoi complicarti di piu' la vita se tu volessi utilizzare la programmazione 
generica [1], ovvero implementare un'operazione che funziona 
indipendentemente dal tipo di dato utilizzato.
Esempio:
<code>
    int funzione_generica (ADT A, int (*p_func)(void *data))
    {
        per_ogni_elemento a in A
            if (!p_func(a.data))
                return 0;
        return 1;
    }
</code>
Dopo di che, dovresti defirti una funzione appropriata a seconda del tipo di 
dati contenuto nello stack. Immagina che siano stringhe:
<code>
    int f_stringa (void *s) {
        char * tmp;
        tmp = s;
        if (s == NULL) {
            puts("Stringa vuota");
            return 0:
        }
        else
            printf("Stringa di %d caratteri\n", strlen(tmp));
        return 1;
    }
</code>
Dopo di che puoi utilizzare la funzione in questo modo:
<code>
    funzione_generica(adt, f_stringa());
</code>
La funzione che ti ho proposto e' molto grezza, perche' non mantiene una 
storia tra una passata e l'altra. Quelle che utilizzavo io nelle ADT erano 
piu' complesse perche' mantenevano anche una "storia", tra un'iterazione e 
l'altra, in modo simile a quello che fanno le funzioni generiche fornite 
con le glib.
Ti consiglio appunto di:
- guardare la documentazione delle glib per vedere come funzionano li le ADT 
  (in particolare come hanno implementato le operazioni sulle ADT)
- guardare anche solo il qsort() della libreria standard, per vedere come 
  funzioni un algoritmo di programmazione generica
[1] Si parla di programmazione generica, quando implementi un algoritmo che 
    funziona su qualunque tipo di dato. Un esempio (conosciuto, ma non 
    unico) sono i template in C++. Quello che qui fai passando un puntatore 
    a funzione, nei template lo fai implementando (o sfruttando se il tipo 
    e' gia' definito) alcuni operatori / metodi degli oggetti del template.
-- 
Mox
"Trust the programmer."
"Don't prevent the programmer from doing what needs to be done."
"Keep the language small and simple."
"Provide only one way to do an operation."

successivo) un puntatore a void come unico dato.
.
.
.
ti ringrazio, ora provo poi ti faccio sapere. Ciao.