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

Can you cast a pointer to a function of this type:

void (*one)(int a) 

to one of this type:

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

and then safely call the specified function with the additional arguments (s) that it was selected to accept? I believed that such a thing was illegal, that both types of functions should be compatible. (The value of the same prototype is the same return value, the same list of parameters.) But this is exactly what this GTK + code bit does (taken from here ):

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

If you look at the signal β€œclicked” (or just look at other examples of its use from the first link), you will see that its handlers should be declared as follows:

 void user_function(GtkButton *button, gpointer user_data); 

When you register a handler with g_signal_connect_swapped (), the arguments of the widget pointer and data pointer are reversed, so the declaration should look like this:

 void user_function(gpointer user_data, GtkButton *button); 

Here is the problem. The gtk_widget_destroy () function registered as a callback is prototyped as follows:

 void gtk_widget_destroy(GtkWidget *widget); 

take only one argument. Presumably, since the data pointer (GtkWindow) and the pointer to the alarm widget (GtkButton) are swapped, the only argument that it gets is the window pointer, and the button pointer that will be passed after will be silently ignored, Some Googling included similar examples, even registering functions like gtk_main_quit () that take no arguments at all.

Do I think this is a violation of the standards? Ask GTK + developers to find legit magic to make it work?

+6
c gtk
source share
3 answers

The C calling convention forces the caller to clear arguments on the stack. Therefore, if the caller submits too many arguments, this is not a problem. Additional arguments are simply ignored.

So yes, you can point to another type of function pointer with the same arguments, and then some, and call the original function with too many arguments, and it will work.

+2
source share

In my opinion, C89 standards are pretty confusing in this regard. As far as I know, they do not prohibit casting from / to work without specifying parameters, therefore:

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

In the end, compilers should be able to drop from one to two , so I see no reason to prohibit direct listing.

In any case, signal processing in GTK + uses some dark magic behind the scenes to control callbacks with different argument patterns, but that's another question.

+1
source share

Now, how cool this is, I am also going through a GTK tutorial now and stumbled upon the same problem.

I tried a few examples, and then asked the question What happens if I throw a pointer to a function by changing the number of parameters , with a simplified example.

The answer to your question (adapted from the excellent answers to my question above and the answers from the question Index of functions other than another signature ):

  • Yes, this is a violation of standard C. If you put a pointer to a pointer to a function of an incompatible type, before you call, you must return it back to its original (or compatible) type. Everything else is undefined behavior.
  • However, what it does in practice depends on the C compiler, especially the call it uses. The most common calling convention (at least on i386) simply pushes the parameters onto the stack in reverse order, so if the function needs fewer parameters than it is provided, it will just use the first parameters and ignore the rest - which is what you want to. This will break, however, on platforms with different calling conventions.

So the real question is why GLib developers did it this way. But I think another question ...

+1
source share

All Articles