[glib/test/gobjectnew: 2/3] GParamSpec: add g_param_spec_get_default_value()



commit c18462b5803a3dd57d3ccb67153ad7851cc8ce08
Author: Ryan Lortie <desrt desrt ca>
Date:   Tue Apr 23 11:11:20 2013 -0400

    GParamSpec: add g_param_spec_get_default_value()
    
    The way of getting the default value out of a GParamSpec is to allocate
    a GValue, initialise it, then call g_param_spec_set_default() to set the
    default value into that GValue.
    
    This is exactly how we handle setting the default value for all of the
    construct properties that were not explicitly passed to g_object_new().
    
    Instead of doing the alloc/init/store on all construct properties on
    every call to g_object_new(), we can cache those GValues in the private
    data of the GParamSpec itself and reuse them.
    
    This patch does not actually make that change to g_object_new() yet, but
    it adds the API to GParamSpec so that a future patch to GObject can make
    the change.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=698056

 docs/reference/gobject/gobject-sections.txt |    1 +
 gobject/gparam.c                            |   62 +++++++++++++++++++++++++++
 gobject/gparam.h                            |    3 +-
 gobject/tests/param.c                       |   16 +++++++
 4 files changed, 81 insertions(+), 1 deletions(-)
---
diff --git a/docs/reference/gobject/gobject-sections.txt b/docs/reference/gobject/gobject-sections.txt
index 9a190e0..d507f15 100644
--- a/docs/reference/gobject/gobject-sections.txt
+++ b/docs/reference/gobject/gobject-sections.txt
@@ -480,6 +480,7 @@ g_param_spec_ref
 g_param_spec_unref
 g_param_spec_sink
 g_param_spec_ref_sink
+g_param_spec_get_default_value
 g_param_value_set_default
 g_param_value_defaults
 g_param_value_validate
diff --git a/gobject/gparam.c b/gobject/gparam.c
index b5c3024..72bd855 100644
--- a/gobject/gparam.c
+++ b/gobject/gparam.c
@@ -80,6 +80,13 @@ static gchar*        value_param_lcopy_value         (const GValue   *value,
                                                 GTypeCValue    *collect_values,
                                                 guint           collect_flags);
 
+typedef struct
+{
+  GValue default_value;
+} GParamSpecPrivate;
+
+static gint g_param_private_offset;
+#define PRIV(inst) (&G_STRUCT_MEMBER(GParamSpecPrivate, (inst), g_param_private_offset))
 
 /* --- functions --- */
 void
@@ -147,6 +154,9 @@ g_param_spec_class_init (GParamSpecClass *class,
   class->value_set_default = NULL;
   class->value_validate = NULL;
   class->values_cmp = NULL;
+
+  g_type_class_add_private (class, sizeof (GParamSpecPrivate));
+  g_param_private_offset = g_type_class_get_instance_private_offset (class);
 }
 
 static void
@@ -168,6 +178,11 @@ g_param_spec_init (GParamSpec      *pspec,
 static void
 g_param_spec_finalize (GParamSpec *pspec)
 {
+  GParamSpecPrivate *priv = PRIV (pspec);
+
+  if (priv->default_value.g_type)
+    g_value_reset (&priv->default_value);
+
   g_datalist_clear (&pspec->qdata);
 
   if (!(pspec->flags & G_PARAM_STATIC_NICK))
@@ -1506,3 +1521,50 @@ g_value_dup_param (const GValue *value)
 
   return value->data[0].v_pointer ? g_param_spec_ref (value->data[0].v_pointer) : NULL;
 }
+
+/**
+ * g_param_get_default_value:
+ * @param: a #GParamSpec
+ *
+ * Gets the default value of @param as a pointer to a #GValue.
+ *
+ * The #GValue will remain value for the life of @param.
+ *
+ * Returns: a pointer to a #GValue which must not be modified
+ *
+ * Since: 2.38
+ **/
+const GValue *
+g_param_spec_get_default_value (GParamSpec *pspec)
+{
+  GParamSpecPrivate *priv = PRIV (pspec);
+
+  /* We use the type field of the GValue as the key for the once because
+   * it will be zero before it is initialised and non-zero after.  We
+   * have to take care that we don't write a non-zero value to the type
+   * field before we are completely done, however, because then another
+   * thread could come along and find the value partially-initialised.
+   *
+   * In order to accomplish this we store the default value in a
+   * stack-allocated GValue.  We then set the type field in that value
+   * to zero and copy the contents into place.  We then end by storing
+   * the type as the last step in order to ensure that we're completely
+   * done before a g_once_init_enter() could take the fast path in
+   * another thread.
+   */
+  if (g_once_init_enter (&priv->default_value.g_type))
+    {
+      GValue default_value = G_VALUE_INIT;
+
+      g_value_init (&default_value, pspec->value_type);
+      g_param_value_set_default (pspec, &default_value);
+
+      /* store all but the type */
+      default_value.g_type = 0;
+      priv->default_value = default_value;
+
+      g_once_init_leave (&priv->default_value.g_type, pspec->value_type);
+    }
+
+  return &priv->default_value;
+}
diff --git a/gobject/gparam.h b/gobject/gparam.h
index ebdc91e..b35ad51 100644
--- a/gobject/gparam.h
+++ b/gobject/gparam.h
@@ -336,6 +336,8 @@ void           g_value_take_param               (GValue        *value,
 GLIB_DEPRECATED_FOR(g_value_take_param)
 void           g_value_set_param_take_ownership (GValue        *value,
                                                  GParamSpec    *param);
+GLIB_AVAILABLE_IN_2_36
+const GValue *  g_param_spec_get_default_value  (GParamSpec     *param);
 
 /* --- convenience functions --- */
 typedef struct _GParamSpecTypeInfo GParamSpecTypeInfo;
@@ -421,7 +423,6 @@ GParamSpec**        g_param_spec_pool_list          (GParamSpecPool *pool,
                                                 guint          *n_pspecs_p);
 
 
-
 /* contracts:
  *
  * gboolean value_validate (GParamSpec *pspec,
diff --git a/gobject/tests/param.c b/gobject/tests/param.c
index 0c9edab..31515dc 100644
--- a/gobject/tests/param.c
+++ b/gobject/tests/param.c
@@ -786,6 +786,21 @@ test_param_implement (void)
           }
 }
 
+static void
+test_param_default (void)
+{
+  GParamSpec *param;
+  const GValue *def;
+
+  param = g_param_spec_int ("my-int", "My Int", "Blurb", 0, 20, 10, G_PARAM_READWRITE);
+  def = g_param_spec_get_default_value (param);
+
+  g_assert (G_VALUE_HOLDS (def, G_TYPE_INT));
+  g_assert_cmpint (g_value_get_int (def), ==, 10);
+
+  g_param_spec_unref (param);
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -798,6 +813,7 @@ main (int argc, char *argv[])
   g_test_add_func ("/param/convert", test_param_convert);
   g_test_add_func ("/param/implement", test_param_implement);
   g_test_add_func ("/value/transform", test_value_transform);
+  g_test_add_func ("/param/default", test_param_default);
 
   return g_test_run ();
 }


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