2010-01-07 18 views
6

¿Se puede convertir un puntero función de este tipo:¿Se puede convertir un puntero a una función de un tipo en una función de otro tipo que toma argumentos adicionales?

void (*one)(int a) 

a uno de este tipo:

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

y luego invocar de forma segura al cual apunta a la función con los argumentos adicionales (s) que tiene ha sido lanzado para tomar? Había pensado que tal cosa era ilegal, que ambos tipos de funciones tenían que ser compatibles. (Es decir, el mismo prototipo - el mismo valor de retorno, la misma lista de parámetros.) Pero eso es exactamente lo que parece este trozo de código GTK + para estar haciendo (tomado de here):

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

Si nos fijamos el "clic "señal (o simplemente un vistazo a otros ejemplos de su uso desde el primer enlace), se verá que se espera que sus manipuladores para ser declarado como esto:

void user_function(GtkButton *button, gpointer user_data); 

cuando se registra un controlador a través de g_signal_connect_swapped(), el Los argumentos del puntero del widget y del puntero de datos se intercambian en orden, por lo tanto, la declaración debería verse como esta en su lugar:

void user_function(gpointer user_data, GtkButton *button); 

Aquí está el problema. La función gtk_widget_destroy() registrada como una devolución de llamada es un prototipo de esta manera:

void gtk_widget_destroy(GtkWidget *widget); 

a tomar sólo un único argumento. Presumiblemente, debido a que el puntero de datos (una GtkWindow) y el puntero al widget de señalización (un GtkButton) se intercambian, el único argumento que recibe será el puntero de la ventana, y el puntero del botón, que se pasará después, se ignorará silenciosamente . Algunos Google ha encontrado ejemplos similares, incluso el registro de funciones como gtk_main_quit() que no toman ningún argumento.

¿Estoy en lo cierto al creer que se trata de una violación de las normas? ¿Los desarrolladores de GTK + han encontrado algo de magia legal para que todo esto funcione?

+0

Dupe: http://stackoverflow.com/questions/188839/function-pointer-cast-to-different-signature –

Respuesta

2

La convención de llamadas C hace que la persona que llama tenga la responsabilidad de limpiar los argumentos en la pila. Entonces, si la persona que llama proporciona demasiados argumentos, no es un problema. Los argumentos adicionales simplemente se ignoran.

Así que sí, puede convertir un puntero a otro tipo de puntero a función con los mismos argumentos y más, y llama a la función original con demasiados argumentos y funcionará.

+2

En realidad, creo que no existe tal cosa como "la C convención de llamadas ". El estándar C no lo define, por lo que depende del compilador y de la plataforma qué convención usar. Sin embargo, * la mayoría de los compiladores/plataformas usan la convención de llamadas que usted describe. – sleske

1

En mi opinión, las normas C89 en este sentido son bastante confusas. Por lo que yo sé, no no permitir la fundición de/a funcionar sin especificación de parámetro, por lo que:

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

Al final, los compiladores deben ser capaces de emitir a partir one a two, por lo que don' T ver la razón para prohibir el lanzamiento directo.

De todos modos, la gestión de señales en GTK + utiliza un poco de dark magic detrás de escena para gestionar las devoluciones de llamada con diferentes patrones de argumentos, pero esta es una pregunta diferente.

1

Ahora, qué bueno es eso, actualmente también estoy trabajando en el tutorial de GTK y tropecé con exactamente el mismo problema.

He intentado algunos ejemplos, y luego hice la pregunta What happens if I cast a function pointer, changing the number of parameters, con un ejemplo simplificado.

La respuesta a su pregunta (adaptado de las excelentes respuestas a mi pregunta anterior, y las respuestas de la pregunta Function pointer cast to different signature):

  • Sí, esto es una violación de la norma C. Si convierte un puntero de función en un puntero de función de un tipo incompatible, debe devolverlo a su tipo original (o compatible) antes de llamarlo. Cualquier otra cosa es un comportamiento indefinido.
  • Sin embargo, lo que hace en la práctica depende del compilador de C, especialmente la convención de llamadas que utiliza. La convención de llamada más común (al menos en i386) pasa a sólo hay que poner los parámetros en la pila en orden inverso, de modo que si una función necesita menos parámetros de lo que se suministra, se acaba de utilizar los primeros parámetros e ignorar el resto - el cual es justo lo que quieres Esto será romper sin embargo en plataformas con diferentes convenciones de llamadas.

Así que la verdadera pregunta es por qué los desarrolladores de GLib lo hicieron así. Pero esa es una pregunta diferente, supongo ...

Cuestiones relacionadas