2008-09-02 12 views
1

Wie der Titel bereits angibt, versuche ich eine verschachtelte Funktion zu deklarieren und einen Zeiger auf diese Funktion zurückzugeben. Ich möchte, dass diese Funktion 'nicht' einen neuen Funktionszeiger zurückgibt, der die Negation der ursprünglichen Funktion zurückgibt. HierZurückgeben von Funktionszeiger an eine verschachtelte Funktion in C

ist, was ich habe:

someType not(someType original) { 
    int isNot(ListEntry* entry) { 
     return !original(entry); 
    } 

    someType resultFunc = calloc(1024, 1); 
    memcpy(resultFunc, &isNot, 1024); 

    return resultFunc; 
} 

sometype ist definiert als:

typedef int(*someType)(ListEntry* entry) 

Antwort

0

Ich bin mit GCC.

Sie auf verschachtelte Funktionen drehen können durch die Flagge mit:

-fnested-functions 

beim Kompilieren.

0

Ich habe auch nie von verschachtelten Funktionen in C gehört, aber wenn gcc dies unterstützt, wird das nicht so funktionieren, wie Sie es erwarten. Sie kopieren einfach nur die Maschinenanweisungen von isNot, und das enthält nicht den tatsächlichen Wert von "Original" zu dem Zeitpunkt, an dem "nicht" aufgerufen wird.

Sie sollten eine C++ - Klasse verwenden, um eine zu implementieren, die einen Zeiger speichert, den Sie mit dem Wert von "original" initialisieren und eine Instanz dieser Klasse von "not" zurückgeben können.

19

Steve, Sie haben ein völlig falsches mentales Modell, was eine C-Funktion ist.

someType resultFunc = calloc(1024, 1); 
memcpy(resultFunc, &isNot, 1024); 

aus dem Code-Fragment, kann ich vermuten, dass Sie denken, dass Sie Funktion des kompilierten Code in einen Block von Speicher kopieren und sie dann wieder verwenden. So etwas stinkt nach Lisp, nur nicht in Lispeln machst du es nicht so.

In der Tat, wenn Sie sagen "& IsNot", erhalten Sie einen Zeiger auf Funktion. Das Kopieren des Speichers, auf den der Zeiger zeigt, ist kontraproduktiv - der Speicher wurde initialisiert, als Sie Ihre ausführbare Datei in den Speicher geladen haben, und sie ändert sich nicht. In jedem Fall würde das Schreiben von someFunc() zu einem Core-Dump führen, da der Heap-Speicher, auf dem someFunc läuft, nicht ausgeführt werden kann - dies schützt Sie vor allen Arten von Viren.

Sie scheinen eine Implementierung von Closures in C zu erwarten. Diese Implementierung ist einfach nicht da. Im Gegensatz zu Lisp oder Perl oder Ruby kann C Elemente eines Stapelrahmens nicht beibehalten, nachdem Sie diesen Rahmen verlassen haben. Selbst wenn in einigen Compilern verschachtelte Funktionen erlaubt sind, bin ich mir sicher, dass Sie innerhalb dieser Funktionen nicht auf nicht-globale Variablen verweisen können. Das Schließende an Closures ist in der Tat ein C++ - Objekt, das den Zustand speichert und operator() implementiert, aber es ist ein völlig anderer Ansatz, und Sie müssten die Dinge immer noch manuell erledigen.

Update: here ist der relevante Teil der GCC-Dokumentation. Suchen Sie nach "Aber diese Technik funktioniert nur so lange, wie die enthaltende Funktion (hack, in diesem Beispiel) nicht beendet wird."

+0

danke für den Link, mein Lieblingszitat war "Wenn Sie versuchen, die verschachtelte Funktion über ihre Adresse aufzurufen, nachdem die enthaltene Funktion beendet wurde, wird die Hölle los." – luke

1

Sie werden nicht in der Lage sein, dies in der von Ihnen gewünschten Weise zu tun. Sie haben ein paar alternative Möglichkeiten.

können Sie Makros verwenden:

#define FN_NOT(F) !F 
#define notSomeFunc FN_NOT(someFunc) 
... 
x = notSomeFunc(entry); 

Aber ich vermute, dass Sie die negierte Funktion zu übergeben um auf andere Funktionen in der Lage sein wollte, die Funktionszeiger nehmen, so dass wird nicht funktionieren.

Sie Ihre Schnittstellen ändern können einige zusätzliche Informationen zu übernehmen, zB

struct closure { 
    void *env; 
    int (*f)(struct closure* extra, ListEntry*); 
}; 

static int isNot(struct closure* extra, ListEntry *entry) { 
    someType original = extra->env; 
    return !original(entry); 
} 

struct closure not(someType original) { 
    closure rv; 
    rv.env = original; 
    rv.f = &isNot; 
    return rv; 
} 

Und dann mag es verwenden:

struct closure inverse_fn; 
inverse_fn = not(&fn); 
if(inverse_fn.f(&inverse_fn, entry)) { 
    ... 
} 

Es gibt andere Dinge, die Sie können, wie Jiting Funktionen zur Laufzeit versuchen, aber diese Arten von Techniken werden plattform- und architekturabhängig sein. Diese Lösung ist peinlich, aber rein C und portabel.