The only two of these that you really need to care about unless you're attempting to work on some very low level code are GValue and GParamType
I'll start with GParamType
GParamType
is for used for registering a property with a GObject. Say, for example, I have a GObject subclass called Person, and I wanted it to have two properties: Name and Age. In the class_init
function I would register these like so
{
GParamSpec *pspec;
. . .
pspec = g_param_spec_string ("name", "Name", "The name of the person", "", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (object_class, PROP_NAME, pspec);
pspec = g_param_spec_int ("age", "Age", "The age of the person", 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (object_class, PROP_AGE, spec);
. . .
}
Now you can call g_object_get
or g_object_set
on those properties and the system will know how to handle it
char *name;
int age;
g_object_set (G_OBJECT (person), "name", "Steve", "age", 37, NULL);
g_object_get (G_OBJECT (person), "name", &name, "age", &age, NULL);
g_print ("%s is %d years old\n", name, age);
// And because the type system knows when a property is a string, it knows how to give
// you a copy of the string, so you need to free it once you've finished with it
g_free (name);
The various parameters are explained here: GParamSpec There are GValue types for all the standard types: strings, bools, ints etc, and some other libraries such as GStreamer will register their own custom ones.
Outside of installing properties on GObjectClass you very rarely need to deal with GParamSpec. The two main occasions where they appear is in the GObjectClass set/get_property methods and the GObject notify signal. It is useful in the last case to detect which property has received the notify signal, by calling g_param_spec_get_name
, but really it's better to use a more specific notify signal like so:
g_signal_connect (person, "notify::name", G_CALLBACK (name_changed_cb), NULL);
g_signal_connect (person, "notify::age", G_CALLBACK (age_changed_cb), NULL);
rather than
g_signal_connect (person, "notify", G_CALLBACK (something_changed_cb), NULL);
Sometimes you may want to create your own structures and use those for the properties. For example if I had
struct _PersonDetails {
char *name;
int age;
}
and instead of having two properties on the Person object, I wanted one called "details". The GLib type system does not know how to deal with my custom struct _PersonDetails
so I would need to create a boxed type for it, so that it knew how to correctly copy/free the structure as it is passed around the Glib internals. And that is where GValue
comes in.
GValue
is for wrapping values of different types so they can be copied and freed correctly (if they need to be), and so that generic functions can be used.
For example, the GObjectClass method set_property has the prototype of
void set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
This means that any type which can be represented by a GValue can be passed in and specific functions such as set_int_property, set_string_property, set_bool_property are not required.
It also means that the functions g_object_set
and g_object_get
know how to deal with the parameters that are passed in because it knows that the property "name" is registered to be a string type, and it has the functions necessary to copy/free that string.
More about GValue can be found here - Generic values
To register our custom struct _PersonDetails
with the GLib type system we would create a custom Boxed type which told the system how to copy and free it. The details are here: Boxed Types
G_DEFINE_BOXED_TYPE (PersonDetails, person_details,
person_details_copy,
person_details_free)
. . .
static gpointer
person_details_copy (gpointer data)
{
struct _PersonDetails *details = (struct _PersonDetails *)data;
struct _PersonDetails *copy = g_new (struct _PersonDetails, 1);
// We need to copy the string
copy->name = g_strdup (details->name);
copy->age = details->age;
return (gpointer) copy;
}
static void
person_details_free (gpointer data)
{
struct _PersonDetails *details = (struct _PersonDetails *)data;
// name was allocated so it needs freed as well
g_free (details->name);
g_free (details);
}
Now we can register our type using
pspec = g_param_spec_boxed ("details", "Details", "The person's details", person_details_get_type (), G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (object_class, PROP_DETAILS, pspec);