Object properties

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 g_object_class_install_properties.

The best way to understand how object properties work is by looking at a real example
of how it is used:

The client code above looks simple but a lot of things happen under the hood:

g_object_set_property first ensures a property
with this name was registered in file's class_init handler. If so it walks the class hierarchy,
from bottom-most most-derived type, to top-most fundamental type to find the class
which registered that property. It then tries to convert the user-provided
GValue
into a GValue whose type is that of the associated property.

If the user provides a signed charGValue, as is shown
here, and if the object's property was registered as an unsigned int,
g_value_transform will try to transform the input signed char into
an unsigned int. Of course, the success of the transformation depends on the availability
of the required transform function. In practice, there will almost always be a transformation
[2]
which matches and conversion will be carried out if needed.

After transformation, the GValue is validated by
g_param_value_validate which makes sure the user's
data stored in the GValue matches the characteristics specified by
the property's GParamSpec.
Here, the GParamSpec we
provided in class_init has a validation function which makes sure that the GValue
contains a value which respects the minimum and maximum bounds of the
GParamSpec. In the example above, the client's GValue does not
respect these constraints (it is set to 11, while the maximum is 10). As such, the
g_object_set_property function will return with an error.

If the user's GValue had been set to a valid value, g_object_set_property
would have proceeded with calling the object's
set_property class method. Here, since our
implementation of ViewerFile did override this method, execution would jump to
viewer_file_set_property after having retrieved from the
GParamSpec the param_id[3]
which had been stored by
g_object_class_install_property.

Once the property has been set by the object's
set_property class method, execution
returns to g_object_set_property which makes sure that
the "notify" signal is emitted on the object's instance with the changed property as
parameter unless notifications were frozen by g_object_freeze_notify.

g_object_thaw_notify can be used to re-enable notification of
property modifications through the
“notify” signal. It is important to remember that
even if properties are changed while property change notification is frozen, the "notify"
signal will be emitted once for each of these changed properties as soon as the property
change notification is thawed: no property change is lost for the "notify"
signal, although multiple notifications for a single property are
compressed. Signals can only be delayed by the notification freezing
mechanism.

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 g_object_set_property
and g_object_get_property
are meant to be used by language bindings. For application there is an easier way and
that is described next.

Accessing multiple properties at once

It is interesting to note that the g_object_set and
g_object_set_valist (variadic version) functions can be used to set
multiple properties at once. The client code shown above can then be re-written as:

These high level functions have one drawback — they don't provide a return value.
One should pay attention to the argument types and ranges when using them.
A known source of errors is to pass a different type from what the
property expects; for instance, passing an integer when the property
expects a floating point value and thus shifting all subsequent parameters
by some number of bytes. Also forgetting the terminating
NULL will lead to undefined behaviour.

This explains how g_object_new,
g_object_newv and g_object_new_valist
work: they parse the user-provided variable number of parameters and invoke
g_object_set on the parameters only after the object has been successfully constructed.
The "notify" signal will be emitted for each property set.

[2] Its behaviour might not be what you expect but it is up to you to actually avoid
relying on these transformations.

[3]
It should be noted that the param_id used here need only to uniquely identify each
GParamSpec within the ViewerFileClass 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.