Consiglio Manuali-ListaDoppiamentePuntata tipo dato generico

di il
75 risposte

Consiglio Manuali-ListaDoppiamentePuntata tipo dato generico

Buongiorno a tutti. Mi trovo davanti a qualcosa che programmo per la prima volta.. E cioè una libreria per la gestione di una lista doppiamente puntata che preveda l'inserimento di un qualsiasi tipo di dato(int,float,char ecc..). Ho cercato in rete vario materiale didattico.. e ciò che ho capito è che devo creare la mia struct, e definire un tipo di dato generico (che sia un puntatore a funzione che ritorna void) e sfruttare la proprietà delle callback. Quello che sono riuscito a scrivere fin ora è questo... (potrebbero essere eresie..)

lib.h
typedef struct abr * ABR;
typedef struct abr ** PABR;
typedef void(*TIPODATO)(void*); // dichiarazione dei puntatori di tipo TipoDato
typedef int(*CONFRONTA) (void*,void*); // dichiarazione dei puntatori di tipo Confronta

void getInputInt(void *k);
int confrontaInt (void *a, void *b);
void abr_insert(PABR t, int dim, int tipo, TIPODATO getValue, CONFRONTA Compare);
void copiaDato (void *dest,int dpos,void *src, int spos, int dim);
lib.c
typedef struct abrgenerico
{
    TIPODATO info;
    struct abrgenerico *sx;
    struct abrgenerico *dx} abr;

void abr_insert(PABR t, int dim, int tipo, TIPODATO getValue, CONFRONTA Compare)
{
    int elemento;
    void *key=malloc(tipo);
    getValue(key);
    if (t!=NULL)
    {
      //  if ( Compare(key,t->info)< 0 )
       //     t->sx=abr_insert(t->sx,tipo,getValue,Compare);
       // else if (Compare(key,t->info)>0)
       //     t->dx=abr_insert(t->dx,tipo,getValue,Compare);
    }

    else {
        ABR nodo=(abr *)malloc(sizeof(abr));
        copiaDato(nodo->info,0,key,0,tipo);//  t->info=key ?
        nodo->sx=NULL;
        nodo->dx=NULL;

    }
    free(key);
}

void copiaDato (void *dest,int dpos,void *src, int spos, int dim)
{
    void *dest_addr=dest+(dpos+dim);
    void *src_addr= src+(spos+dim);
    memcpy(dest_addr,src_addr,dim);
}

void getInputInt(void *k)
{
    printf("Inserisci un valore intero: ");
    scanf("%d", (int*)k);
}

int confrontaInt (void *a, void *b)
{
    int inta= *((int*)a);
    int intb= *((int)b);
    return ((inta>intb) ? 1 :
            ((inta<intb) ? -1 : 0));

}
Diciamo che ho scritto una prova solo per l'int.. Quindi ipotizzando un main di questo tipo
int main()
{
    int n,sceltatipo;
    PABR vAbr=NULL;

    printf("Costruisci Abr:\n");
    printf("Quanti elementi vuoi inserire?");
    scanf("%d",&n);

    abr_insert(vAbr,n,sizeof(int),&getInputInt,&confrontaInt);

    return 0;
}
Non so se come approccio iniziale vada bene.. In ogni caso all'interno della funzione inserimento ho commentato delle righe.. in quanto per la variabile t->info dovrei fare un cast, che non riesco a scrivere nel caso appunto di un campo di una lista.
Ciò che cerco è qualche consiglio, se appunto ho iniziato bene, o se conoscente qualche manuale o libro online che posso consultare nel caso in cui non ci ho capito proprio niente!

75 Risposte

  • Re: Consiglio Manuali-ListaDoppiamentePuntata tipo dato generico

    Provo a darti un suggerimento, per la libreria generica.

    La libreria deve accogliere il dato generico e due puntatori all'elemento che precede e a quello segue quindi
    
    struct nodoLista{
        void * Dato;
        struct nodoLista * succ;
        struct nodoLista * prec;
    }
    
    Adesso nelle funzioni che dovrai scrivere devi ricordarti che devi fare il cast a void.
  • Re: Consiglio Manuali-ListaDoppiamentePuntata tipo dato generico

    Ok. Come dicevo non riesco a fare il cast, perchè non riesco ad accedere a dato nel cast..
  • Re: Consiglio Manuali-ListaDoppiamentePuntata tipo dato generico

    Ovviamente non puoi fare il cast all'interno della libreria, perché la libreria non conosce il dato che riceverà. Quello che puoi fare è fare il cast a void nei parametri della funzione oppure creare una libreria che si occupa di gestire il dato.
    Es.
    Nel main quando chiami la funzione inserisci scrivi:
    inserisci(puntatore alla libreria, (void *)datodainserire);
  • Re: Consiglio Manuali-ListaDoppiamentePuntata tipo dato generico

    Infatti quello che devo fare è proprio creare le funzioni che gestiscano gli inserimenti dei vari tipi diversi all'interno della libreria.. per questo avevo dichiarato il campo della struct con TIPODATO (che è un puntatore a void), e all'interno dell'inserimento avevo dichiarato la variabile getValue di tipo TIPODATO.. utilizzando le funzioni getInputInt e confrontaInt perchè ho scritto appunto solo il caso dell'int. Forse non ero stato chiaro su quello che cercavo di fare...


    Ho riletto il tuo messaggio, il tipo dato che si deve inserire la libreria lo conosce nella variabile tipo, che passo dal main, quindi il cast lo devo fare proprio con la variabile tipo.
  • Re: Consiglio Manuali-ListaDoppiamentePuntata tipo dato generico

    Aspetta. Quando scrivi una libreria intendi un modulo generico. Di essa tu conosci solo l'header, fai finta che l'hai comprata da un tuo fornitore e che la puoi solo utilizzare, cioè il tuo fornitore non dice com'è scritta.
    Adesso, quando si alloca una variabile esplicitamente la variabile è allocata nello heap, per cui sta li finché esplicitamente non deallochi, la potrai utilizzare finché hai un puntatore che punta ad essa. Questo puntatore lo devi far puntare alla variabile void * della tua struttura.

    Provo a rifare l'esempio:

    .h
    void inserisci(struct nodo *nodo, void *dato);


    Nel main puoi scrivere:
    
    
    /* alloco lista che chiamo lista*/
    ...
    int *dato;
    
    dato = (int *)malloc(sizeof(int));
    
    dato = 5;
    
    inserisci(lista,(void *)dato);
    
    Cosi facendo hai collegato il campo dato della tua lista alla variabile int del main. Adesso al posto dell'int nel main puoi mettere tutto quello che vuoi.
  • Re: Consiglio Manuali-ListaDoppiamentePuntata tipo dato generico

    Allora, che cosa intendi con int tipo, poi utilizzi questo per la malloc.
    Poi getValue che cos'è?

    Io purtroppo non posso far girare sul PC il codice.
  • Re: Consiglio Manuali-ListaDoppiamentePuntata tipo dato generico

    Purtroppo per sbaglio ho cancellato il mio ultimo messaggio.
    La variabile tipo identifica il tipo di dato che sto inserendo, getValue è un puntatore a void. In realtà io ho intrapreso questo metodo tramite delle ricerche online.. Ecco perchè cercavo consiglio. E' la prima volta che scrivo qualche struttura con un tipo di dato indefinito, e credo di esser un po' confuso.. Forse mi sto complicando anche le cose. Quello che devo fare è definire una libreria per la manipolazione di alberi binari di ricerca che contengono qualunque tipo di dato.. anche definito dall'utente. Non so se sto sulla strada giusta..
  • Re: Consiglio Manuali-ListaDoppiamentePuntata tipo dato generico

    Se imposti così le cose ti complichi la vita.
    Potresti, se vuoi percorrere questa strada, indicare non int tipo ma char *tipo dove passi il nome del tipo di dato che può essere generico.
    Quando scrivi librerie devi pensare che poi chi la utilizza può usarla con un tipo di dato proprio che tu non può affatto immaginare. Quindi getValue non serve a molto.

    Io però ti consiglio di creare una libreria che si occupi della gestione del dato.
  • Re: Consiglio Manuali-ListaDoppiamentePuntata tipo dato generico

    Ok ho capito cosa intenti, e in effetti è giusto.. perchè io all'interno della libreria devo gestire tutto, nel main posso ricevere qualunque tipo di dato, e nel codice del main stesso non vorrei specificare un caso per ogni tipo inserito. Per ovviare a questo problema la mialibreria.c dovrebbe includere un altra libreriadato.h in cui gestisco appunto il dato? Così' intendi tu? Non mi è comunque ben chiaro come faccio a gestirlo il dato in maniera indefinita, e a questo punto il cast avviene comunque nella funzione inserimento?!
  • Re: Consiglio Manuali-ListaDoppiamentePuntata tipo dato generico

    Provo a scriverti al volo una mini libreria
    list.h
    
    typedef struct nodoLista * LISTA;
    
    void inserisci(LISTA , void *);
    
    list.c
    
    typedef struct nodoLista{
        void * Dato;
        struct nodoLista * succ;
        struct nodoLista * prec;
    }
    
    void inserisci(LISTA l, void * d){
       /* Qui hai il puntatore al dato di tipo void, quindi lo puoi inserire al nodo della lista */
    }
    
    
    Adesso il problema lo sposti tutto nel main, quindi quando il main chiama la funzione inserisci dovra passare un dato di tipo (void *).

    Nel main hai allocato un dato, qualsiasi pinco pallo, dico allocato perche deve stare nello heap, allora il main che chiama la funzione inserisci scrivera:
    
    
       inserisci(lista, (void *)pincopallo);
    
    
    in alternativa avrai un modulo:
    dato.h
    
    typedef struct dato pincopallo;
    
    /* Tutte le operazioni sul dato, allocaione del dato, confronto di dati, ecc...*/
    void * getValue(pincopallo d); // ritorna un dato puntato da void *
    
    dato.c
    
    /* Tutte le implementazioni delle operazioni sul dato */
    
    
    Adesso nel main la funzione inserisci la scrivi in questo modo:
    
       inserisci(lista, getValue(pincopallo));
    
  • Re: Consiglio Manuali-ListaDoppiamentePuntata tipo dato generico

    Ok tutto chiaro.. il dubbio che mi rimane è il seguente.. sia nel primo caso, che nel secondo.. se passo a inserisci una variabile void, poi nella libreria come faccio i confronti tra le variabili per gestire inserimento,cancellazione ecc... Cioè nel main, da tastiera l'utente deve dare in input un dato.. La parte che mi sfugge, è che... Come faccio a far gestire questo dato void?

    Esempio...
    In inpunt dò "ciao" ... Nella mia libreria il dato dovrà essere riconosciuto in qualche modo come stringa, per poter effettuare i confronti con gli altri dati presenti nell'abr di quel tipo.
    In che modo il programma capisce di aver ricevuto una stringa e quindi richiama la funzione scritta da me in libreria di ConfrontoStringhe, piuttosto che ConfrontoInt, ecc ecc....

    Non so se sono stato chiaro.
  • Re: Consiglio Manuali-ListaDoppiamentePuntata tipo dato generico

    Quello che dicevo appunto io, perdonami se non sono riuscito a spiegarmi correttamente.

    Distingui la libreria lista da tutto il resto, la lista colleziona solo dati, crea una lista, inserisce in testa, in coda, cancella, ecc... Solo operazioni attinenti alla lista.

    La libreria dato gestisce il dato, quindi:
    confronta due dati, alloca dati, ecc... e questa libreria trattera solo quel tipo di dato, ovvero una per le stringhe, una per struct studente, ecc...

    infatti, se hai una stringa il confronto lo fai con le strcmp, ma se ti trovi un dato struct studente il confronto lo devi fare sui singoli campi di struct studente.

    Questo di solito lo si lascia a chi utilizza la libreria, in questo modo la tua libreria diventa veramente generica, posso usarla io con per collezionare in una lista doppiamente puntata il dato voloAereo che tu non hai la minima idea di come implemento.
  • Re: Consiglio Manuali-ListaDoppiamentePuntata tipo dato generico

    Quindi tu dici di creare una libreria dato per ogni tipo di dato con le varie funzioni che trattano quel tipo di dato, o una libreria dato generica con tutte le funzioni che trattano i vari tipi di dati?
    Quello che non mi è chiaro è se io dal main passo un puntatore a void, in che modo poi il dato viene riconosciuto e gestito da una funzione piuttosto che un'altra? forse non sono chiaro io proprio nella domanda.. cioè ammesso che io abbia per esempio una funzione per il confrontoInt e una per il confrontoStringa rispettivamente:
    int confrontoInt (void *a, void *b)
    {int a1= *(int*) a;
    int b1= *(int*) b;
    return a1-b1;}
    int confrontoStringa (void *a, void *b)
    { char* a1= (char*) a;
    char* b1= (char*) b;
    return strcmp(a1,b1);}
    Se nel main inserisco una stringa, e richiamo la funzione inserimento con un puntatore a void, in che modo all'interno della libreria indirizzo il confronto a confrontoStringa?
  • Re: Consiglio Manuali-ListaDoppiamentePuntata tipo dato generico

    Facciamo ordine.

    Il main sa che dato sta gestendo se no non possible che riesca a gestirlo, quindi puoi utilizzare la funzione adatta per il tipo di dato.
    Non stai distinguendo tra chi utlizza la libreria e chi implementa la libreria.

    Domanda il tuo obiettivo e scrivere la libreria generica? Quella che posso usare anche io?
    Nella libreria non puoi fare confronti come li stai facendo tu. Ti ripeto la libreria non sa nulla dei dati. Devi farlo fare dalla libreria ad hoc per quel dato. Quindi se hai necessita di prendere un dato dalla lista lo prendi dalla libreria, chiedi alla libreria che gestisce quel dato di convertitelo, lo confronti e poi fai quello che ti serve.
Devi accedere o registrarti per scrivere nel forum
75 risposte