Re: GSignal convenience functions
- From: Tim Janik <timj gtk org>
- To: James Henstridge <james daa com au>
- Cc: Gtk+ Developers <gtk-devel-list gnome org>
- Subject: Re: GSignal convenience functions
- Date: Mon, 11 Dec 2000 11:38:11 +0100 (CET)
On Thu, 7 Dec 2000, James Henstridge wrote:
> I have done a little testing of these functions, and they seem to work
> well. One bit of ugly code is putting the instance variable in the GValue
> array. At the moment, I misuse the collect_value function for the type
> (if it is available) to set the value, or just set it as a G_TYPE_POINTER
> value. This could probably be cleaned up a bit more.
i have those functions already (or equivalents at least), though i originally
intended to do things like hovoc, i.e. implement g_object_emit_signal().
after some initial starring, i adopted your idea of "misusing" collect_value
though, and in lack of set_as_pointer() i went for a more general implementation
of that as g_value_set_instance() (my function is a bit more complicated though,
to preserve collect_value() constrains). the G_TYPE_POINTER hack is not viable
though, we need to hold reference counts across signal emissions.
FYI, here's the implementation:
void
g_value_set_instance (GValue *value,
gpointer instance)
{
g_return_if_fail (G_IS_VALUE (value));
g_value_reset (value);
if (instance)
{
GType g_type = G_VALUE_TYPE (value);
GTypeValueTable *value_table = g_type_value_table_peek (g_type);
GTypeCValue cvalue = { 0, };
guint nth_value = 0;
guint collect_type = value_table->collect_type;
gchar *error_msg;
g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
g_return_if_fail (g_type_is_a (G_TYPE_FROM_INSTANCE (instance), G_VALUE_TYPE (value)));
g_return_if_fail (value_table->collect_type == G_VALUE_COLLECT_POINTER);
cvalue.v_pointer = instance;
error_msg = value_table->collect_value (value, nth_value++, &collect_type, &cvalue);
/* this shouldn't be triggered, instance types should collect just one pointer,
* but since we have to follow the calling conventions for collect_value(),
* we can attempt to feed them with 0s if they insist on extra args.
*/
while (collect_type && !error_msg)
{
memset (&cvalue, 0, sizeof (cvalue));
error_msg = value_table->collect_value (value, nth_value++, &collect_type, &cvalue);
}
if (error_msg)
{
g_warning ("%s: %s", G_STRLOC, error_msg);
g_free (error_msg);
/* we purposely leak the value here, it might not be
* in a sane state if an error condition occoured
*/
memset (value, 0, sizeof (*value));
value->g_type = g_type;
value_table->value_init (value);
}
}
}
> We may want to change the name of g_signal_connect_cclosure
> though. Alternatively, with a few #defines, we could provide APIs more
> similar to gtk_signal_connect, _after, _object and _object_after in terms
> of connect_cclosure.
hm, your g_signal_connect_cclosure() doesn't actually take a closure
argument, so the name is indeed a bit odd. as for your emit functions,
what we basically needed was:
void g_signal_emit_valist (gpointer instance,
guint signal_id,
GQuark detail,
va_list var_args);
> There are probably a few problems with this patch, but it is probably a
> good start for these functions.
on the implementation:
> +guint
> +g_signal_new (const gchar *signal_name,
> + GType itype,
> + GSignalFlags signal_flags,
> + guint function_offset,
> + GSignalAccumulator accumulator,
> + GSignalCMarshaller c_marshaller,
> + GType return_type,
> + guint n_params,
> + ...)
> +{
[...]
> +}
this is basically what i have, though g_signal_new() currently
takes a closure:
guint
g_signal_new (const gchar *signal_name,
GType itype,
GSignalFlags signal_flags,
GClosure *class_closure,
GSignalAccumulator accumulator,
GSignalCMarshaller c_marshaller,
GType return_type,
guint n_params,
...);
the class_closure argument will probably vanish when i commit the neccessary
bits to allow LB chaining of class signal handlers (because a signal can
then have multiple class_closures).
> +guint
> +g_signal_connect_cclosure (gpointer instance,
> + gchar *signal,
> + GQuark detail,
if you take the signal as a string, the detail is encoded in that.
> + GCallback func,
> + gpointer user_data,
> + gboolean after,
> + gboolean swap)
> +static gboolean
> +g_signal_collect_params (GValue *instance_and_params,
> + guint n_params,
> + GType itype,
> + const GType *param_types,
> + gpointer instance,
> + va_list var_args)
> +{
> + GTypeValueTable *value_table;
> + register GValue *last_param;
> + register gboolean failed = FALSE;
> +
> + /* set instance member. Misuse the collect_value method if
> + * available. Otherwise, just use G_TYPE_POINTER. */
> + value_table = g_type_value_table_peek (itype);
> + if (value_table->collect_value)
> + {
> + GType collect_type;
> + GTypeCValue collect_value;
> + gchar *error;
> +
> + g_value_init (instance_and_params, itype);
> + collect_value.v_pointer = instance;
> + error = (value_table->collect_value) (instance_and_params, 0,
> + &collect_type, &collect_value);
> + if (error)
> + {
> + g_warning("g_signal_collect_params(): %s", error);
> + g_free(error);
> + failed = TRUE;
> + }
> + }
> + else
> + {
> + /* not perfect, but will work with the generated marshalers */
> + g_value_init (instance_and_params, G_TYPE_POINTER);
> + g_value_set_pointer (instance_and_params, instance);
> + }
> +
> + instance_and_params++;
> + for (last_param = instance_and_params + n_params;
> + instance_and_params < last_param; instance_and_params++)
> + {
> + gchar *error;
> +
> + g_value_init (instance_and_params, *(param_types++));
> + G_VALUE_COLLECT (instance_and_params, var_args, &error);
> + if (error)
> + {
> + failed = TRUE;
> + g_warning ("g_signal_collect_params(): %s", error);
> + g_free (error);
here, you need to abort the loop, since if collection of a var args list
failed somewhere in the middle, you cannot rely on the rest being
collectable (at least not without crashes ;)
also, you're leaking the correctly collected values if you abort.
> + }
> + }
> +
> + return failed;
> +}
> +
> +void
> +g_signal_emit (gpointer instance,
> + guint signal_id,
> + GQuark detail,
> + ...)
> +{
> + GValue *instance_and_params;
> + GValue return_value = { 0, };
> + GSignalQuery query;
> + gboolean abort = FALSE;
> + va_list var_args;
> + guint i;
> +
> + g_signal_query (signal_id, &query);
> + g_return_if_fail (query.signal_id != 0);
> +
> + instance_and_params = g_new0 (GValue, query.n_params + 1);
> +
> + va_start (var_args, detail);
> + abort = g_signal_collect_params (instance_and_params,
> + query.n_params,
> + query.itype,
> + query.param_types,
> + instance,
> + var_args);
> + if (!abort)
> + {
> + if (query.return_type != G_TYPE_NONE)
> + g_value_init (&return_value, query.return_type);
> +
> + g_signal_emitv (instance_and_params, query.signal_id,
> + detail, &return_value);
> +
> + if (query.return_type != G_TYPE_NONE)
> + {
> + gchar *error;
> + G_VALUE_LCOPY (&return_value, var_args, &error);
gtk_signal_emit() has a certain "feature" that needs to be preserved
here, that is, if gtk_signal_emit (obj, &ret_val); did not result
in any handlers being called, ret_val's contents are left untouched.
granted, you couldn't apropriately conditionalize G_VALUE_LCOPY() without
modifying signal_emit_R().
> +void
> +g_signal_emit_by_name (gpointer instance,
> + gchar *name,
> + GQuark detail,
> + ...)
> +{
[...]
> +}
as with connect, the detail is encoded in the signal name here, and if
you had implemented emit_valist(), you could have avoided quite some code
duplication here ;)
in general, you could (should) have also used internal gsignal.c functions,
e.g. looking up the signal ids etc, but then again, you're of course not
as familiar with that code as i am ;)
thanks for taking a stab at this though, we probably need more convenience
functions on top of what i have now, and for those i'd actually like to
see patches (they are much easier to implement than monsters like emit_valist,
some maybe even via plain #defines).
to avoid duplication and to let you look for more required convenience,
here's my new API (i'll commit later this day after i compiled the
ChangeLog entries):
guint g_signal_new (const gchar *signal_name,
GType itype,
GSignalFlags signal_flags,
GClosure *class_closure,
GSignalAccumulator accumulator,
GSignalCMarshaller c_marshaller,
GType return_type,
guint n_params,
...);
guint g_signal_newv (const gchar *signal_name,
GType itype,
GSignalFlags signal_flags,
GClosure *class_closure,
GSignalAccumulator accumulator,
GSignalCMarshaller c_marshaller,
GType return_type,
guint n_params,
GType *param_types);
void g_signal_emitv (const GValue *instance_and_params,
guint signal_id,
GQuark detail,
GValue *return_value);
void g_signal_emit_valist (gpointer instance,
guint signal_id,
GQuark detail,
va_list var_args);
void g_signal_emit (gpointer instance,
guint signal_id,
GQuark detail,
...);
void g_signal_emit_by_name (gpointer instance,
const gchar *detailed_signal,
...);
guint g_signal_connect_closure_by_id (gpointer instance,
guint signal_id,
GQuark detail,
GClosure *closure,
gboolean after);
guint g_signal_connect_closure (gpointer instance,
const gchar *detailed_signal,
GClosure *closure,
gboolean after);
guint g_signal_connect_data (gpointer instance,
const gchar *detailed_signal,
GCallback c_handler,
gpointer data,
GClosureNotify destroy_data,
gboolean swapped,
gboolean after);
guint g_signal_connect_object (gpointer instance,
const gchar *detailed_signal,
GCallback c_handler,
gpointer gobject,
gboolean swapped,
gboolean after);
g_signal_connect_object() actually resides in gobject.h as it'll only
work with gobject arguments of type GObject* (it has while_alive
semantics out of the box).
>
> James.
>
---
ciaoTJ
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]