[glib/wip/otte/interface-types: 2/2] value: Allow automatic transforms to/from interfaces



commit 2c75535b6527abd122a993ee0db03dba262c4b6c
Author: Benjamin Otte <otte redhat com>
Date:   Mon Nov 25 19:58:59 2019 +0100

    value: Allow automatic transforms to/from interfaces
    
    Use the new g_type_interface_instantiable_prerequisite() to check
    compatibility for transform functions.
    
    In particular, this allows interfaces (in my case GDK_TYPE_PAINTABLE) to
    be transformed to/from any GObject type (in my case G_TYPE_OBJECT) using
    the transform function registered to transform between any 2 objects
    (g_value_object_transform_value() does a type check and uses NULL if the
    types don't match).
    
    And this in turn allows be to g_object_bind_property() a gobject-typed
    generic property (GtkListItem::item) to a GtkImage::paintable.

 gobject/gvalue.c      | 13 +++++--
 gobject/tests/value.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 104 insertions(+), 2 deletions(-)
---
diff --git a/gobject/gvalue.c b/gobject/gvalue.c
index c30501a6b..a8ba2f33b 100644
--- a/gobject/gvalue.c
+++ b/gobject/gvalue.c
@@ -448,6 +448,15 @@ g_value_init_from_instance (GValue  *value,
     }
 }
 
+static GType
+transform_lookup_get_parent_type (GType type)
+{
+  if (g_type_fundamental (type) == G_TYPE_INTERFACE)
+    return g_type_interface_instantiable_prerequisite (type);
+
+  return g_type_parent (type);
+}
+
 static GValueTransform
 transform_func_lookup (GType src_type,
                       GType dest_type)
@@ -470,11 +479,11 @@ transform_func_lookup (GType src_type,
                  g_type_value_table_peek (entry.src_type) == g_type_value_table_peek (src_type))
                return e->func;
            }
-         entry.dest_type = g_type_parent (entry.dest_type);
+         entry.dest_type = transform_lookup_get_parent_type (entry.dest_type);
        }
       while (entry.dest_type);
       
-      entry.src_type = g_type_parent (entry.src_type);
+      entry.src_type = transform_lookup_get_parent_type (entry.src_type);
     }
   while (entry.src_type);
 
diff --git a/gobject/tests/value.c b/gobject/tests/value.c
index b5a136486..2a7c63c0e 100644
--- a/gobject/tests/value.c
+++ b/gobject/tests/value.c
@@ -83,6 +83,98 @@ test_valuearray_basic (void)
   g_value_array_free (a2);
 }
 
+/* We create some dummy objects with this relationship:
+ *
+ *               GObject           TestInterface
+ *              /       \         /  /
+ *     TestObjectA     TestObjectB  /
+ *      /       \                  /
+ * TestObjectA1 TestObjectA2-------   
+ *
+ * ie: TestObjectB is a subclass of TestObjectA and TestObjectC is
+ * related to neither.
+ */
+
+typedef GTypeInterface TestInterfaceInterface;
+static GType test_interface_get_type (void);
+G_DEFINE_INTERFACE (TestInterface, test_interface, G_TYPE_OBJECT)
+static void test_interface_default_init (TestInterfaceInterface *iface) { }
+
+static GType test_object_a_get_type (void);
+typedef GObject TestObjectA; typedef GObjectClass TestObjectAClass;
+G_DEFINE_TYPE (TestObjectA, test_object_a, G_TYPE_OBJECT)
+static void test_object_a_class_init (TestObjectAClass *class) { }
+static void test_object_a_init (TestObjectA *a) { }
+
+static GType test_object_b_get_type (void);
+typedef GObject TestObjectB; typedef GObjectClass TestObjectBClass;
+static void test_object_b_iface_init (TestInterfaceInterface *iface) { }
+G_DEFINE_TYPE_WITH_CODE (TestObjectB, test_object_b, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (test_interface_get_type (), test_object_b_iface_init))
+static void test_object_b_class_init (TestObjectBClass *class) { }
+static void test_object_b_init (TestObjectB *b) { }
+
+static GType test_object_a1_get_type (void);
+typedef GObject TestObjectA1; typedef GObjectClass TestObjectA1Class;
+G_DEFINE_TYPE (TestObjectA1, test_object_a1, G_TYPE_OBJECT)
+static void test_object_a1_class_init (TestObjectA1Class *class) { }
+static void test_object_a1_init (TestObjectA1 *c) { }
+
+static GType test_object_a2_get_type (void);
+typedef GObject TestObjectA2; typedef GObjectClass TestObjectA2Class;
+static void test_object_a2_iface_init (TestInterfaceInterface *iface) { }
+G_DEFINE_TYPE_WITH_CODE (TestObjectA2, test_object_a2, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (test_interface_get_type (), test_object_a2_iface_init))
+static void test_object_a2_class_init (TestObjectA2Class *class) { }
+static void test_object_a2_init (TestObjectA2 *b) { }
+
+static void
+test_value_transform_object (void)
+{
+  GValue src = G_VALUE_INIT;
+  GValue dest = G_VALUE_INIT;
+  GObject *object;
+  guint i, s, d;
+  GType types[] = {
+    G_TYPE_OBJECT,
+    test_interface_get_type (),
+    test_object_a_get_type (),
+    test_object_b_get_type (),
+    test_object_a1_get_type (),
+    test_object_a2_get_type ()
+  };
+
+  for (i = 0; i < G_N_ELEMENTS (types); i++)
+    {
+      if (!G_TYPE_IS_CLASSED (types[i]))
+        continue;
+
+      object = g_object_new (types[i], NULL);
+
+      for (s = 0; s < G_N_ELEMENTS (types); s++)
+        {
+          if (!G_TYPE_CHECK_INSTANCE_TYPE (object, types[s]))
+            continue;
+
+          g_value_init (&src, types[s]);
+          g_value_set_object (&src, object);
+
+          for (d = 0; d < G_N_ELEMENTS (types); d++)
+            {
+              g_test_message ("Next: %s object in GValue of %s to GValue of %s", g_type_name (types[i]), 
g_type_name (types[s]), g_type_name (types[d]));
+              g_assert (g_value_type_transformable (types[s], types[d]));
+              g_value_init (&dest, types[d]);
+              g_assert (g_value_transform (&src, &dest));
+              g_assert_cmpint (g_value_get_object (&dest) != NULL, ==, G_TYPE_CHECK_INSTANCE_TYPE (object, 
types[d]));
+              g_value_unset (&dest);
+            }
+          g_value_unset (&src);
+        }
+
+      g_object_unref (object);
+    }
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -90,6 +182,7 @@ main (int argc, char *argv[])
 
   g_test_add_func ("/value/basic", test_value_basic);
   g_test_add_func ("/value/array/basic", test_valuearray_basic);
+  g_test_add_func ("/value/transform-object", test_value_transform_object);
 
   return g_test_run ();
 }


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