GObject library is really awfully documented. It's damn hard to figure the purposes of entities created. Namely, I don't get the roles of GValue
, GTypeValueTable
, GTypeInfo
, GParamSpec
and TypeData
.
In brief, the process of type registration is as follows. Each type is represented by a TypeNode
structure. There are 2 storages of TypeNode
structures: static_fundamental_type_nodes array
for storing TypeNodes
of static fundamental types and static_type_nodes_ht
hash table for static non-fundamental types. Each GType
is just the memory address of the corresponding TypeNode
in case of non-fundamental types or index of TypeNode
in static_fundamental_type_nodes
in case of fundamental types. What happens to dynamic types - I don't know, please explain me if you can. The corresponding code resides in gtype_init function, responsible for initialization of the type system: http://git.gnome.org/browse/glib/tree/gobject/gtype.c#n4323.
GValue
, GParamSpec
and GObject
are GTypes
themselves, so they are registered as types.
GValue
is meant used to register new type values through it, but how?.
GParameters
and GParamSpec
seem to be required for registering GObject
type (not sure). How exactly it is done? What are the roles of each?
MOST IMPORTANTLY: What are the roles of GTypeValueTable
, GTypeInfo
and TypeData
? TypeData
is referrenced by TypeNode
and contains GTypeValueTable
as well as substructures BoxedData, ClassData, IFaceData, InstanceData (why Instance, aren't we registering type?). Moreover, they seems to duplicate each other, cause ALL of them contain references to base_init/finalize, class_init/finalize has a reference to GTypeValueTable
.
So, GObject papas, if you're reading this, please, explain yourselves! Describe the purpose of those structures you use.
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With