gobject: Add install_properties()

Since we added g_object_notify_by_pspec(), an efficient way to install
and notify properties relies on storing the GParamSpec pointers inside
a static arrays, like we do for signal identifiers.

Instead of multiple calls to g_object_class_install_property(), we
should have a single function to take the static array of GParamSpecs
and iterate it.

https://bugzilla.gnome.org/show_bug.cgi?id=626919

Signed-off-by: Emmanuele Bassi <ebassi@linux.intel.com>
This commit is contained in:
Emmanuele Bassi 2010-08-18 15:32:27 +01:00
parent 58a40904af
commit 9cd43d7a4c
7 changed files with 350 additions and 3 deletions

View File

@ -244,6 +244,7 @@ G_OBJECT_TYPE_NAME
G_OBJECT_CLASS_TYPE G_OBJECT_CLASS_TYPE
G_OBJECT_CLASS_NAME G_OBJECT_CLASS_NAME
g_object_class_install_property g_object_class_install_property
g_object_class_install_properties
g_object_class_find_property g_object_class_find_property
g_object_class_list_properties g_object_class_list_properties
g_object_class_override_property g_object_class_override_property

View File

@ -383,7 +383,7 @@ g_object_do_class_init (GObjectClass *class)
g_type_add_interface_check (NULL, object_interface_check_properties); g_type_add_interface_check (NULL, object_interface_check_properties);
} }
static void static inline void
install_property_internal (GType g_type, install_property_internal (GType g_type,
guint property_id, guint property_id,
GParamSpec *pspec) GParamSpec *pspec)
@ -444,7 +444,7 @@ g_object_class_install_property (GObjectClass *class,
if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
class->construct_properties = g_slist_prepend (class->construct_properties, pspec); class->construct_properties = g_slist_prepend (class->construct_properties, pspec);
/* for property overrides of construct poperties, we have to get rid /* for property overrides of construct properties, we have to get rid
* of the overidden inherited construct property * of the overidden inherited construct property
*/ */
pspec = g_param_spec_pool_lookup (pspec_pool, pspec->name, g_type_parent (G_OBJECT_CLASS_TYPE (class)), TRUE); pspec = g_param_spec_pool_lookup (pspec_pool, pspec->name, g_type_parent (G_OBJECT_CLASS_TYPE (class)), TRUE);
@ -452,6 +452,123 @@ g_object_class_install_property (GObjectClass *class,
class->construct_properties = g_slist_remove (class->construct_properties, pspec); class->construct_properties = g_slist_remove (class->construct_properties, pspec);
} }
/**
* g_object_class_install_properties:
* @oclass: a #GObjectClass
* @n_pspecs: the length of the #GParamSpec<!-- -->s array
* @pspecs: (array length=n_pspecs): the #GParamSpec<!-- -->s array
* defining the new properties
*
* Installs new properties from an array of #GParamSpec<!-- -->s. This is
* usually done in the class initializer.
*
* The property id of each property is the index of each #GParamSpec in
* the @pspecs array.
*
* The property id of 0 is treated specially by #GObject and it should not
* be used to store a #GParamSpec.
*
* This function should be used if you plan to use a static array of
* #GParamSpec<!-- -->s and g_object_notify_pspec(). For instance, this
* class initialization:
*
* |[
* enum {
* PROP_0, PROP_FOO, PROP_BAR, N_PROPERTIES
* };
*
* static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
*
* static void
* my_object_class_init (MyObjectClass *klass)
* {
* GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
*
* obj_properties[PROP_FOO] =
* g_param_spec_int ("foo", "Foo", "Foo",
* -1, G_MAXINT,
* 0,
* G_PARAM_READWRITE);
*
* obj_properties[PROP_BAR] =
* g_param_spec_string ("bar", "Bar", "Bar",
* NULL,
* G_PARAM_READWRITE);
*
* gobject_class->set_property = my_object_set_property;
* gobject_class->get_property = my_object_get_property;
* g_object_class_install_properties (gobject_class,
* N_PROPERTIES,
* obj_properties);
* }
* ]|
*
* allows calling g_object_notify_by_pspec() to notify of property changes:
*
* |[
* void
* my_object_set_foo (MyObject *self, gint foo)
* {
* if (self->foo != foo)
* {
* self->foo = foo;
* g_object_notify_by_pspec (G_OBJECT (self), obj_properties[PROP_FOO]);
* }
* }
* ]|
*
* Since: 2.26
*/
void
g_object_class_install_properties (GObjectClass *oclass,
guint n_pspecs,
GParamSpec **pspecs)
{
GType oclass_type;
gint i;
g_return_if_fail (G_IS_OBJECT_CLASS (oclass));
g_return_if_fail (n_pspecs > 1);
g_return_if_fail (pspecs[0] == NULL);
if (CLASS_HAS_DERIVED_CLASS (oclass))
g_error ("Attempt to add properties to %s after it was derived",
G_OBJECT_CLASS_NAME (oclass));
oclass_type = G_OBJECT_CLASS_TYPE (oclass);
/* we skip the first element of the array as it would have a 0 prop_id */
for (i = 1; i < n_pspecs; i++)
{
GParamSpec *pspec = pspecs[i];
g_return_if_fail (pspec != NULL);
if (pspec->flags & G_PARAM_WRITABLE)
g_return_if_fail (oclass->set_property != NULL);
if (pspec->flags & G_PARAM_READABLE)
g_return_if_fail (oclass->get_property != NULL);
g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0); /* paranoid */
if (pspec->flags & G_PARAM_CONSTRUCT)
g_return_if_fail ((pspec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
g_return_if_fail (pspec->flags & G_PARAM_WRITABLE);
oclass->flags |= CLASS_HAS_PROPS_FLAG;
install_property_internal (oclass_type, i, pspec);
if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
oclass->construct_properties = g_slist_prepend (oclass->construct_properties, pspec);
/* for property overrides of construct properties, we have to get rid
* of the overidden inherited construct property
*/
pspec = g_param_spec_pool_lookup (pspec_pool, pspec->name, g_type_parent (G_OBJECT_CLASS_TYPE (oclass)), TRUE);
if (pspec && pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
oclass->construct_properties = g_slist_remove (oclass->construct_properties, pspec);
}
}
/** /**
* g_object_interface_install_property: * g_object_interface_install_property:
* @g_iface: any interface vtable for the interface, or the default * @g_iface: any interface vtable for the interface, or the default

View File

@ -390,6 +390,9 @@ GParamSpec**g_object_class_list_properties (GObjectClass *oclass,
void g_object_class_override_property (GObjectClass *oclass, void g_object_class_override_property (GObjectClass *oclass,
guint property_id, guint property_id,
const gchar *name); const gchar *name);
void g_object_class_install_properties (GObjectClass *oclass,
guint n_pspecs,
GParamSpec **pspecs);
void g_object_interface_install_property (gpointer g_iface, void g_object_interface_install_property (gpointer g_iface,
GParamSpec *pspec); GParamSpec *pspec);

View File

@ -140,6 +140,7 @@ g_initially_unowned_get_type
g_object_add_weak_pointer g_object_add_weak_pointer
g_object_class_find_property g_object_class_find_property
g_object_class_install_property g_object_class_install_property
g_object_class_install_properties
g_object_class_list_properties g_object_class_list_properties
g_object_class_override_property g_object_class_override_property
g_object_connect G_GNUC_NULL_TERMINATED g_object_connect G_GNUC_NULL_TERMINATED

View File

@ -1,3 +1,4 @@
binding binding
dynamictests dynamictests
properties
threadtests threadtests

View File

@ -5,10 +5,12 @@ INCLUDES = -g $(gobject_INCLUDES) $(GLIB_DEBUG_FLAGS)
noinst_PROGRAMS = $(TEST_PROGS) noinst_PROGRAMS = $(TEST_PROGS)
libgobject_LDADD = ../libgobject-2.0.la $(top_builddir)/gthread/libgthread-2.0.la $(top_builddir)/glib/libglib-2.0.la libgobject_LDADD = ../libgobject-2.0.la $(top_builddir)/gthread/libgthread-2.0.la $(top_builddir)/glib/libglib-2.0.la
TEST_PROGS += threadtests dynamictests binding TEST_PROGS += threadtests dynamictests binding properties
threadtests_SOURCES = threadtests.c threadtests_SOURCES = threadtests.c
threadtests_LDADD = $(libgobject_LDADD) threadtests_LDADD = $(libgobject_LDADD)
dynamictests_SOURCES = dynamictests.c dynamictests_SOURCES = dynamictests.c
dynamictests_LDADD = $(libgobject_LDADD) dynamictests_LDADD = $(libgobject_LDADD)
binding_SOURCES = binding.c binding_SOURCES = binding.c
binding_LDADD = $(libgobject_LDADD) binding_LDADD = $(libgobject_LDADD)
properties_SOURCES = properties.c
properties_LDADD = $(libgobject_LDADD)

222
gobject/tests/properties.c Normal file
View File

@ -0,0 +1,222 @@
#include <stdlib.h>
#include <gstdio.h>
#include <glib-object.h>
typedef struct _TestObject {
GObject parent_instance;
gint foo;
gboolean bar;
gchar *baz;
} TestObject;
typedef struct _TestObjectClass {
GObjectClass parent_class;
} TestObjectClass;
enum { PROP_0, PROP_FOO, PROP_BAR, PROP_BAZ, N_PROPERTIES };
static GParamSpec *properties[N_PROPERTIES] = { NULL, };
G_DEFINE_TYPE (TestObject, test_object, G_TYPE_OBJECT);
static void
test_object_set_foo (TestObject *obj,
gint foo)
{
if (obj->foo != foo)
{
obj->foo = foo;
g_assert (properties[PROP_FOO] != NULL);
g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_FOO]);
}
}
static void
test_object_set_bar (TestObject *obj,
gboolean bar)
{
bar = !!bar;
if (obj->bar != bar)
{
obj->bar = bar;
g_assert (properties[PROP_BAR] != NULL);
g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_BAR]);
}
}
static void
test_object_set_baz (TestObject *obj,
const gchar *baz)
{
if (g_strcmp0 (obj->baz, baz) != 0)
{
g_free (obj->baz);
obj->baz = g_strdup (baz);
g_assert (properties[PROP_BAZ] != NULL);
g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_BAZ]);
}
}
static void
test_object_finalize (GObject *gobject)
{
g_free (((TestObject *) gobject)->baz);
G_OBJECT_CLASS (test_object_parent_class)->finalize (gobject);
}
static void
test_object_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
TestObject *tobj = (TestObject *) gobject;
g_assert_cmpint (prop_id, !=, 0);
g_assert_cmpint (prop_id, !=, N_PROPERTIES);
g_assert (pspec == properties[prop_id]);
switch (prop_id)
{
case PROP_FOO:
test_object_set_foo (tobj, g_value_get_int (value));
break;
case PROP_BAR:
test_object_set_bar (tobj, g_value_get_boolean (value));
break;
case PROP_BAZ:
test_object_set_baz (tobj, g_value_get_string (value));
break;
default:
g_assert_not_reached ();
}
}
static void
test_object_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
TestObject *tobj = (TestObject *) gobject;
g_assert_cmpint (prop_id, !=, 0);
g_assert_cmpint (prop_id, !=, N_PROPERTIES);
g_assert (pspec == properties[prop_id]);
switch (prop_id)
{
case PROP_FOO:
g_value_set_int (value, tobj->foo);
break;
case PROP_BAR:
g_value_set_boolean (value, tobj->bar);
break;
case PROP_BAZ:
g_value_set_string (value, tobj->baz);
break;
default:
g_assert_not_reached ();
}
}
static void
test_object_class_init (TestObjectClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
properties[PROP_FOO] = g_param_spec_int ("foo", "Foo", "Foo",
-1, G_MAXINT,
0,
G_PARAM_READWRITE);
properties[PROP_BAR] = g_param_spec_boolean ("bar", "Bar", "Bar",
FALSE,
G_PARAM_READWRITE);
properties[PROP_BAZ] = g_param_spec_string ("baz", "Baz", "Baz",
NULL,
G_PARAM_READWRITE);
gobject_class->set_property = test_object_set_property;
gobject_class->get_property = test_object_get_property;
gobject_class->finalize = test_object_finalize;
g_object_class_install_properties (gobject_class, N_PROPERTIES, properties);
}
static void
test_object_init (TestObject *self)
{
self->foo = 42;
self->bar = TRUE;
self->baz = g_strdup ("Hello");
}
static void
properties_install (void)
{
TestObject *obj = g_object_new (test_object_get_type (), NULL);
GParamSpec *pspec;
g_assert (properties[PROP_FOO] != NULL);
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (obj), "foo");
g_assert (properties[PROP_FOO] == pspec);
g_object_unref (obj);
}
typedef struct {
const gchar *name;
GParamSpec *pspec;
} TestNotifyClosure;
static void
on_notify (GObject *gobject,
GParamSpec *pspec,
TestNotifyClosure *clos)
{
g_assert (clos->pspec == pspec);
g_assert_cmpstr (clos->name, ==, pspec->name);
}
static void
properties_notify (void)
{
TestObject *obj = g_object_new (test_object_get_type (), NULL);
TestNotifyClosure clos;
g_assert (properties[PROP_FOO] != NULL);
clos.name = "foo";
clos.pspec = properties[PROP_FOO];
g_signal_connect (obj, "notify", G_CALLBACK (on_notify), &clos);
g_object_set (obj, "foo", 47, NULL);
g_object_unref (obj);
}
int
main (int argc, char *argv[])
{
g_type_init ();
g_test_init (&argc, &argv, NULL);
g_test_bug_base ("http://bugzilla.gnome.org/");
g_test_add_func ("/properties/install", properties_install);
g_test_add_func ("/properties/notify", properties_notify);
return g_test_run ();
}