[glib/issue-737] Allow calling get_property() with an uninitialized GValue



commit 02dc60973f432ecf936756a8255076fcac582347
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Tue Jun 4 14:37:37 2019 +0100

    Allow calling get_property() with an uninitialized GValue
    
    We already have the GType with which the GValue should be initialized,
    so requiring an initialized GValue is not really necessary, and it
    actually complicates code that wraps GObject. Additionally, it
    introduces a mostly unnecessary g_value_rest().
    
    We already changed g_object_getv() to allow passing uninitialized
    GValues, but this fell through the cracks.
    
    Closes: #737

 gobject/gobject.c          | 65 +++++++++++++++++++++++++++-------------------
 gobject/tests/properties.c | 46 ++++++++++++++++++++++++++++++++
 2 files changed, 84 insertions(+), 27 deletions(-)
---
diff --git a/gobject/gobject.c b/gobject/gobject.c
index c95311b3e..5f613f230 100644
--- a/gobject/gobject.c
+++ b/gobject/gobject.c
@@ -2565,9 +2565,15 @@ g_object_set_property (GObject       *object,
  * @property_name: the name of the property to get
  * @value: return location for the property value
  *
- * Gets a property of an object. @value must have been initialized to the
- * expected type of the property (or a type to which the expected type can be
- * transformed) using g_value_init().
+ * Gets a property of an object.
+ *
+ * The @value can be:
+ *
+ *  - an empty #GValue initialized by %G_VALUE_INIT, which will be
+ *    automatically initialized with the expected type of the property
+ *  - a #GValue initialized with the expected type of the property
+ *  - a #GValue initialized with a type to which the expected type
+ *    of the property can be transformed
  *
  * In general, a copy is made of the property contents and the caller is
  * responsible for freeing the memory by calling g_value_unset().
@@ -2584,7 +2590,7 @@ g_object_get_property (GObject       *object,
   
   g_return_if_fail (G_IS_OBJECT (object));
   g_return_if_fail (property_name != NULL);
-  g_return_if_fail (G_IS_VALUE (value));
+  g_return_if_fail (value != NULL);
   
   g_object_ref (object);
   
@@ -2597,33 +2603,38 @@ g_object_get_property (GObject     *object,
     {
       GValue *prop_value, tmp_value = G_VALUE_INIT;
       
-      /* auto-conversion of the callers value type
-       */
-      if (G_VALUE_TYPE (value) == pspec->value_type)
-       {
-         g_value_reset (value);
-         prop_value = value;
-       }
+      if (G_VALUE_TYPE (value) == G_TYPE_INVALID)
+        {
+          /* uninitialized value */
+          g_value_init (value, pspec->value_type);
+          prop_value = value;
+        }
+      else if (G_VALUE_TYPE (value) == pspec->value_type)
+        {
+          /* auto-conversion of the callers value type */
+          g_value_reset (value);
+          prop_value = value;
+        }
       else if (!g_value_type_transformable (pspec->value_type, G_VALUE_TYPE (value)))
-       {
-         g_warning ("%s: can't retrieve property '%s' of type '%s' as value of type '%s'",
-                    G_STRFUNC, pspec->name,
-                    g_type_name (pspec->value_type),
-                    G_VALUE_TYPE_NAME (value));
-         g_object_unref (object);
-         return;
-       }
+        {
+          g_warning ("%s: can't retrieve property '%s' of type '%s' as value of type '%s'",
+                     G_STRFUNC, pspec->name,
+                     g_type_name (pspec->value_type),
+                     G_VALUE_TYPE_NAME (value));
+          g_object_unref (object);
+          return;
+        }
       else
-       {
-         g_value_init (&tmp_value, pspec->value_type);
-         prop_value = &tmp_value;
-       }
+        {
+          g_value_init (&tmp_value, pspec->value_type);
+          prop_value = &tmp_value;
+        }
       object_get_property (object, pspec, prop_value);
       if (prop_value != value)
-       {
-         g_value_transform (prop_value, value);
-         g_value_unset (&tmp_value);
-       }
+        {
+          g_value_transform (prop_value, value);
+          g_value_unset (&tmp_value);
+        }
     }
   
   g_object_unref (object);
diff --git a/gobject/tests/properties.c b/gobject/tests/properties.c
index 0aa325a74..1e48e3389 100644
--- a/gobject/tests/properties.c
+++ b/gobject/tests/properties.c
@@ -541,6 +541,51 @@ properties_testv_getv (void)
   g_object_unref (test_obj);
 }
 
+static void
+properties_get_property (void)
+{
+  TestObject *test_obj;
+  struct {
+    const char *name;
+    GType gtype;
+    GValue value;
+  } test_props[] = {
+    { "foo", G_TYPE_INT, G_VALUE_INIT },
+    { "bar", G_TYPE_INVALID, G_VALUE_INIT },
+    { "bar", G_TYPE_STRING, G_VALUE_INIT },
+  };
+  int i;
+
+  g_test_summary ("g_object_get_property() accepts uninitialized, "
+                  "initialized, and transformable values");
+
+  for (i = 0; i < G_N_ELEMENTS (test_props); i++)
+    {
+      if (test_props[i].gtype != G_TYPE_INVALID)
+        g_value_init (&(test_props[i].value), test_props[i].gtype);
+    }
+
+  test_obj = (TestObject *) g_object_new_with_properties (test_object_get_type (), 0, NULL, NULL);
+
+  g_test_message ("Test g_object_get_property with an initialized value");
+  g_object_get_property (G_OBJECT (test_obj), test_props[0].name, &(test_props[0].value));
+  g_assert_cmpint (g_value_get_int (&(test_props[0].value)), ==, 42);
+
+  g_test_message ("Test g_object_get_property with an uninitialized value");
+  g_object_get_property (G_OBJECT (test_obj), test_props[1].name, &(test_props[1].value));
+  g_assert_true (g_value_get_boolean (&(test_props[1].value)));
+
+  g_test_message ("Test g_object_get_property with a transformable value");
+  g_object_get_property (G_OBJECT (test_obj), test_props[2].name, &(test_props[2].value));
+  g_assert_true (G_VALUE_HOLDS_STRING (&(test_props[2].value)));
+  g_assert_cmpstr (g_value_get_string (&(test_props[2].value)), ==, "TRUE");
+
+  for (i = 0; i < G_N_ELEMENTS (test_props); i++)
+    g_value_unset (&(test_props[i].value));
+
+  g_object_unref (test_obj);
+}
+
 static void
 properties_testv_notify_queue (void)
 {
@@ -599,6 +644,7 @@ main (int argc, char *argv[])
   g_test_add_func ("/properties/notify", properties_notify);
   g_test_add_func ("/properties/notify-queue", properties_notify_queue);
   g_test_add_func ("/properties/construct", properties_construct);
+  g_test_add_func ("/properties/get-property", properties_get_property);
 
   g_test_add_func ("/properties/testv_with_no_properties",
       properties_testv_with_no_properties);


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