2010-03-16 14 views
11

Ich habe eine Funktion, die einen Block von Daten und die Größe des Blocks und einen Funktionszeiger als Argument nimmt. Dann iteriert es über die Daten und führt eine Berechnung für jedes Element des Datenblocks durch. Im Folgenden ist der wesentliche Umriss, was ich tue:Generische Funktionszeiger in C

int myfunction(int* data, int size, int (*functionAsPointer)(int)){ 
    //walking through the data and calculating something 
    for (int n = 0; n < size; n++){ 
     data[n] = (*function)(data[n]); 
    } 
} 

Die Funktionen, die ich als Argumente bin vorbei dies in etwa so aussehen:

int mycalculation(int input){ 
    //doing some math with input 
    //... 
    return input; 
} 

Dieses gut funktioniert, aber jetzt muss ich weitergeben eine zusätzliche Variable zu meinem Funktionspointer. Etwas nach dem Vorbild

int mynewcalculation(int input, int someVariable){ 
    //e.g. 
    input = input * someVariable; 
    //... 
    return input; 
} 

Gibt es eine elegante Möglichkeit, dies und zugleich zu erreichen Idee, meine gesamte Design zu halten?

+2

Gibt es einen Grund, warum Sie die Funktionsdeklaration nicht einfach so ändern können, dass sie den zusätzlichen ganzzahligen Parameter enthält? –

+0

@dbingham: Wenn ich mich nicht irre, würde ich dann eine zweite "myfunction" brauchen, die einen Funktionszeiger mit zwei "ints" nehmen würde. Das Problem ist, dass es viel mehr Funktionen mit verschiedenen Typen und einer Anzahl von Argumenten geben wird. Das würde mein Design durcheinander bringen. – Lucas

+0

Ich bin mir nicht sicher, ich habe die Frage von Anfang an richtig verstanden. Ich denke, Jefromi hat die Grundlagen sowieso ziemlich gut abgedeckt. –

Antwort

13

Der übliche Weg ist es völlig generisch zu machen mit einem void *, die natürlich alle Arten von Typsicherheit Probleme verursacht:

int map_function(int* data, int size, int (*f_ptr)(int, void*), void *userdata){ 
    //walking through the data and calculating something 
    for (int n = 0; n < size; n++){ 
     data[n] = (*f_ptr)(data[n], userdata); 
    } 
} 

int simple_calculation(int input, void *userdata) { 
    // ignore userdata and do some math with input 
    return input; 
} 

int calculation_with_single_extra_arg(int input, void *userdata) { 
    int input2 = * (int*) userdata; 
    // do some math with input and input2 
    return input; 
} 

int calculation_with_many_extra_args(int input, void *userdata) { 
    my_data_t data = (my_data_t *) userdata; 
    return input * (input * data->a + data->b) + data->c; 
} 

int main(int argc, char **argv) { 
    int array[100]; 
    my_data_t userdata = { 1, 2, 3 }; 

    populate(array, 100); 

    map_function(data, calculation_with_many_extra_args, &userdata); 
} 

Jede gegebene Funktion zu übergeben, dass Prototyp haben muss, aber man kann Schieben Sie alle gewünschten Daten durch userdata. Es gibt absolut keine Typchecking.

Sie könnten auch va_args verwenden, wie dbingham suggeriert; Dies ist jedoch nicht wirklich anders, da der Prototyp für die zu mappende Funktion int(int, va_list) sein muss.

Edit: Ich bevorzuge die void* Ansatz. Die va_list fügt sowieso keine Typesicherheit hinzu und fügt mehr Potential für Benutzerfehler hinzu (insbesondere den Aufruf von va_end zweimal oder die Implementierung der va_arg Schleife nicht richtig). Das void * fügt auch keine zusätzlichen Codezeilen hinzu; es wird sauber durchgelassen, und der Benutzer deroziert es (hoffentlich) in den richtigen Typ (was im allgemeinen Fall wahrscheinlich eine Struktur ist).

+0

Ich kann mir keine saubere Methode vorstellen, aber die hier beschriebene 'void *' Methode ist nicht schlecht. So funktioniert das System wie 'ioctl()'. Sie können alle benötigten Daten (Integer, Struktur, NULL) über den Zeiger übergeben, aber die Funktion und der Aufrufer müssen beide vorsichtig sein, da Sie keine Typprüfung haben, um Sie zu schützen. – bta

+0

Ja, es ist gruselig, aber das ist C für dich! Dies ist auch die Art, wie es mit einer Reihe von GLib-Funktionen gemacht wird (z.B. 'g_hash_table_foreach'). – Cascabel

+0

Ich habe früher etwas Ähnliches versucht, aber das gab mir Hunderte von Warnungen vom Typ: "Argument vom inkompatiblen Zeigertyp übergeben". Kann ich diese ignorieren? – Lucas