2012-09-16 28 views
7

Io sono l'attuazione semplice libreria per gli elenchi in C, e ho un problema con la scrittura find la funzione.Come scrivere funzione C accettare (uno) argomento di qualsiasi tipo

vorrei la mia funzione di accettare qualsiasi tipo di argomento da trovare, sia: find(my_list, 3) e find(my_list, my_int_var_to_find). Ho già informazioni sul tipo di elementi della lista.

Per ora ho trovato paio di modi che si occupano di questo:

  • funzione diversa con suffisso per diverse tipologie: int findi(void* list, int i), int findd(void* list, double d) - ma non mi piace questo approccio, sembra che la ridondanza per io e un'API sono confusi.

  • usando l'unione:

    typedef union { 
        int i; 
        double d; 
        char c; 
        ... 
    } any_type; 
    

    ma in questo modo forzo utente sia per conoscere any_type unione, e di creare prima invocazione di find. Vorrei evitarlo.

  • utilizzando la funzione variadic: int find(void* list, ...). Mi piace questo approccio. Tuttavia, sono preoccupato per le restrizioni sul numero di argomenti. L'utente è libero di scrivere int x = find(list, 1, 2.0, 'c') anche se non so cosa dovrebbe significare.

ho visto anche la risposta a questa domanda: C : send different structures for one function argument ma è irrilevante, perché voglio accettare argomenti non-puntatore.

Qual è il modo corretto di gestire questa funzione?

+0

Non ce n'è uno. – Mehrdad

+2

Mi piacerebbe avvicinarmi al n. 1 e utilizzare una macro usando il nuovo C11 '_Generic' per distinguere il tipo. – oldrinb

risposta

7

Si potrebbe invece provare l'attuazione di vostra funzione simile a una funzione generica come bsearch, che può effettuare una ricerca binaria su una matrice di qualsiasi tipo di dati:

void *bsearch(const void *key, const void *base, size_t nmemb, size_t size, 
       int (*compar)(const void *, const void *)) 

Invece di codificare le diverse implementazioni per diversi tipi di dati all'interno della propria funzione, si passa invece un puntatore a una funzione che eseguirà l'operazione dipendente dal tipo e conosce solo l'implementazione sottostante. Nel tuo caso, potrebbe trattarsi di una sorta di funzione di attraversamento/iterazione.

L'altra cosa bsearch deve sapere (a parte l'ovvio - chiave di ricerca e lunghezza dell'array) è la dimensione di ogni elemento nell'array, in modo che possa calcolare l'indirizzo di ogni elemento nell'array e passarlo a la funzione di confronto.


Se si dispone di un elenco limitato di tipi che devono essere utilizzati, non c'è nulla di sbagliato nell'avere una famiglia di funzioni findX(). Il metodo precedente richiede una funzione per ogni tipo di dati da passare alla funzione bsearch, tuttavia una delle differenze principali è che funzionalità comune non deve essere ripetuta e la funzione generica può essere utilizzato per qualsiasi tipo di dati .

Non direi davvero che c'è un corretto modo per farlo, dipende da te e dipende davvero dal problema che stai cercando di risolvere.

+0

Sembra davvero il modo più semplice (a prima vista). Per ora ho una lista finita di tipi. Ma se voglio aggiungere un altro tipo ho bisogno di scrivere un'altra funzione wrapper. Non penso che sia il modo migliore per risolvere questo problema. –

+0

@BartekChaber: Sì, non c'è un modo elegante per fare ciò che si desidera in C, a differenza dei modelli in linguaggi come C++ e Java. Fare una funzione generica e aggirare i puntatori di funzione è come di solito lo faccio nei miei progetti. – AusCBloke

+0

@AusCBloke: dipende dalle tue esigenze, se C11 va bene c'è _Generic. – kyrias

1

Non sono sicuro che rispondere alla mia domanda sia educato, ma voglio la tua opinione.

Ho provato a risolvere questo problema utilizzando va_list. Perchè così? Perché in questo modo posso scrivere solo una funzione. Per favore, ricorda che I sa che tipo l'argomento dovrebbe essere. In questo modo posso fare questo:

int find(void* list, ...) { 
     any_type object = {0}; 
     int i = -1; 
     va_list args; 
     va_start(args, list); 
     switch(type_of_elem(list)) { 
     case INT: object.i = va_arg(args, int); break; 
     ... 
     } 
     /* now &object is pointer to memory ready for comparision 
     * f.eg. using memcmp */ 
     return i; 
    } 

Il vantaggio di questa soluzione è che posso avvolgere presentato switch-case e riutilizzarlo con altre funzioni.

Dopo una ricerca un po 'più sulla mia preoccupazione per quanto riguarda il numero illimitato di argomenti mi sono reso conto che printf manca anche questo limite. Puoi scrivere printf("%d", 1, 2, 3). Ma ho ottimizzato la mia soluzione con la macro aggiuntive:

#define find_(list, object) find((list), (object)) 

che produce messaggio di errore in fase di compilazione, dicendo che find_ macro expects 2 arguments not 3.

Cosa ne pensi? Pensi che questa sia una soluzione migliore di quanto suggerito in precedenza?

+1

Vorrei assolutamente andare con l'approccio va_list. Non sono sicuro che la macro aggiunga molto. Il problema più probabile è che qualcuno esegua 'find (list, 12)' quando la lista è doppia. La macro continua a non dare un avvertimento in quel caso. Perché qualcuno dovrebbe provare a fare 'find (list, 1, 2.0, 'c')' comunque? – asc99c

+0

@ asc99c Perché? Perché la firma della funzione lo consente :) Hai assolutamente ragione a non essere sicuro durante la ricerca. Spero che questo sia l'unico svantaggio di questo approccio. –