[glib] gobject: new g_set_weak_pointer() & g_clear_weak_pointer() helpers



commit 156d32cb8039f2b3df4af49550aaea4b9701d072
Author: Martin Blanchard <tchaik gmx com>
Date:   Wed Dec 20 23:05:53 2017 +0100

    gobject: new g_set_weak_pointer() & g_clear_weak_pointer() helpers
    
    Weak-pointers are currently lacking g_set_object() & g_clear_object()
    helpers equivalent. New functions (and macros, both are provided) are
    convenient in many case, especially for the property's notify-on-set
    pattern:
    
      if (g_set_weak_pointer (...))
        g_object_notify (...)
    
    Inspired by Christian Hergert's original implementation for
    gnome-builder.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=749527

 docs/reference/gobject/gobject-sections.txt |    2 +
 gobject/gobject.h                           |  102 ++++++++++++++++++++++++++
 gobject/tests/reference.c                   |  106 +++++++++++++++++++++++++++
 3 files changed, 210 insertions(+), 0 deletions(-)
---
diff --git a/docs/reference/gobject/gobject-sections.txt b/docs/reference/gobject/gobject-sections.txt
index 2f4b300..d1fc4bd 100644
--- a/docs/reference/gobject/gobject-sections.txt
+++ b/docs/reference/gobject/gobject-sections.txt
@@ -279,6 +279,8 @@ g_object_weak_ref
 g_object_weak_unref
 g_object_add_weak_pointer
 g_object_remove_weak_pointer
+g_set_weak_pointer
+g_clear_weak_pointer
 GToggleNotify
 g_object_add_toggle_ref
 g_object_remove_toggle_ref
diff --git a/gobject/gobject.h b/gobject/gobject.h
index eaefe92..1a6c969 100644
--- a/gobject/gobject.h
+++ b/gobject/gobject.h
@@ -739,6 +739,108 @@ static inline gboolean
   (g_set_object) ((GObject **) (object_ptr), (GObject *) (new_object)) \
  )
 
+/**
+ * g_clear_weak_pointer: (skip)
+ * @weak_pointer_location: The memory address of a pointer
+ *
+ * Clears a weak reference to a #GObject.
+ *
+ * @weak_pointer_location must not be %NULL.
+ *
+ * If the weak reference is %NULL then this function does nothing.
+ * Otherwise, the weak reference to the object is removed for that location
+ * and the pointer is set to %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.
+ *
+ * Since: 2.56
+ */
+static inline void
+(g_clear_weak_pointer) (gpointer *weak_pointer_location)
+{
+  GObject *object = (GObject *) *weak_pointer_location;
+
+  if (object != NULL)
+    {
+      g_object_remove_weak_pointer (object, weak_pointer_location);
+      *weak_pointer_location = NULL;
+    }
+}
+
+#define g_clear_weak_pointer(weak_pointer_location) \
+ (/* Check types match. */ \
+  (g_clear_weak_pointer) ((gpointer *) (weak_pointer_location)) \
+ )
+
+/**
+ * g_set_weak_pointer: (skip)
+ * @weak_pointer_location: the memory address of a pointer
+ * @new_object: (nullable) (transfer none): a pointer to the new #GObject to
+ *   assign to it, or %NULL to clear the pointer
+ *
+ * Updates a pointer to weakly refer to @new_object. It assigns @new_object
+ * to @weak_pointer_location and ensures that @weak_pointer_location will
+ * automaticaly be set to %NULL if @new_object gets destroyed. The assignment
+ * is not atomic. The weak reference is not thread-safe, see
+ * g_object_add_weak_pointer() for details.
+ *
+ * @weak_pointer_location 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_weak_pointer (&foo->bar, new_bar))
+ *       g_object_notify (foo, "bar");
+ *   }
+ * ]|
+ *
+ * Returns: %TRUE if the value of @weak_pointer_location changed, %FALSE otherwise
+ *
+ * Since: 2.56
+ */
+static inline gboolean
+(g_set_weak_pointer) (gpointer *weak_pointer_location,
+                      GObject  *new_object)
+{
+  GObject *old_object = (GObject *) *weak_pointer_location;
+
+  /* elide a (weak_pointer_location != 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 (old_object == new_object)
+    return FALSE;
+
+  if (old_object != NULL)
+    g_object_remove_weak_pointer (old_object, weak_pointer_location);
+
+  *weak_pointer_location = new_object;
+
+  if (new_object != NULL)
+    g_object_add_weak_pointer (new_object, weak_pointer_location);
+
+  return TRUE;
+}
+
+#define g_set_weak_pointer(weak_pointer_location, new_object) \
+ (/* Check types match. */ \
+  0 ? *(weak_pointer_location) = (new_object), FALSE : \
+  (g_set_weak_pointer) ((gpointer *) (weak_pointer_location), (GObject *) (new_object)) \
+ )
+
 typedef struct {
     /*<private>*/
     union { gpointer p; } priv;
diff --git a/gobject/tests/reference.c b/gobject/tests/reference.c
index aefa1e8..e3f8631 100644
--- a/gobject/tests/reference.c
+++ b/gobject/tests/reference.c
@@ -313,6 +313,108 @@ test_weak_pointer (void)
   g_assert (weak2 == obj);
 }
 
+static void
+test_weak_pointer_clear (void)
+{
+  GObject *obj;
+  gpointer weak = NULL;
+
+  g_clear_weak_pointer (&weak);
+  g_assert_null (weak);
+
+  weak = obj = g_object_new (G_TYPE_OBJECT, NULL);
+  g_assert_cmpint (obj->ref_count, ==, 1);
+
+  g_object_add_weak_pointer (obj, &weak);
+  g_assert_cmpint (obj->ref_count, ==, 1);
+  g_assert_true (weak == obj);
+
+  g_clear_weak_pointer (&weak);
+  g_assert_cmpint (obj->ref_count, ==, 1);
+  g_assert_null (weak);
+
+  g_object_unref (obj);
+}
+
+static void
+test_weak_pointer_clear_function (void)
+{
+  GObject *obj;
+  gpointer weak = NULL;
+
+  (g_clear_weak_pointer) (&weak);
+  g_assert_null (weak);
+
+  weak = obj = g_object_new (G_TYPE_OBJECT, NULL);
+  g_assert_cmpint (obj->ref_count, ==, 1);
+
+  g_object_add_weak_pointer (obj, &weak);
+  g_assert_cmpint (obj->ref_count, ==, 1);
+  g_assert_true (weak == obj);
+
+  (g_clear_weak_pointer) (&weak);
+  g_assert_cmpint (obj->ref_count, ==, 1);
+  g_assert_null (weak);
+
+  g_object_unref (obj);
+}
+
+static void
+test_weak_pointer_set (void)
+{
+  GObject *obj;
+  gpointer weak = NULL;
+
+  g_assert_false (g_set_weak_pointer (&weak, NULL));
+  g_assert_null (weak);
+
+  obj = g_object_new (G_TYPE_OBJECT, NULL);
+  g_assert_cmpint (obj->ref_count, ==, 1);
+
+  g_assert_true (g_set_weak_pointer (&weak, obj));
+  g_assert_cmpint (obj->ref_count, ==, 1);
+  g_assert_true (weak == obj);
+
+  g_assert_true (g_set_weak_pointer (&weak, NULL));
+  g_assert_cmpint (obj->ref_count, ==, 1);
+  g_assert_null (weak);
+
+  g_assert_true (g_set_weak_pointer (&weak, obj));
+  g_assert_cmpint (obj->ref_count, ==, 1);
+  g_assert_true (weak == obj);
+
+  g_object_unref (obj);
+  g_assert_null (weak);
+}
+
+static void
+test_weak_pointer_set_function (void)
+{
+  GObject *obj;
+  gpointer weak = NULL;
+
+  g_assert_false ((g_set_weak_pointer) (&weak, NULL));
+  g_assert_null (weak);
+
+  obj = g_object_new (G_TYPE_OBJECT, NULL);
+  g_assert_cmpint (obj->ref_count, ==, 1);
+
+  g_assert_true ((g_set_weak_pointer) (&weak, obj));
+  g_assert_cmpint (obj->ref_count, ==, 1);
+  g_assert_true (weak == obj);
+
+  g_assert_true ((g_set_weak_pointer) (&weak, NULL));
+  g_assert_cmpint (obj->ref_count, ==, 1);
+  g_assert_null (weak);
+
+  g_assert_true ((g_set_weak_pointer) (&weak, obj));
+  g_assert_cmpint (obj->ref_count, ==, 1);
+  g_assert_true (weak == obj);
+
+  g_object_unref (obj);
+  g_assert_null (weak);
+}
+
 /* See gobject/tests/threadtests.c for the threaded version */
 static void
 test_weak_ref (void)
@@ -667,6 +769,10 @@ main (int argc, char **argv)
   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);
+  g_test_add_func ("/object/weak-pointer/clear", test_weak_pointer_clear);
+  g_test_add_func ("/object/weak-pointer/clear-function", test_weak_pointer_clear_function);
+  g_test_add_func ("/object/weak-pointer/set", test_weak_pointer_set);
+  g_test_add_func ("/object/weak-pointer/set-function", test_weak_pointer_set_function);
   g_test_add_func ("/object/weak-ref", test_weak_ref);
   g_test_add_func ("/object/toggle-ref", test_toggle_ref);
   g_test_add_func ("/object/qdata", test_object_qdata);


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