2010-01-07 7 views
6

Können Sie werfen einen Funktionszeiger dieser Art:Können Sie einen Zeiger auf eine Funktion eines Typs in eine Funktion eines anderen Typs umwandeln, die zusätzliche Argumente benötigt?

void (*one)(int a) 

einem dieser Art:

void (*two)(int a, int b) 

und rufen Sie dann sicher das spitze zu den zusätzlichen Argumente (s) fungiert es hat wurde geworfen zu nehmen? Ich hatte gedacht, dass so etwas illegal ist, dass beide Funktionstypen kompatibel sein müssen. (Gemeint ist den gleichen Prototyp - gleichen Rückgabewert, gleiche Parameterliste). Aber das ist genau das, was das Bit von GTK + Code erscheint (von here genommen) zu tun:

g_signal_connect_swapped(G_OBJECT(button), "clicked", 
         G_CALLBACK(gtk_widget_destroy), G_OBJECT(window)); 

Wenn Sie die „geklickt nachschlagen "-Signal (oder gerade Blick auf andere Beispiele für die Verwendung von der ersten Link), werden Sie so sehen, dass deklariert werden, um seine Handler erwartet:

void user_function(GtkButton *button, gpointer user_data); 

Wenn Sie einen Handler über g_signal_connect_swapped registrieren(), die Widgetzeiger und Datenzeigerargumente werden in der Reihenfolge vertauscht. Daher sollte die Deklaration stattdessen folgendermaßen aussehen:

void user_function(gpointer user_data, GtkButton *button); 

Hier ist das Problem. Die gtk_widget_destroy() Funktion als Rückruf registriert wird wie folgt prototypisiert:

void gtk_widget_destroy(GtkWidget *widget); 

nur ein einziges Argument zu nehmen. Vermutlich, weil der Datenzeiger (ein GtkWindow) und der Zeiger auf das Signalisierungs-Widget (ein GtkButton) vertauscht sind, ist das einzige Argument, das es empfängt, der Fensterzeiger und der Tastenzeiger, der nachher übergeben wird, wird im Hintergrund ignoriert . Etwas Googeln hat ähnliche Beispiele aufgetaucht, sogar die Registrierung von Funktionen wie gtk_main_quit(), die überhaupt keine Argumente annehmen.

Bin ich richtig, wenn ich glaube, dass dies eine Standardverletzung ist? Haben die GTK + -Entwickler eine legale Magie gefunden, um das alles zu funktionieren?

+0

Dupe: http://StackOverflow.com/Questions/188839/Function-Pointer-cast-to-different-Signature –

Antwort

2

Die C-Aufrufkonvention macht es in der Verantwortung des Aufrufers, die Argumente auf dem Stapel zu bereinigen. Wenn der Aufrufer also zu viele Argumente liefert, ist das kein Problem. Die zusätzlichen Argumente werden einfach ignoriert.

Also ja, Sie können einen Funktionszeiger auf einen anderen Funktionszeigertyp mit den gleichen Argumenten und dann einige, und rufen Sie die ursprüngliche Funktion mit zu vielen Argumenten und es funktioniert.

+2

Eigentlich glaube ich, dass es so etwas wie "die C Aufrufkonvention ". Der C-Standard definiert es nicht, daher ist es Sache des Compilers und der Plattform, welche Konvention zu verwenden. Die meisten Compiler/Plattformen verwenden jedoch die von Ihnen beschriebene Aufrufkonvention. – sleske

1

Meiner Meinung nach sind C89-Standards in dieser Hinsicht ziemlich verwirrend. Soweit ich weiß, dass sie nicht aus/nicht zulassen Gießen ohne param Spezifikation zu funktionieren, so:

typedef void (*one)(int first); 
typedef void (*two)(int first, int second); 
typedef void (*empty)(); 

one src = something; 
two dst; 

/* Disallowed by C89 standards */ 
dst = (two) src; 

/* Not disallowed by C89 standards */ 
dst = (two) ((empty) src); 

Am Ende müssen die Compiler der Lage sein, one-two zu werfen, so dass ich don‘ Ich sehe den Grund, die direkte Besetzung zu verbieten.

Wie auch immer, die Signalverarbeitung in GTK + verwendet einige dark magic hinter den Kulissen, um Callbacks mit unterschiedlichen Argumentmustern zu verwalten, aber das ist eine andere Frage.

1

Nun, wie cool ist das, ich arbeite mich auch gerade durch das GTK-Tutorial und stolperte über genau das gleiche Problem.

Ich habe ein paar Beispiele versucht, und dann Frage What happens if I cast a function pointer, changing the number of parameters, mit einem vereinfachten Beispiel.

Die Antwort auf Ihre Frage (von den ausgezeichneten Antworten auf meine Frage oben angepasst, und die Antworten von Frage Function pointer cast to different signature):

  • Ja, das ist eine Verletzung des C-Standard ist. Wenn Sie einen Funktionszeiger auf einen Funktionszeiger eines inkompatiblen Typs umwandeln, müssen Sie ihn vor dem Aufruf auf den ursprünglichen (oder einen kompatiblen) Typ zurücksetzen. Alles andere ist undefiniertes Verhalten.
  • Wie es in der Praxis funktioniert, hängt jedoch vom C-Compiler ab, insbesondere von der verwendeten Aufrufkonvention. Die häufigste Aufrufkonvention (zumindest bei i386) besteht darin, die Parameter einfach in umgekehrter Reihenfolge auf den Stack zu setzen. Wenn also eine Funktion weniger Parameter benötigt als sie geliefert wird, werden nur die ersten Parameter verwendet und der Rest ignoriert ist genau das, was du willst. Diese wird jedoch auf Plattformen mit unterschiedlichen Aufrufkonventionen brechen.

Also die eigentliche Frage ist, warum die GLib-Entwickler es so gemacht haben. Aber das ist eine andere Frage, die ich denke ...

Verwandte Themen