2012-09-16 1 views
7

Ich implementiere einfache Bibliothek für Listen in C, und ich habe ein Problem mit dem Schreiben find Funktion.Wie schreibe ich C-Funktion akzeptiert (ein) Argument von einem beliebigen Typ

würde ich meine Funktion wie jede Art von Argument zu akzeptieren, zu finden, die beide: find(my_list, 3) und find(my_list, my_int_var_to_find). Ich habe bereits Informationen, was ist der Typ der Liste Elemente.

Für jetzt habe ich gefunden paar Möglichkeiten, mit diesem zu tun:

  • andere Funktion mit Suffix für verschiedene Arten: int findi(void* list, int i), int findd(void* list, double d) - aber ich weiß nicht, wie dieser Ansatz scheint es, wie Redundanz für Ich und eine API ist verwirrend.

  • mit Gewerkschaft:

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

    aber auf diese Weise ich beide zu erzwingen, dass Benutzer wissen über any_type Vereinigung, und es von find vor Aufruf zu erstellen. Ich möchte das vermeiden.

  • mit Variadic-Funktion: int find(void* list, ...). Ich mag diesen Ansatz. Ich bin jedoch besorgt über keine Einschränkungen der Anzahl der Argumente. Der Benutzer kann int x = find(list, 1, 2.0, 'c') schreiben, obwohl ich nicht weiß, was es bedeuten soll.

Ich habe auch Antwort auf diese Frage gesehen: C : send different structures for one function argument aber es ist irrelevant, weil ich nicht-Zeiger Argumente annehmen möchten.

Was ist der richtige Umgang mit dieser Funktion?

+0

Es gibt keins. – Mehrdad

+2

Ich würde Ansatz Nr. 1 und verwenden Sie ein Makro mit dem neuen C11 '_Generic' für die Unterscheidung von Typ. – oldrinb

Antwort

7

Sie stattdessen versuchen könnte Ihre Funktion ähnlich eine generische Funktion wie bsearch Implementierung, die eine binäre Suche auf einem Array von beliebigen Datentypen ausführen:

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

Anstatt Hartcodierung der verschiedenen Implementierungen für verschiedene Datentypen innerhalb der Funktion übergeben Sie stattdessen einen Zeiger auf eine Funktion, die die typabhängige Operation ausführt, und nur die zugrunde liegende Implementierung kennt. In Ihrem Fall könnte das eine Art Traversierungs-/Iterationsfunktion sein. Die andere Sache bsearch muss wissen (abgesehen von der offensichtlichen - Suchschlüssel und Array-Länge) ist die Größe jedes Elements im Array, so dass es die Adresse jedes Elements im Array berechnen und übergeben kann die Vergleichsfunktion.


Wenn Sie eine endliche Liste von Typen hatten, die bearbeitet werden sollten, ist nichts falsch daran, eine Familie von findX() Funktionen zu haben. Das obige Verfahren erfordert eine Funktion für jeden Datentyp, der an die bsearch-Funktion übergeben werden soll. Einer der Hauptunterschiede besteht jedoch darin, dass die allgemeine Funktionalität nicht wiederholt werden muss und die generische Funktion für den Datentyp beliebiger verwendet werden kann.

Ich würde nicht wirklich sagen, es gibt keine richtige Weg, dies zu tun, es liegt an Ihnen und hängt wirklich auf das Problem, das Sie versuchen zu lösen.

+0

Es sieht wirklich am einfachsten aus (auf den ersten Blick). Für jetzt habe ich endliche Liste von Typen. Aber wenn ich einen anderen Typ hinzufügen möchte, muss ich eine andere Wrapper-Funktion schreiben. Ich denke nicht, dass es der beste Weg ist, um dieses Problem zu lösen. –

+0

@BartekChaber: Ja, es gibt nicht wirklich eine elegante Möglichkeit, das zu tun, was Sie in C wollen, im Gegensatz zu Vorlagen in Sprachen wie C++ und Java. Das Generieren einer generischen Funktion und das Verteilen von Funktionszeigern ist, wie ich normalerweise in meinen Projekten vorgehe. – AusCBloke

+0

@AusCBloke: Hängt von Ihren Anforderungen ab, wenn C11 in Ordnung ist, gibt es _Generic. – kyrias

1

Ich bin mir nicht sicher, ob meine eigene Frage höflich ist, aber ich möchte Ihre Meinung.

Ich habe versucht, dieses Problem mit va_list zu lösen. Warum? Weil ich auf diese Weise nur eine Funktion schreiben kann. Bitte beachten Sie, dass ich wissen, welcher Typ das Argument sein sollte. Auf diese Weise kann ich dies tun:

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; 
    } 

Der Vorteil dieser Lösung ist, dass ich vorgestellt Schalter-Fall wickeln kann und mit anderen Funktionen wieder verwenden.

Nachdem ich ein wenig mehr über meine Bedenken bezüglich der Anzahl der Argumente diskutiert habe, habe ich festgestellt, dass printf diese Grenze auch nicht hat. Sie können printf("%d", 1, 2, 3) schreiben. Aber ich meine Lösung mit zusätzlichen Makro gezwickt:

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

Welche Fehlermeldung bei der Kompilierung erzeugt, sagen, dass find_ macro expects 2 arguments not 3.

Was denken Sie darüber? Denken Sie, dass dies eine bessere Lösung als bisher vorgeschlagen ist?

+1

Ich würde definitiv mit dem va_list Ansatz gehen. Ich bin mir nicht sicher, ob das Makro viel hinzufügt. Das wahrscheinlichste Problem ist, dass jemand 'find (list, 12)' findet, wenn die Liste aus Doubles besteht. Das Makro gibt in diesem Fall immer noch keine Warnung aus. Warum sollte jemand versuchen, '(list, 1, 2.0, 'c')' irgendwie zu finden? – asc99c

+0

@ asc99c Warum? Weil die Signatur der Funktion dies zulässt :) Sie haben absolut recht, wenn Sie bei der Suche nicht typsicher sind. Ich hoffe, das ist der einzige Nachteil dieses Ansatzes. –

Verwandte Themen