Can you cast a function pointer of this type:
void (*one)(int a)
to one of this type:
void (*two)(int a, int b)
and then safely invoke the pointed-to function with the additional arguments(s) it has been cast to take? I had thought such a thing was illegal, that both function types had to be compatible. (Meaning the same prototype--same return value, same parameter list.) But that is exactly what this bit of GTK+ code appears to be doing (taken from here):
g_signal_connect_swapped(G_OBJECT(button), "clicked",
G_CALLBACK(gtk_widget_destroy), G_OBJECT(window));
If you look up the "clicked" signal (or just look at other examples of its use from the first link), you will see that its handlers are expected to be declared like this:
void user_function(GtkButton *button, gpointer user_data);
When you register a handler via g_signal_connect_swapped(), the widget pointer and data pointer arguments are swapped in order, thus, the declaration should look like this instead:
void user_function(gpointer user_data, GtkButton *button);
Here is the problem. The gtk_widget_destroy() function registered as a callback is prototyped like this:
void gtk_widget_destroy(GtkWidget *widget);
to take only a single argument. Presumably, because the data pointer (a GtkWindow) and the pointer to the signaling widget (a GtkButton) are swapped, the sole argument it receives will be the window pointer, and the button pointer, which will be passed after, will silently be ignored. Some Googling has turned up similar examples, even the registering of functions like gtk_main_quit() that take no arguments at all.
Am I correct in believing this to be a standards violation? Have the GTK+ developers found some legal magic to make this all work?
The C calling convention makes it the responsibility of the caller to clean up the arguments on the stack. So if the caller supplies too many arguments, it is not a problem. The additional arguments are just ignored.
So yes, you can cast a function pointer to another function pointer type with the same arguments and then some, and call the original function with too many arguments and it will work.
In my opinion, C89 standards in this regard are quite confusing. As far as I know, they don't disallow casting from/to function with no param specification, 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);
At the end, the compilers must be able to cast from one
to two
, so I don't see the reason to forbid the direct cast.
Anyway, signal handling in GTK+ uses some dark magic behind the scenes to manage callbacks with different argument patterns, but this is a different question.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With