Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you cast a pointer to a function of one type to a function of another type that takes additional arguments?

Tags:

c

gtk

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?

like image 888
Pete Avatar asked Jan 07 '10 18:01

Pete


Video Answer


2 Answers

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.

like image 77
Arve Avatar answered Sep 22 '22 13:09

Arve


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.

like image 43
ntd Avatar answered Sep 19 '22 13:09

ntd