mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-10 03:16:17 +01:00
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:
parent
d5fbbe400a
commit
588af03a28
@ -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
|
||||
|
@ -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__ */
|
||||
|
@ -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__ */
|
||||
|
Loading…
Reference in New Issue
Block a user