[glib] gobject: Add g_set_object() convenience function to set GObject pointers



commit d951db4236146efd8f4f343740d50433739526c8
Author: Philip Withnall <philip withnall collabora co uk>
Date:   Tue Dec 16 11:29:03 2014 +0000

    gobject: Add g_set_object() convenience function to set GObject pointers
    
    Along the same lines as g_clear_object(), g_set_object() is a
    convenience function to update a GObject pointer, handling reference
    counting transparently and correctly.
    
    Specifically, it handles the case where a pointer is set to its current
    value. If handled naïvely, that could result in the object instance
    being finalised. In the following code, that happens when
    (my_obj == new_value) and the object has a single reference:
        g_clear_object (&my_obj);
        my_obj = g_object_ref (new_value);
    
    It also simplifies boilerplate code such as set_property()
    implementations, which are otherwise long and boring.
    
    Test cases included.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=741589

 docs/reference/gobject/gobject-sections.txt |    1 +
 gobject/gobject.h                           |   63 +++++++++++++++++++++++++++
 gobject/tests/reference.c                   |   60 +++++++++++++++++++++++++
 3 files changed, 124 insertions(+), 0 deletions(-)
---
diff --git a/docs/reference/gobject/gobject-sections.txt b/docs/reference/gobject/gobject-sections.txt
index 84eee38..c96a604 100644
--- a/docs/reference/gobject/gobject-sections.txt
+++ b/docs/reference/gobject/gobject-sections.txt
@@ -263,6 +263,7 @@ GParameter
 g_object_ref
 g_object_unref
 g_object_ref_sink
+g_set_object
 g_clear_object
 GInitiallyUnowned
 GInitiallyUnownedClass
diff --git a/gobject/gobject.h b/gobject/gobject.h
index 522011f..40d3160 100644
--- a/gobject/gobject.h
+++ b/gobject/gobject.h
@@ -651,6 +651,69 @@ GLIB_AVAILABLE_IN_ALL
 void    g_clear_object (volatile GObject **object_ptr);
 #define g_clear_object(object_ptr) g_clear_pointer ((object_ptr), g_object_unref)
 
+/**
+ * g_set_object: (skip)
+ * @object_ptr: a pointer to a #GObject reference
+ * @new_object: (nullable) (transfer none): a pointer to the new #GObject to
+ *   assign to it, or %NULL to clear the pointer
+ *
+ * Updates a #GObject pointer to refer to @new_object. It increments the
+ * reference count of @new_object (if non-%NULL), decrements the reference
+ * count of the current value of @object_ptr (if non-%NULL), and assigns
+ * @new_object to @object_ptr. The assignment is not atomic.
+ *
+ * @object_ptr must not be %NULL.
+ *
+ * A macro is also included that allows this function to be used without
+ * pointer casts. The function itself is static inline, so its address may vary
+ * between compilation units.
+ *
+ * One convenient usage of this function is in implementing property setters:
+ * |[
+ *   void
+ *   foo_set_bar (Foo *foo,
+ *                Bar *new_bar)
+ *   {
+ *     g_return_if_fail (IS_FOO (foo));
+ *     g_return_if_fail (new_bar == NULL || IS_BAR (new_bar));
+ *
+ *     if (g_set_object (&foo->bar, new_bar))
+ *       g_object_notify (foo, "bar");
+ *   }
+ * ]|
+ *
+ * Returns: %TRUE if the value of @object_ptr changed, %FALSE otherwise
+ *
+ * Since: 2.44
+ */
+static inline gboolean
+(g_set_object) (GObject **object_ptr,
+                GObject  *new_object)
+{
+  /* rely on g_object_[un]ref() to check the pointers are actually GObjects;
+   * elide a (object_ptr != NULL) check because most of the time we will be
+   * operating on struct members with a constant offset, so a NULL check would
+   * not catch bugs */
+
+  if (*object_ptr == new_object)
+    return FALSE;
+
+  if (new_object != NULL)
+    g_object_ref (new_object);
+  if (*object_ptr != NULL)
+    g_object_unref (*object_ptr);
+
+  *object_ptr = new_object;
+
+  return TRUE;
+}
+
+#define g_set_object(object_ptr, new_object) \
+ (/* Check types match. */ \
+  0 ? *(object_ptr) = (new_object), FALSE : \
+  (g_set_object) ((GObject **) (object_ptr), (GObject *) (new_object)) \
+ )
+
 typedef struct {
     /*<private>*/
     union { gpointer p; } priv;
diff --git a/gobject/tests/reference.c b/gobject/tests/reference.c
index 0742339..aefa1e8 100644
--- a/gobject/tests/reference.c
+++ b/gobject/tests/reference.c
@@ -152,6 +152,64 @@ test_clear_function (void)
 }
 
 static void
+test_set (void)
+{
+  GObject *o = NULL;
+  GObject *tmp;
+
+  g_assert (!g_set_object (&o, NULL));
+  g_assert (o == NULL);
+
+  tmp = g_object_new (G_TYPE_OBJECT, NULL);
+  g_assert_cmpint (tmp->ref_count, ==, 1);
+
+  g_assert (g_set_object (&o, tmp));
+  g_assert (o == tmp);
+  g_assert_cmpint (tmp->ref_count, ==, 2);
+
+  g_object_unref (tmp);
+  g_assert_cmpint (tmp->ref_count, ==, 1);
+
+  /* Setting it again shouldn’t cause finalisation. */
+  g_assert (!g_set_object (&o, tmp));
+  g_assert (o == tmp);
+  g_assert_cmpint (tmp->ref_count, ==, 1);
+
+  g_assert (g_set_object (&o, NULL));
+  g_assert (o == NULL);
+  g_assert (!G_IS_OBJECT (tmp));  /* finalised */
+}
+
+static void
+test_set_function (void)
+{
+  GObject *o = NULL;
+  GObject *tmp;
+
+  g_assert (!(g_set_object) (&o, NULL));
+  g_assert (o == NULL);
+
+  tmp = g_object_new (G_TYPE_OBJECT, NULL);
+  g_assert_cmpint (tmp->ref_count, ==, 1);
+
+  g_assert ((g_set_object) (&o, tmp));
+  g_assert (o == tmp);
+  g_assert_cmpint (tmp->ref_count, ==, 2);
+
+  g_object_unref (tmp);
+  g_assert_cmpint (tmp->ref_count, ==, 1);
+
+  /* Setting it again shouldn’t cause finalisation. */
+  g_assert (!(g_set_object) (&o, tmp));
+  g_assert (o == tmp);
+  g_assert_cmpint (tmp->ref_count, ==, 1);
+
+  g_assert ((g_set_object) (&o, NULL));
+  g_assert (o == NULL);
+  g_assert (!G_IS_OBJECT (tmp));  /* finalised */
+}
+
+static void
 toggle_cb (gpointer data, GObject *obj, gboolean is_last)
 {
   gboolean *b = data;
@@ -604,6 +662,8 @@ main (int argc, char **argv)
   g_test_add_func ("/type/class-private", test_class_private);
   g_test_add_func ("/object/clear", test_clear);
   g_test_add_func ("/object/clear-function", test_clear_function);
+  g_test_add_func ("/object/set", test_set);
+  g_test_add_func ("/object/set-function", test_set_function);
   g_test_add_func ("/object/value", test_object_value);
   g_test_add_func ("/object/initially-unowned", test_initially_unowned);
   g_test_add_func ("/object/weak-pointer", test_weak_pointer);


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