diff options
author | Philip Withnall <philip.withnall@collabora.co.uk> | 2014-06-23 01:00:40 +0100 |
---|---|---|
committer | Philip Withnall <philip.withnall@collabora.co.uk> | 2014-06-27 17:36:52 +0100 |
commit | d395a38afc6410bc9d60f501745300305e137b37 (patch) | |
tree | 190c487d09e0771cdfbd43d5ba5ee58f9f220258 /tests | |
parent | 10ba1c82bd6f58ccab99eda85416ec5600ac015d (diff) |
gsignal: Correctly handle incorrect parameter counts in signal callbacks
This is a minefield.
g_signal_connect_swapped() is intended to be used in situations where
the number of formal parameters in the callback function differs from
the number of actual parameters emitted by the signal. This allows the
common idiom of (e.g.) calling gtk_widget_hide() on a signal callback
from another object, with the widget being passed in the user_data.
It turns out that these situations are entirely implementation defined.
The C specification disavows all knowledge of how parameter passing
should work when the callback function prototype isn’t known, leaving
this to the calling convention to define.
All calling conventions that we care about handle this sensibly,
explicitly covering the case of the number of actual parameters
exceeding the number of formal parameters, just like they handle
varargs. However, there are a few calling conventions which won’t handle
this properly, and will cause explosions should you try. For example, I
believe that passing a WinAPI function (calling convention: stdcall) as
a signal callback will cause problems. I haven’t verified this
experimentally though.
The gsignal-checker now allows for the number of formal and actual
parameters to differ, but only if the calling convention for the
callback function is safe. That should cover things.
Diffstat (limited to 'tests')
-rw-r--r-- | tests/gsignal-connect.c | 51 | ||||
-rw-r--r-- | tests/gsignal.head.c | 15 |
2 files changed, 60 insertions, 6 deletions
diff --git a/tests/gsignal-connect.c b/tests/gsignal-connect.c index f60520a..ea8ea6b 100644 --- a/tests/gsignal-connect.c +++ b/tests/gsignal-connect.c @@ -132,14 +132,11 @@ } /* - * Incorrect number of arguments in signal handler for signal ‘GObject::notify’. Expected 3 but saw 2. - * (GCallback) object_notify_missing_parameter_cb, NULL); - * ^ - * note: expanded from macro 'g_signal_connect' - * g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, (GConnectFlags) 0) - * ^ + * No error */ { + // As long as object_notify_missing_parameter_cb is using a safe calling + // convention, this is actually fine. GObject *some_object = g_malloc (5); // only checking the type g_signal_connect (some_object, "notify", (GCallback) object_notify_missing_parameter_cb, NULL); @@ -284,3 +281,45 @@ some_thing, NULL, G_CONNECT_SWAPPED | G_CONNECT_AFTER); } + +/* + * No error + */ +{ + GSettings *settings = g_malloc (5); // only checking the type + GApplication *app = g_malloc (5); // only checking the type + g_signal_connect_swapped (app, "activate", + G_CALLBACK (application_activate_swapped_cb), + settings); +} + +/* + * Incorrect number of arguments in signal handler for signal ‘GApplication::activate’. Expected 2 but saw 4. + * G_CALLBACK (application_activate_swapped_excess_arguments_cb), + * ^ + * note: expanded from macro 'G_CALLBACK' + * #define G_CALLBACK(f) ((GCallback) (f)) + * ^ + * note: expanded from macro 'g_signal_connect_swapped' + * g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, G_CONNECT_SWAPPED) + * ^ + */ +{ + GSettings *settings = g_malloc (5); // only checking the type + GApplication *app = g_malloc (5); // only checking the type + g_signal_connect_swapped (app, "activate", + G_CALLBACK (application_activate_swapped_excess_arguments_cb), + settings); +} + +/* + * No error + */ +{ + // A more typical use of G_CONNECT_SWAPPED, where we use an existing + // function and swap the arguments to avoid writing a wrapper function. + GApplication *app = g_malloc (5); // only checking the type + GObject *some_object = g_malloc (5); // only checking the type + g_signal_connect_swapped (app, "activate", + G_CALLBACK (g_object_run_dispose), some_object); +} diff --git a/tests/gsignal.head.c b/tests/gsignal.head.c index f651e4f..ec6e6d0 100644 --- a/tests/gsignal.head.c +++ b/tests/gsignal.head.c @@ -104,6 +104,21 @@ settings_changed_swapped_stream_cb (GInputStream *stream, const gchar *key, /* Something */ } +static void +application_activate_swapped_cb (GSettings *self, GApplication *app) +{ + /* Done */ +} + +static void +application_activate_swapped_excess_arguments_cb (GSettings *self, + const gchar *some_random_arg, + guint another_random_arg, + gpointer user_data) +{ + /* Done */ +} + int main (void) { |