mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-03-03 22:52:09 +01:00
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:
parent
58a40904af
commit
9cd43d7a4c
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
1
gobject/tests/.gitignore
vendored
1
gobject/tests/.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
binding
|
binding
|
||||||
dynamictests
|
dynamictests
|
||||||
|
properties
|
||||||
threadtests
|
threadtests
|
||||||
|
@ -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
222
gobject/tests/properties.c
Normal 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 ();
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user