One of GObject's nice features is its generic get/set mechanism for object
properties. When an object
is instantiated, the object's class_init handler should be used to register
the object's properties with The best way to understand how object properties work is by looking at a real example on how it is used:
/************************************************/
/* Implementation */
/************************************************/
enum
{
PROP_0,
PROP_MAMAN_NAME,
PROP_PAPA_NUMBER
};
static void
maman_bar_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
MamanBar *self = MAMAN_BAR (object);
switch (property_id)
{
case PROP_MAMAN_NAME:
g_free (self->priv->name);
self->priv->name = g_value_dup_string (value);
g_print ("maman: %s\n", self->priv->name);
break;
case PROP_PAPA_NUMBER:
self->priv->papa_number = g_value_get_uchar (value);
g_print ("papa: %u\n", self->priv->papa_number);
break;
default:
/* We don't have any other property... */
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
maman_bar_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
MamanBar *self = MAMAN_BAR (object);
switch (property_id)
{
case PROP_MAMAN_NAME:
g_value_set_string (value, self->priv->name);
break;
case PROP_PAPA_NUMBER:
g_value_set_uchar (value, self->priv->papa_number);
break;
default:
/* We don't have any other property... */
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
maman_bar_class_init (MamanBarClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
GParamSpec *pspec;
gobject_class->set_property = maman_bar_set_property;
gobject_class->get_property = maman_bar_get_property;
pspec = g_param_spec_string ("maman-name",
"Maman construct prop",
"Set maman's name",
"no-name-set" /* default value */,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
g_object_class_install_property (gobject_class,
PROP_MAMAN_NAME_NAME,
pspec);
pspec = g_param_spec_uchar ("papa-number",
"Number of current Papa",
"Set/Get papa's number",
0 /* minimum value */,
10 /* maximum value */,
2 /* default value */,
G_PARAM_READWRITE);
g_object_class_install_property (gobject_class,
PROP_PAPA_NUMBER,
pspec);
}
/************************************************/
/* Use */
/************************************************/
GObject *bar;
GValue val = { 0, };
bar = g_object_new (MAMAN_TYPE_SUBBAR, NULL);
g_value_init (&val, G_TYPE_CHAR);
g_value_set_char (&val, 11);
g_object_set_property (G_OBJECT (bar), "papa-number", &val);
g_value_unset (&val);
The client code just above looks simple but a lot of things happen under the hood:
If the user provides a signed char GValue, as is shown
here, and if the object's property was registered as an unsigned int,
After transformation, the GValue is validated by
If the user's GValue had been set to a valid value,
Once the property has been set by the object's set_property class method, the code path
returns to
It sounds like a tedious task to set up GValues every time when one wants to modify a property.
In practice one will rarely do this. The functions
It is interesting to note that the
MamanBar *foo;
foo = /* */;
g_object_set (G_OBJECT (foo),
"papa-number", 2,
"maman-name", "test",
NULL);
This saves us from managing the GValues that we were needing to handle when using
Of course, the _get versions are also available: These high level functions have one drawback - they don't provide a return result. One should pay attention to the argument types and ranges when using them. A know source of errors is to e.g. pass a gfloat instead of a gdouble and thus shifting all subsequent parameters by four bytes. Also forgetting the terminating NULL will lead to unexpected behaviour.
Really attentive readers now understand how [6] Its behaviour might not be what you expect but it is up to you to actually avoid relying on these transformations. [7] It should be noted that the param_id used here need only to uniquely identify each GParamSpec within the FooClass such that the switch used in the set and get methods actually works. Of course, this locally-unique integer is purely an optimization: it would have been possible to use a set of if (strcmp (a, b) == 0) {} else if (strcmp (a, b) == 0) {} statements. |