[glib] gobject: Add install_properties()



commit 9cd43d7a4c5d3a50187c2eaba7ab903cf6456d7d
Author: Emmanuele Bassi <ebassi linux intel com>
Date:   Wed Aug 18 15:32:27 2010 +0100

    gobject: Add install_properties()
    
    Since we added g_object_notify_by_pspec(), an efficient way to install
    and notify properties relies on storing the GParamSpec pointers inside
    a static arrays, like we do for signal identifiers.
    
    Instead of multiple calls to g_object_class_install_property(), we
    should have a single function to take the static array of GParamSpecs
    and iterate it.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=626919
    
    Signed-off-by: Emmanuele Bassi <ebassi linux intel com>

 docs/reference/gobject/gobject-sections.txt |    1 +
 gobject/gobject.c                           |  121 ++++++++++++++-
 gobject/gobject.h                           |    3 +
 gobject/gobject.symbols                     |    1 +
 gobject/tests/.gitignore                    |    1 +
 gobject/tests/Makefile.am                   |    4 +-
 gobject/tests/properties.c                  |  222 +++++++++++++++++++++++++++
 7 files changed, 350 insertions(+), 3 deletions(-)
---
diff --git a/docs/reference/gobject/gobject-sections.txt b/docs/reference/gobject/gobject-sections.txt
index 22230bb..17ba0df 100644
--- a/docs/reference/gobject/gobject-sections.txt
+++ b/docs/reference/gobject/gobject-sections.txt
@@ -244,6 +244,7 @@ G_OBJECT_TYPE_NAME
 G_OBJECT_CLASS_TYPE
 G_OBJECT_CLASS_NAME
 g_object_class_install_property
+g_object_class_install_properties
 g_object_class_find_property
 g_object_class_list_properties
 g_object_class_override_property
diff --git a/gobject/gobject.c b/gobject/gobject.c
index 819333f..8fe12d0 100644
--- a/gobject/gobject.c
+++ b/gobject/gobject.c
@@ -383,7 +383,7 @@ g_object_do_class_init (GObjectClass *class)
   g_type_add_interface_check (NULL, object_interface_check_properties);
 }
 
-static void
+static inline void
 install_property_internal (GType       g_type,
 			   guint       property_id,
 			   GParamSpec *pspec)
@@ -444,7 +444,7 @@ g_object_class_install_property (GObjectClass *class,
   if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
     class->construct_properties = g_slist_prepend (class->construct_properties, pspec);
 
-  /* for property overrides of construct poperties, we have to get rid
+  /* for property overrides of construct properties, we have to get rid
    * of the overidden inherited construct property
    */
   pspec = g_param_spec_pool_lookup (pspec_pool, pspec->name, g_type_parent (G_OBJECT_CLASS_TYPE (class)), TRUE);
@@ -453,6 +453,123 @@ g_object_class_install_property (GObjectClass *class,
 }
 
 /**
+ * g_object_class_install_properties:
+ * @oclass: a #GObjectClass
+ * @n_pspecs: the length of the #GParamSpec<!-- -->s array
+ * @pspecs: (array length=n_pspecs): the #GParamSpec<!-- -->s array
+ *   defining the new properties
+ *
+ * Installs new properties from an array of #GParamSpec<!-- -->s. This is
+ * usually done in the class initializer.
+ *
+ * The property id of each property is the index of each #GParamSpec in
+ * the @pspecs array.
+ *
+ * The property id of 0 is treated specially by #GObject and it should not
+ * be used to store a #GParamSpec.
+ *
+ * This function should be used if you plan to use a static array of
+ * #GParamSpec<!-- -->s and g_object_notify_pspec(). For instance, this
+ * class initialization:
+ *
+ * |[
+ * enum {
+ *   PROP_0, PROP_FOO, PROP_BAR, N_PROPERTIES
+ * };
+ *
+ * static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
+ *
+ * static void
+ * my_object_class_init (MyObjectClass *klass)
+ * {
+ *   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ *
+ *   obj_properties[PROP_FOO] =
+ *     g_param_spec_int ("foo", "Foo", "Foo",
+ *                       -1, G_MAXINT,
+ *                       0,
+ *                       G_PARAM_READWRITE);
+ *
+ *   obj_properties[PROP_BAR] =
+ *     g_param_spec_string ("bar", "Bar", "Bar",
+ *                          NULL,
+ *                          G_PARAM_READWRITE);
+ *
+ *   gobject_class->set_property = my_object_set_property;
+ *   gobject_class->get_property = my_object_get_property;
+ *   g_object_class_install_properties (gobject_class,
+ *                                      N_PROPERTIES,
+ *                                      obj_properties);
+ * }
+ * ]|
+ *
+ * allows calling g_object_notify_by_pspec() to notify of property changes:
+ *
+ * |[
+ * void
+ * my_object_set_foo (MyObject *self, gint foo)
+ * {
+ *   if (self->foo != foo)
+ *     {
+ *       self->foo = foo;
+ *       g_object_notify_by_pspec (G_OBJECT (self), obj_properties[PROP_FOO]);
+ *     }
+ *  }
+ * ]|
+ *
+ * Since: 2.26
+ */
+void
+g_object_class_install_properties (GObjectClass  *oclass,
+                                   guint          n_pspecs,
+                                   GParamSpec   **pspecs)
+{
+  GType oclass_type;
+  gint i;
+
+  g_return_if_fail (G_IS_OBJECT_CLASS (oclass));
+  g_return_if_fail (n_pspecs > 1);
+  g_return_if_fail (pspecs[0] == NULL);
+
+  if (CLASS_HAS_DERIVED_CLASS (oclass))
+    g_error ("Attempt to add properties to %s after it was derived",
+             G_OBJECT_CLASS_NAME (oclass));
+
+  oclass_type = G_OBJECT_CLASS_TYPE (oclass);
+
+  /* we skip the first element of the array as it would have a 0 prop_id */
+  for (i = 1; i < n_pspecs; i++)
+    {
+      GParamSpec *pspec = pspecs[i];
+
+      g_return_if_fail (pspec != NULL);
+
+      if (pspec->flags & G_PARAM_WRITABLE)
+        g_return_if_fail (oclass->set_property != NULL);
+      if (pspec->flags & G_PARAM_READABLE)
+        g_return_if_fail (oclass->get_property != NULL);
+      g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0);	/* paranoid */
+      if (pspec->flags & G_PARAM_CONSTRUCT)
+        g_return_if_fail ((pspec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
+      if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
+        g_return_if_fail (pspec->flags & G_PARAM_WRITABLE);
+
+      oclass->flags |= CLASS_HAS_PROPS_FLAG;
+      install_property_internal (oclass_type, i, pspec);
+
+      if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
+        oclass->construct_properties = g_slist_prepend (oclass->construct_properties, pspec);
+
+      /* for property overrides of construct properties, we have to get rid
+       * of the overidden inherited construct property
+       */
+      pspec = g_param_spec_pool_lookup (pspec_pool, pspec->name, g_type_parent (G_OBJECT_CLASS_TYPE (oclass)), TRUE);
+      if (pspec && pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
+        oclass->construct_properties = g_slist_remove (oclass->construct_properties, pspec);
+    }
+}
+
+/**
  * g_object_interface_install_property:
  * @g_iface: any interface vtable for the interface, or the default
  *  vtable for the interface.
diff --git a/gobject/gobject.h b/gobject/gobject.h
index 6707224..c969b8f 100644
--- a/gobject/gobject.h
+++ b/gobject/gobject.h
@@ -390,6 +390,9 @@ GParamSpec**g_object_class_list_properties    (GObjectClass   *oclass,
 void        g_object_class_override_property  (GObjectClass   *oclass,
 					       guint           property_id,
 					       const gchar    *name);
+void        g_object_class_install_properties (GObjectClass   *oclass,
+                                               guint           n_pspecs,
+                                               GParamSpec    **pspecs);
 
 void        g_object_interface_install_property (gpointer     g_iface,
 						 GParamSpec  *pspec);
diff --git a/gobject/gobject.symbols b/gobject/gobject.symbols
index d1103ea..6705427 100644
--- a/gobject/gobject.symbols
+++ b/gobject/gobject.symbols
@@ -140,6 +140,7 @@ g_initially_unowned_get_type
 g_object_add_weak_pointer
 g_object_class_find_property
 g_object_class_install_property
+g_object_class_install_properties
 g_object_class_list_properties
 g_object_class_override_property
 g_object_connect G_GNUC_NULL_TERMINATED
diff --git a/gobject/tests/.gitignore b/gobject/tests/.gitignore
index ca0d628..ba9d6c8 100644
--- a/gobject/tests/.gitignore
+++ b/gobject/tests/.gitignore
@@ -1,3 +1,4 @@
 binding
 dynamictests
+properties
 threadtests
diff --git a/gobject/tests/Makefile.am b/gobject/tests/Makefile.am
index 6efbcd9..8b1394d 100644
--- a/gobject/tests/Makefile.am
+++ b/gobject/tests/Makefile.am
@@ -5,10 +5,12 @@ INCLUDES = -g $(gobject_INCLUDES) $(GLIB_DEBUG_FLAGS)
 noinst_PROGRAMS  = $(TEST_PROGS)
 libgobject_LDADD = ../libgobject-2.0.la $(top_builddir)/gthread/libgthread-2.0.la $(top_builddir)/glib/libglib-2.0.la
 
-TEST_PROGS             += threadtests dynamictests binding
+TEST_PROGS             += threadtests dynamictests binding properties
 threadtests_SOURCES	= threadtests.c
 threadtests_LDADD	= $(libgobject_LDADD)
 dynamictests_SOURCES	= dynamictests.c
 dynamictests_LDADD	= $(libgobject_LDADD)
 binding_SOURCES		= binding.c
 binding_LDADD		= $(libgobject_LDADD)
+properties_SOURCES      = properties.c
+properties_LDADD        = $(libgobject_LDADD)
diff --git a/gobject/tests/properties.c b/gobject/tests/properties.c
new file mode 100644
index 0000000..e2fef4c
--- /dev/null
+++ b/gobject/tests/properties.c
@@ -0,0 +1,222 @@
+#include <stdlib.h>
+#include <gstdio.h>
+#include <glib-object.h>
+
+typedef struct _TestObject {
+  GObject parent_instance;
+  gint foo;
+  gboolean bar;
+  gchar *baz;
+} TestObject;
+
+typedef struct _TestObjectClass {
+  GObjectClass parent_class;
+} TestObjectClass;
+
+enum { PROP_0, PROP_FOO, PROP_BAR, PROP_BAZ, N_PROPERTIES };
+
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
+
+G_DEFINE_TYPE (TestObject, test_object, G_TYPE_OBJECT);
+
+static void
+test_object_set_foo (TestObject *obj,
+                     gint        foo)
+{
+  if (obj->foo != foo)
+    {
+      obj->foo = foo;
+
+      g_assert (properties[PROP_FOO] != NULL);
+      g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_FOO]);
+    }
+}
+
+static void
+test_object_set_bar (TestObject *obj,
+                     gboolean    bar)
+{
+  bar = !!bar;
+
+  if (obj->bar != bar)
+    {
+      obj->bar = bar;
+
+      g_assert (properties[PROP_BAR] != NULL);
+      g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_BAR]);
+    }
+}
+
+static void
+test_object_set_baz (TestObject  *obj,
+                     const gchar *baz)
+{
+  if (g_strcmp0 (obj->baz, baz) != 0)
+    {
+      g_free (obj->baz);
+      obj->baz = g_strdup (baz);
+
+      g_assert (properties[PROP_BAZ] != NULL);
+      g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_BAZ]);
+    }
+}
+
+static void
+test_object_finalize (GObject *gobject)
+{
+  g_free (((TestObject *) gobject)->baz);
+
+  G_OBJECT_CLASS (test_object_parent_class)->finalize (gobject);
+}
+
+static void
+test_object_set_property (GObject *gobject,
+                          guint prop_id,
+                          const GValue *value,
+                          GParamSpec *pspec)
+{
+  TestObject *tobj = (TestObject *) gobject;
+
+  g_assert_cmpint (prop_id, !=, 0);
+  g_assert_cmpint (prop_id, !=, N_PROPERTIES);
+  g_assert (pspec == properties[prop_id]);
+
+  switch (prop_id)
+    {
+    case PROP_FOO:
+      test_object_set_foo (tobj, g_value_get_int (value));
+      break;
+
+    case PROP_BAR:
+      test_object_set_bar (tobj, g_value_get_boolean (value));
+      break;
+
+    case PROP_BAZ:
+      test_object_set_baz (tobj, g_value_get_string (value));
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static void
+test_object_get_property (GObject *gobject,
+                          guint prop_id,
+                          GValue *value,
+                          GParamSpec *pspec)
+{
+  TestObject *tobj = (TestObject *) gobject;
+
+  g_assert_cmpint (prop_id, !=, 0);
+  g_assert_cmpint (prop_id, !=, N_PROPERTIES);
+  g_assert (pspec == properties[prop_id]);
+
+  switch (prop_id)
+    {
+    case PROP_FOO:
+      g_value_set_int (value, tobj->foo);
+      break;
+
+    case PROP_BAR:
+      g_value_set_boolean (value, tobj->bar);
+      break;
+
+    case PROP_BAZ:
+      g_value_set_string (value, tobj->baz);
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static void
+test_object_class_init (TestObjectClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  properties[PROP_FOO] = g_param_spec_int ("foo", "Foo", "Foo",
+                                           -1, G_MAXINT,
+                                           0,
+                                           G_PARAM_READWRITE);
+  properties[PROP_BAR] = g_param_spec_boolean ("bar", "Bar", "Bar",
+                                               FALSE,
+                                               G_PARAM_READWRITE);
+  properties[PROP_BAZ] = g_param_spec_string ("baz", "Baz", "Baz",
+                                              NULL,
+                                              G_PARAM_READWRITE);
+
+  gobject_class->set_property = test_object_set_property;
+  gobject_class->get_property = test_object_get_property;
+  gobject_class->finalize = test_object_finalize;
+
+  g_object_class_install_properties (gobject_class, N_PROPERTIES, properties);
+}
+
+static void
+test_object_init (TestObject *self)
+{
+  self->foo = 42;
+  self->bar = TRUE;
+  self->baz = g_strdup ("Hello");
+}
+
+static void
+properties_install (void)
+{
+  TestObject *obj = g_object_new (test_object_get_type (), NULL);
+  GParamSpec *pspec;
+
+  g_assert (properties[PROP_FOO] != NULL);
+
+  pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (obj), "foo");
+  g_assert (properties[PROP_FOO] == pspec);
+
+  g_object_unref (obj);
+}
+
+typedef struct {
+  const gchar *name;
+  GParamSpec *pspec;
+} TestNotifyClosure;
+
+static void
+on_notify (GObject           *gobject,
+           GParamSpec        *pspec,
+           TestNotifyClosure *clos)
+{
+  g_assert (clos->pspec == pspec);
+  g_assert_cmpstr (clos->name, ==, pspec->name);
+}
+
+static void
+properties_notify (void)
+{
+  TestObject *obj = g_object_new (test_object_get_type (), NULL);
+  TestNotifyClosure clos;
+
+  g_assert (properties[PROP_FOO] != NULL);
+
+  clos.name = "foo";
+  clos.pspec = properties[PROP_FOO];
+
+  g_signal_connect (obj, "notify", G_CALLBACK (on_notify), &clos);
+  g_object_set (obj, "foo", 47, NULL);
+
+  g_object_unref (obj);
+}
+
+int
+main (int argc, char *argv[])
+{
+  g_type_init ();
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_bug_base ("http://bugzilla.gnome.org/";);
+
+  g_test_add_func ("/properties/install", properties_install);
+  g_test_add_func ("/properties/notify", properties_notify);
+
+  return g_test_run ();
+}



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