From c18462b5803a3dd57d3ccb67153ad7851cc8ce08 Mon Sep 17 00:00:00 2001 From: Ryan Lortie Date: Tue, 23 Apr 2013 11:11:20 -0400 Subject: [PATCH] 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 deletion(-) 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 (); } -- 2.7.4