[glib] binding: Add a closure-based variant of bind_property_full()



commit 3be3ad61d142ca5bbd5659809af749ea5bf441ac
Author: Emmanuele Bassi <ebassi linux intel com>
Date:   Tue Jul 13 06:03:03 2010 +0100

    binding: Add a closure-based variant of bind_property_full()
    
    Since using the function pointer version muddles the memory management
    requirements of language bindings, we should implement a GClosure-based
    variant on top of g_object_bind_property_full().
    
    https://bugzilla.gnome.org/show_bug.cgi?id=622278

 docs/reference/gobject/gobject-sections.txt |    1 +
 gobject/gbinding.c                          |  183 +++++++++++++++++++++++++--
 gobject/gbinding.h                          |   35 +++--
 gobject/gobject.symbols                     |    1 +
 gobject/tests/binding.c                     |   76 +++++++++++-
 5 files changed, 271 insertions(+), 25 deletions(-)
---
diff --git a/docs/reference/gobject/gobject-sections.txt b/docs/reference/gobject/gobject-sections.txt
index ad5cc4f..d1f4a05 100644
--- a/docs/reference/gobject/gobject-sections.txt
+++ b/docs/reference/gobject/gobject-sections.txt
@@ -867,6 +867,7 @@ g_binding_get_flags
 g_object_bind_property
 GBindingTransformFunc
 g_object_bind_property_full
+g_object_bind_property_with_closures
 <SUBSECTION Standard>
 G_TYPE_BINDING
 G_TYPE_BINDING_FLAGS
diff --git a/gobject/gbinding.c b/gobject/gbinding.c
index f3d773d..b8ccdb9 100644
--- a/gobject/gbinding.c
+++ b/gobject/gbinding.c
@@ -809,12 +809,6 @@ g_object_bind_property_full (gpointer               source,
       return NULL;
     }
 
-  if (transform_to == NULL)
-    transform_to = default_transform_to;
-
-  if (transform_from == NULL)
-    transform_from = default_transform_from;
-
   pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (source), source_property);
   if (pspec == NULL)
     {
@@ -881,9 +875,12 @@ g_object_bind_property_full (gpointer               source,
                           "flags", flags,
                           NULL);
 
-  /* making these properties would be awkward, though not impossible */
-  binding->transform_s2t = transform_to;
-  binding->transform_t2s = transform_from;
+  if (transform_to != NULL)
+    binding->transform_s2t = transform_to;
+
+  if (transform_from != NULL)
+    binding->transform_t2s = transform_from;
+
   binding->transform_data = user_data;
   binding->notify = notify;
 
@@ -951,3 +948,171 @@ g_object_bind_property (gpointer       source,
                                       NULL,
                                       NULL, NULL);
 }
+
+typedef struct _TransformData
+{
+  GClosure *transform_to_closure;
+  GClosure *transform_from_closure;
+} TransformData;
+
+static gboolean
+bind_with_closures_transform_to (GBinding     *binding,
+                                 const GValue *source,
+                                 GValue       *target,
+                                 gpointer      data)
+{
+  TransformData *t_data = data;
+  GValue params[3] = { { 0, }, { 0, }, { 0, } };
+  GValue retval = { 0, };
+  gboolean res;
+
+  g_value_init (&params[0], G_TYPE_BINDING);
+  g_value_set_object (&params[0], binding);
+
+  g_value_init (&params[1], G_TYPE_VALUE);
+  g_value_set_boxed (&params[1], source);
+
+  g_value_init (&params[2], G_TYPE_VALUE);
+  g_value_set_boxed (&params[2], target);
+
+  g_value_init (&retval, G_TYPE_BOOLEAN);
+  g_value_set_boolean (&retval, FALSE);
+
+  g_closure_invoke (t_data->transform_to_closure, &retval, 3, params, NULL);
+
+  res = g_value_get_boolean (&retval);
+  if (res)
+    {
+      const GValue *out_value = g_value_get_boxed (&params[2]);
+
+      g_assert (out_value != NULL);
+
+      g_value_copy (out_value, target);
+    }
+
+  g_value_unset (&params[0]);
+  g_value_unset (&params[1]);
+  g_value_unset (&params[2]);
+  g_value_unset (&retval);
+
+  return res;
+}
+
+static gboolean
+bind_with_closures_transform_from (GBinding     *binding,
+                                   const GValue *source,
+                                   GValue       *target,
+                                   gpointer      data)
+{
+  TransformData *t_data = data;
+  GValue params[3] = { { 0, }, { 0, }, { 0, } };
+  GValue retval = { 0, };
+  gboolean res;
+
+  g_value_init (&params[0], G_TYPE_BINDING);
+  g_value_set_object (&params[0], binding);
+
+  g_value_init (&params[1], G_TYPE_VALUE);
+  g_value_set_boxed (&params[1], source);
+
+  g_value_init (&params[2], G_TYPE_VALUE);
+  g_value_set_boxed (&params[2], target);
+
+  g_value_init (&retval, G_TYPE_BOOLEAN);
+  g_value_set_boolean (&retval, FALSE);
+
+  g_closure_invoke (t_data->transform_from_closure, &retval, 3, params, NULL);
+
+  res = g_value_get_boolean (&retval);
+  if (res)
+    {
+      const GValue *out_value = g_value_get_boxed (&params[2]);
+
+      g_assert (out_value != NULL);
+
+      g_value_copy (out_value, target);
+    }
+
+  g_value_unset (&params[0]);
+  g_value_unset (&params[1]);
+  g_value_unset (&params[2]);
+  g_value_unset (&retval);
+
+  return res;
+}
+
+static void
+bind_with_closures_free_func (gpointer data)
+{
+  TransformData *t_data = data;
+
+  if (t_data->transform_to_closure != NULL)
+    g_closure_unref (t_data->transform_to_closure);
+
+  if (t_data->transform_from_closure != NULL)
+    g_closure_unref (t_data->transform_from_closure);
+
+  g_slice_free (TransformData, t_data);
+}
+
+/**
+ * g_object_bind_property_with_closures:
+ * @source: the source #GObject
+ * @source_property: the property on @source to bind
+ * @target: the target #GObject
+ * @target_property: the property on @target to bind
+ * @flags: flags to pass to #GBinding
+ * @transform_to: a #GClosure wrapping the transformation function
+ *   from the @source to the @target, or %NULL to use the default
+ * @transform_from: a #GClosure wrapping the transformation function
+ *   from the @target to the @source, or %NULL to use the default
+ *
+ * Creates a binding between @source_property on @source and @target_property
+ * on @target, allowing you to set the transformation functions to be used by
+ * the binding.
+ *
+ * This function is the language bindings friendly version of
+ * g_object_bind_property_full(), using #GClosure<!-- -->s instead of
+ * function pointers.
+ *
+ * Rename to: g_object_bind_property_full
+ *
+ * Return value: (transfer none): the #GBinding instance representing the
+ *   binding between the two #GObject instances. The binding is released
+ *   whenever the #GBinding reference count reaches zero.
+ *
+ * Since: 2.26
+ */
+GBinding *
+g_object_bind_property_with_closures (gpointer       source,
+                                      const gchar   *source_property,
+                                      gpointer       target,
+                                      const gchar   *target_property,
+                                      GBindingFlags  flags,
+                                      GClosure      *transform_to,
+                                      GClosure      *transform_from)
+{
+  TransformData *data;
+
+  data = g_slice_new0 (TransformData);
+
+  if (transform_to != NULL)
+    {
+      data->transform_to_closure = g_closure_ref (transform_to);
+      g_closure_sink (data->transform_to_closure);
+    }
+
+  if (transform_from != NULL)
+    {
+      data->transform_from_closure = g_closure_ref (transform_from);
+      g_closure_sink (data->transform_from_closure);
+    }
+
+  return g_object_bind_property_full (source, source_property,
+                                      target, target_property,
+                                      flags,
+                                      transform_to != NULL ? bind_with_closures_transform_to : NULL,
+                                      transform_from != NULL ? bind_with_closures_transform_from : NULL,
+                                      data,
+                                      bind_with_closures_free_func);
+}
diff --git a/gobject/gbinding.h b/gobject/gbinding.h
index 4ea3425..361eef9 100644
--- a/gobject/gbinding.h
+++ b/gobject/gbinding.h
@@ -103,20 +103,27 @@ GObject *             g_binding_get_target          (GBinding *binding);
 G_CONST_RETURN gchar *g_binding_get_source_property (GBinding *binding);
 G_CONST_RETURN gchar *g_binding_get_target_property (GBinding *binding);
 
-GBinding *g_object_bind_property      (gpointer               source,
-                                       const gchar           *source_property,
-                                       gpointer               target,
-                                       const gchar           *target_property,
-                                       GBindingFlags          flags);
-GBinding *g_object_bind_property_full (gpointer               source,
-                                       const gchar           *source_property,
-                                       gpointer               target,
-                                       const gchar           *target_property,
-                                       GBindingFlags          flags,
-                                       GBindingTransformFunc  transform_to,
-                                       GBindingTransformFunc  transform_from,
-                                       gpointer               user_data,
-                                       GDestroyNotify         notify);
+GBinding *g_object_bind_property               (gpointer               source,
+                                                const gchar           *source_property,
+                                                gpointer               target,
+                                                const gchar           *target_property,
+                                                GBindingFlags          flags);
+GBinding *g_object_bind_property_full          (gpointer               source,
+                                                const gchar           *source_property,
+                                                gpointer               target,
+                                                const gchar           *target_property,
+                                                GBindingFlags          flags,
+                                                GBindingTransformFunc  transform_to,
+                                                GBindingTransformFunc  transform_from,
+                                                gpointer               user_data,
+                                                GDestroyNotify         notify);
+GBinding *g_object_bind_property_with_closures (gpointer               source,
+                                                const gchar           *source_property,
+                                                gpointer               target,
+                                                const gchar           *target_property,
+                                                GBindingFlags          flags,
+                                                GClosure              *transform_to,
+                                                GClosure              *transform_from);
 
 G_END_DECLS
 
diff --git a/gobject/gobject.symbols b/gobject/gobject.symbols
index b2cd130..0eca5e7 100644
--- a/gobject/gobject.symbols
+++ b/gobject/gobject.symbols
@@ -21,6 +21,7 @@ g_binding_get_source_property
 g_binding_get_target_property
 g_object_bind_property
 g_object_bind_property_full
+g_object_bind_property_with_closures
 #endif
 #endif
 
diff --git a/gobject/tests/binding.c b/gobject/tests/binding.c
index 1750e2a..710776b 100644
--- a/gobject/tests/binding.c
+++ b/gobject/tests/binding.c
@@ -247,8 +247,8 @@ binding_default (void)
                                     target, "bar",
                                     G_BINDING_DEFAULT);
 
-  g_assert (g_binding_get_source (binding) == G_OBJECT (source));
-  g_assert (g_binding_get_target (binding) == G_OBJECT (target));
+  g_assert ((BindingSource *) g_binding_get_source (binding) == source);
+  g_assert ((BindingTarget *) g_binding_get_target (binding) == target);
   g_assert_cmpstr (g_binding_get_source_property (binding), ==, "foo");
   g_assert_cmpstr (g_binding_get_target_property (binding), ==, "bar");
   g_assert_cmpint (g_binding_get_flags (binding), ==, G_BINDING_DEFAULT);
@@ -330,6 +330,77 @@ binding_transform (void)
 }
 
 static void
+binding_transform_marshal (GClosure     *closure,
+                           GValue       *return_value,
+                           guint         n_param_values,
+                           const GValue *param_values,
+                           gpointer      invocation_hint G_GNUC_UNUSED,
+                           gpointer      marshal_data)
+{
+  typedef gboolean (* GMarshalFunc_BOOLEAN__VALUE_VALUE) (gpointer data1,
+                                                          gpointer arg_2,
+                                                          gpointer arg_3,
+                                                          gpointer data2);
+  register GMarshalFunc_BOOLEAN__VALUE_VALUE callback;
+  register GCClosure *cc = (GCClosure *) closure;
+  register gpointer data1, data2;
+  gboolean v_return;
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+
+  callback = (GMarshalFunc_BOOLEAN__VALUE_VALUE) (marshal_data ? marshal_data : cc->callback);
+  v_return = callback (data1,
+                       g_value_get_boxed (param_values + 1),
+                       g_value_get_boxed (param_values + 2),
+                       data2);
+
+  g_value_set_boolean (return_value, v_return);
+}
+
+static void
+binding_transform_closure (void)
+{
+  BindingSource *source = g_object_new (binding_source_get_type (), NULL);
+  BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
+  GBinding *binding;
+  gboolean unused_data_1 = FALSE, unused_data_2 = FALSE;
+  GClosure *c2f_clos, *f2c_clos;
+
+  c2f_clos = g_cclosure_new (G_CALLBACK (celsius_to_fahrenheit), &unused_data_1, (GClosureNotify) data_free);
+  g_closure_set_marshal (c2f_clos, binding_transform_marshal);
+
+  f2c_clos = g_cclosure_new (G_CALLBACK (fahrenheit_to_celsius), &unused_data_2, (GClosureNotify) data_free);
+  g_closure_set_marshal (f2c_clos, binding_transform_marshal);
+
+  binding = g_object_bind_property_with_closures (source, "value",
+                                                  target, "value",
+                                                  G_BINDING_BIDIRECTIONAL,
+                                                  c2f_clos,
+                                                  f2c_clos);
+
+  g_object_set (source, "value", 24.0, NULL);
+  g_assert_cmpfloat (target->value, ==, ((9 * 24.0 / 5) + 32.0));
+
+  g_object_set (target, "value", 69.0, NULL);
+  g_assert_cmpfloat (source->value, ==, (5 * (69.0 - 32.0) / 9));
+
+  g_object_unref (source);
+  g_object_unref (target);
+
+  g_assert (unused_data_1);
+  g_assert (unused_data_2);
+}
+
+static void
 binding_chain (void)
 {
   BindingSource *a = g_object_new (binding_source_get_type (), NULL);
@@ -407,6 +478,7 @@ main (int argc, char *argv[])
   g_test_add_func ("/binding/default", binding_default);
   g_test_add_func ("/binding/bidirectional", binding_bidirectional);
   g_test_add_func ("/binding/transform", binding_transform);
+  g_test_add_func ("/binding/transform-closure", binding_transform_closure);
   g_test_add_func ("/binding/chain", binding_chain);
   g_test_add_func ("/binding/sync-create", binding_sync_create);
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]