mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-03-03 14:42:10 +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_NAME
|
||||
g_object_class_install_property
|
||||
g_object_class_install_properties
|
||||
g_object_class_find_property
|
||||
g_object_class_list_properties
|
||||
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);
|
||||
}
|
||||
|
||||
static void
|
||||
static inline void
|
||||
install_property_internal (GType g_type,
|
||||
guint property_id,
|
||||
GParamSpec *pspec)
|
||||
@ -444,7 +444,7 @@ g_object_class_install_property (GObjectClass *class,
|
||||
if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
|
||||
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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_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,
|
||||
guint property_id,
|
||||
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,
|
||||
GParamSpec *pspec);
|
||||
|
@ -140,6 +140,7 @@ g_initially_unowned_get_type
|
||||
g_object_add_weak_pointer
|
||||
g_object_class_find_property
|
||||
g_object_class_install_property
|
||||
g_object_class_install_properties
|
||||
g_object_class_list_properties
|
||||
g_object_class_override_property
|
||||
g_object_connect G_GNUC_NULL_TERMINATED
|
||||
|
1
gobject/tests/.gitignore
vendored
1
gobject/tests/.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
binding
|
||||
dynamictests
|
||||
properties
|
||||
threadtests
|
||||
|
@ -5,10 +5,12 @@ INCLUDES = -g $(gobject_INCLUDES) $(GLIB_DEBUG_FLAGS)
|
||||
noinst_PROGRAMS = $(TEST_PROGS)
|
||||
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_LDADD = $(libgobject_LDADD)
|
||||
dynamictests_SOURCES = dynamictests.c
|
||||
dynamictests_LDADD = $(libgobject_LDADD)
|
||||
binding_SOURCES = binding.c
|
||||
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