Add optional support for varargs marshallers to GClosure

These closures support being invoked on a va_args which can
be useful as you can then avoid boxing the va_args into
GValues in certain cases.

https://bugzilla.gnome.org/show_bug.cgi?id=661140
This commit is contained in:
Alexander Larsson 2012-02-22 19:29:14 +01:00
parent d5fbbe400a
commit 588af03a28
3 changed files with 392 additions and 3 deletions

View File

@ -288,6 +288,24 @@ closure_invoke_notifiers (GClosure *closure,
}
}
static void
g_closure_set_meta_va_marshal (GClosure *closure,
GVaClosureMarshal va_meta_marshal)
{
GRealClosure *real_closure;
g_return_if_fail (closure != NULL);
g_return_if_fail (va_meta_marshal != NULL);
g_return_if_fail (closure->is_invalid == FALSE);
g_return_if_fail (closure->in_marshal == FALSE);
real_closure = G_REAL_CLOSURE (closure);
g_return_if_fail (real_closure->meta_marshal != NULL);
real_closure->va_meta_marshal = va_meta_marshal;
}
/**
* g_closure_set_meta_marshal: (skip)
* @closure: a #GClosure
@ -768,6 +786,70 @@ g_closure_invoke (GClosure *closure,
g_closure_unref (closure);
}
gboolean
_g_closure_supports_invoke_va (GClosure *closure)
{
GRealClosure *real_closure;
g_return_val_if_fail (closure != NULL, FALSE);
real_closure = G_REAL_CLOSURE (closure);
return
real_closure->va_marshal != NULL &&
(real_closure->meta_marshal == NULL ||
real_closure->va_meta_marshal != NULL);
}
void
_g_closure_invoke_va (GClosure *closure,
GValue /*out*/ *return_value,
gpointer instance,
va_list args,
int n_params,
GType *param_types)
{
GRealClosure *real_closure;
g_return_if_fail (closure != NULL);
real_closure = G_REAL_CLOSURE (closure);
g_closure_ref (closure); /* preserve floating flag */
if (!closure->is_invalid)
{
GVaClosureMarshal marshal;
gpointer marshal_data;
gboolean in_marshal = closure->in_marshal;
g_return_if_fail (closure->marshal || real_closure->meta_marshal);
SET (closure, in_marshal, TRUE);
if (real_closure->va_meta_marshal)
{
marshal_data = real_closure->meta_marshal_data;
marshal = real_closure->va_meta_marshal;
}
else
{
marshal_data = NULL;
marshal = real_closure->va_marshal;
}
if (!in_marshal)
closure_invoke_notifiers (closure, PRE_NOTIFY);
marshal (closure,
return_value,
instance, args,
marshal_data,
n_params, param_types);
if (!in_marshal)
closure_invoke_notifiers (closure, POST_NOTIFY);
SET (closure, in_marshal, in_marshal);
}
g_closure_unref (closure);
}
/**
* g_closure_set_marshal: (skip)
* @closure: a #GClosure
@ -794,6 +876,24 @@ g_closure_set_marshal (GClosure *closure,
closure->marshal = marshal;
}
void
_g_closure_set_va_marshal (GClosure *closure,
GVaClosureMarshal marshal)
{
GRealClosure *real_closure;
g_return_if_fail (closure != NULL);
g_return_if_fail (marshal != NULL);
real_closure = G_REAL_CLOSURE (closure);
if (real_closure->va_marshal && real_closure->va_marshal != marshal)
g_warning ("attempt to override closure->va_marshal (%p) with new marshal (%p)",
real_closure->va_marshal, marshal);
else
real_closure->va_marshal = marshal;
}
/**
* g_cclosure_new: (skip)
* @callback_func: the function to invoke
@ -874,6 +974,34 @@ g_type_class_meta_marshal (GClosure *closure,
callback);
}
static void
g_type_class_meta_marshalv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types)
{
GRealClosure *real_closure;
GTypeClass *class;
gpointer callback;
/* GType itype = (GType) closure->data; */
guint offset = GPOINTER_TO_UINT (marshal_data);
real_closure = G_REAL_CLOSURE (closure);
class = G_TYPE_INSTANCE_GET_CLASS (instance, itype, GTypeClass);
callback = G_STRUCT_MEMBER (gpointer, class, offset);
if (callback)
real_closure->va_marshal (closure,
return_value,
instance, args,
callback,
n_params,
param_types);
}
static void
g_type_iface_meta_marshal (GClosure *closure,
GValue /*out*/ *return_value,
@ -897,6 +1025,34 @@ g_type_iface_meta_marshal (GClosure *closure,
callback);
}
static void
g_type_iface_meta_marshalv (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types)
{
GRealClosure *real_closure;
GTypeClass *class;
gpointer callback;
GType itype = (GType) closure->data;
guint offset = GPOINTER_TO_UINT (marshal_data);
real_closure = G_REAL_CLOSURE (closure);
class = G_TYPE_INSTANCE_GET_INTERFACE (instance, itype, GTypeClass);
callback = G_STRUCT_MEMBER (gpointer, class, offset);
if (callback)
real_closure->va_marshal (closure,
return_value,
instance, args,
callback,
n_params,
param_types);
}
/**
* g_signal_type_cclosure_new:
* @itype: the #GType identifier of an interface or classed type
@ -920,10 +1076,15 @@ g_signal_type_cclosure_new (GType itype,
closure = g_closure_new_simple (sizeof (GClosure), (gpointer) itype);
if (G_TYPE_IS_INTERFACE (itype))
g_closure_set_meta_marshal (closure, GUINT_TO_POINTER (struct_offset), g_type_iface_meta_marshal);
{
g_closure_set_meta_marshal (closure, GUINT_TO_POINTER (struct_offset), g_type_iface_meta_marshal);
g_closure_set_meta_va_marshal (closure, g_type_iface_meta_marshalv);
}
else
g_closure_set_meta_marshal (closure, GUINT_TO_POINTER (struct_offset), g_type_class_meta_marshal);
{
g_closure_set_meta_marshal (closure, GUINT_TO_POINTER (struct_offset), g_type_class_meta_marshal);
g_closure_set_meta_va_marshal (closure, g_type_class_meta_marshalv);
}
return closure;
}
@ -1081,6 +1242,86 @@ value_from_ffi_type (GValue *gvalue, gpointer *value)
}
}
typedef union {
gpointer _gpointer;
float _float;
double _double;
gint _gint;
guint _guint;
glong _glong;
gulong _gulong;
gint64 _gint64;
guint64 _guint64;
} va_arg_storage;
static ffi_type *
va_to_ffi_type (GType gtype,
va_list *va,
va_arg_storage *storage)
{
ffi_type *rettype = NULL;
GType type = g_type_fundamental (gtype);
g_assert (type != G_TYPE_INVALID);
switch (type)
{
case G_TYPE_BOOLEAN:
case G_TYPE_CHAR:
case G_TYPE_INT:
case G_TYPE_ENUM:
rettype = &ffi_type_sint;
storage->_gint = va_arg (*va, gint);
break;
case G_TYPE_UCHAR:
case G_TYPE_UINT:
case G_TYPE_FLAGS:
rettype = &ffi_type_uint;
storage->_guint = va_arg (*va, guint);
break;
case G_TYPE_STRING:
case G_TYPE_OBJECT:
case G_TYPE_BOXED:
case G_TYPE_PARAM:
case G_TYPE_POINTER:
case G_TYPE_INTERFACE:
case G_TYPE_VARIANT:
rettype = &ffi_type_pointer;
storage->_gpointer = va_arg (*va, gpointer);
break;
case G_TYPE_FLOAT:
/* Float args are passed as doubles in varargs */
rettype = &ffi_type_float;
storage->_float = (float)va_arg (*va, double);
break;
case G_TYPE_DOUBLE:
rettype = &ffi_type_double;
storage->_double = va_arg (*va, double);
break;
case G_TYPE_LONG:
rettype = &ffi_type_slong;
storage->_glong = va_arg (*va, glong);
break;
case G_TYPE_ULONG:
rettype = &ffi_type_ulong;
storage->_gulong = va_arg (*va, gulong);
break;
case G_TYPE_INT64:
rettype = &ffi_type_sint64;
storage->_gint64 = va_arg (*va, gint64);
break;
case G_TYPE_UINT64:
rettype = &ffi_type_uint64;
storage->_guint64 = va_arg (*va, guint64);
break;
default:
rettype = &ffi_type_pointer;
storage->_guint64 = 0;
g_warning ("va_to_ffi_type: Unsupported fundamental type: %s", g_type_name (type));
break;
}
return rettype;
}
/**
* g_cclosure_marshal_generic:
* @closure: A #GClosure.
@ -1177,6 +1418,123 @@ g_cclosure_marshal_generic (GClosure *closure,
value_from_ffi_type (return_gvalue, rvalue);
}
void
g_cclosure_marshal_generic_va (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args_list,
gpointer marshal_data,
int n_params,
GType *param_types)
{
ffi_type *rtype;
void *rvalue;
int n_args;
ffi_type **atypes;
void **args;
va_arg_storage *storage;
int i;
ffi_cif cif;
GCClosure *cc = (GCClosure*) closure;
gint *enum_tmpval;
gboolean tmpval_used = FALSE;
va_list args_copy;
enum_tmpval = g_alloca (sizeof (gint));
if (return_value && G_VALUE_TYPE (return_value))
{
rtype = value_to_ffi_type (return_value, &rvalue, enum_tmpval, &tmpval_used);
}
else
{
rtype = &ffi_type_void;
}
rvalue = g_alloca (MAX (rtype->size, sizeof (ffi_arg)));
n_args = n_params + 2;
atypes = g_alloca (sizeof (ffi_type *) * n_args);
args = g_alloca (sizeof (gpointer) * n_args);
storage = g_alloca (sizeof (va_arg_storage) * n_params);
if (tmpval_used)
enum_tmpval = g_alloca (sizeof (gint));
if (G_CCLOSURE_SWAP_DATA (closure))
{
atypes[n_args-1] = &ffi_type_pointer;
args[n_args-1] = &instance;
atypes[0] = &ffi_type_pointer;
args[0] = &closure->data;
}
else
{
atypes[0] = &ffi_type_pointer;
args[0] = &instance;
atypes[n_args-1] = &ffi_type_pointer;
args[n_args-1] = &closure->data;
}
va_copy (args_copy, args_list);
/* Box non-primitive arguments */
for (i = 0; i < n_params; i++)
{
GType type = param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE;
GType fundamental = G_TYPE_FUNDAMENTAL (type);
atypes[i+1] = va_to_ffi_type (type,
&args_copy,
&storage[i]);
args[i+1] = &storage[i];
if ((param_types[i] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0)
{
if (fundamental == G_TYPE_STRING && storage[i]._gpointer != NULL)
storage[i]._gpointer = g_strdup (storage[i]._gpointer);
else if (fundamental == G_TYPE_PARAM && storage[i]._gpointer != NULL)
storage[i]._gpointer = g_param_spec_ref (storage[i]._gpointer);
else if (fundamental == G_TYPE_BOXED && storage[i]._gpointer != NULL)
storage[i]._gpointer = g_boxed_copy (type, storage[i]._gpointer);
else if (fundamental == G_TYPE_VARIANT && storage[i]._gpointer != NULL)
storage[i]._gpointer = g_variant_ref_sink (storage[i]._gpointer);
}
if (fundamental == G_TYPE_OBJECT && storage[i]._gpointer != NULL)
storage[i]._gpointer = g_object_ref (storage[i]._gpointer);
}
va_end (args_copy);
if (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, n_args, rtype, atypes) != FFI_OK)
return;
ffi_call (&cif, marshal_data ? marshal_data : cc->callback, rvalue, args);
/* Unbox non-primitive arguments */
for (i = 0; i < n_params; i++)
{
GType type = param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE;
GType fundamental = G_TYPE_FUNDAMENTAL (type);
if ((param_types[i] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0)
{
if (fundamental == G_TYPE_STRING && storage[i]._gpointer != NULL)
g_free (storage[i]._gpointer);
else if (fundamental == G_TYPE_PARAM && storage[i]._gpointer != NULL)
g_param_spec_unref (storage[i]._gpointer);
else if (fundamental == G_TYPE_BOXED && storage[i]._gpointer != NULL)
g_boxed_free (type, storage[i]._gpointer);
else if (fundamental == G_TYPE_VARIANT && storage[i]._gpointer != NULL)
g_variant_unref (storage[i]._gpointer);
}
if (fundamental == G_TYPE_OBJECT && storage[i]._gpointer != NULL)
g_object_unref (storage[i]._gpointer);
}
if (return_value && G_VALUE_TYPE (return_value))
value_from_ffi_type (return_value, rvalue);
}
/**
* g_cclosure_marshal_VOID__VOID:
* @closure: the #GClosure to which the marshaller belongs

View File

@ -120,6 +120,15 @@ typedef void (*GClosureMarshal) (GClosure *closure,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data);
typedef void (* GVaClosureMarshal) (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args,
gpointer marshal_data,
int n_params,
GType *param_types);
/**
* GCClosure:
* @closure: the #GClosure
@ -258,6 +267,15 @@ void g_cclosure_marshal_generic (GClosure *closure,
gpointer invocation_hint,
gpointer marshal_data);
void g_cclosure_marshal_generic_va (GClosure *closure,
GValue *return_value,
gpointer instance,
va_list args_list,
gpointer marshal_data,
int n_params,
GType *param_types);
G_END_DECLS
#endif /* __G_CLOSURE_H__ */

View File

@ -33,6 +33,8 @@ struct _GRealClosure
{
GClosureMarshal meta_marshal;
gpointer meta_marshal_data;
GVaClosureMarshal va_meta_marshal;
GVaClosureMarshal va_marshal;
GClosure closure;
};
@ -58,6 +60,17 @@ void _g_type_boxed_init (GType type,
GBoxedCopyFunc copy_func,
GBoxedFreeFunc free_func);
gboolean _g_closure_supports_invoke_va (GClosure *closure);
void _g_closure_set_va_marshal (GClosure *closure,
GVaClosureMarshal marshal);
void _g_closure_invoke_va (GClosure *closure,
GValue /*out*/ *return_value,
gpointer instance,
va_list args,
int n_params,
GType *param_types);
G_END_DECLS
#endif /* __G_TYPE_PRIVATE_H__ */