A GObject
is a GTypeInstance
.
GTypeInstance
has its own system of constructing and initialising instances. It’s very simple: you call g_type_create_instance()
. GType
allocates the necessary memory for the instance and then calls each _init()
function, from the base type to the most derived type. There are no properties here.
But we want to create a GObject
. That’s always done with g_object_new()
. The first thing that g_object_new()
does is to sort the properties that you pass to it into construct and non-construct properties. The list of construct properties is in the order that you passed them. Added to this list (at the end) are any construct properties that you did not pass to g_object_new()
, with their default values.
g_object_new()
then calls the constructor()
virtual function for your class with the list of construct properties. This is usually the internal function g_object_construct
, but you can override it (but you must chain up). This is the first place that you can possibly intercept construction of a GObject
.
calls
g_object_construct()g_type_create_instance()
which results in all the _init()
functions being called (as described above). It then sets the construct properties in the order that they were given to g_object_new()
(with the default values at the end). This results in you getting a lot of _set_property()
calls.
Then it returns. Your constructor()
override gets another chance to do things here if you want.
Back in g_object_new()
, the constructed()
virtual function is called to let you know that we’re (almost) done. If you implement this then you need to chain up.
Finally, the non-construct properties that you passed to g_object_new()
are set in the order passed. More _set_property()
calls for you.
Then g_object_new()
returns. A lot of people take this chance to do some extra things in their C API constructor function (but binding authors and those who want to subclass you may get angry if you do that).
The long and the short of it is that there are a bunch of different parts of your code that you can hook into to do various things during object construction (in order of when they are called):
constructor()
override (before chainup) of each class from most derived to baseGType
_init()
function of each class from base to most derived_set_property()
function for each construct property given tog_object_new()
, in order_set_property()
function for each construct property not given tog_object_new()
constructor()
override (after chainup) of each class from base to most derivedconstructed()
override (before chainup) of each class from most derived to baseconstructed()
override (after chainup) from each class from base to most derived_set_property()
for the remaining properties passed tog_object_new()
- whatever code you do in your
_new()
function afterg_object_new()
There are a few conclusion to draw from all of this that a lot of people first using GObject
are often surprised about:
- you do not have access to values of properties passed to
g_object_new()
(even construct properties) from yourGType
_init()
function. You should think of_init()
as doing the work necessary to make it safe for_set_property()
calls to begin happening to your object. _init()
is not a great place to set values for properties on your parent class. If those properties are construct properties then they will later be reset to their default values by the later call to_set_property()
(even if the property was not passed tog_object_new()
). Consider usingconstructed()
for this instead.- none-the-less, as a potential parent class, you should expect that your subclasses may attempt to set non-construct properties from their
GType
_init()
functions which means that you may end up seeing_set_property()
for non-construct properties before you see it for construct properties (delivered byGObject
in the usual way).
And a couple more notes and general advice:
- since
_init()
doesn’t have access to any properties, you should limit it to simple always-the-same initialisation tasks such as setting up your->priv
pointer or allocating internal arrays and hashtables or so on. - overriding
constructor()
is difficult and ugly. Attempting to manipulate the property array that gets passed through is even more complicated. You almost certainly don’t want to do this unless you are doing something exceptionally evil. constructed()
is easy to override and at this point all your construct properties have been set. This is a good place to do the ‘heavy lifting’ of initialising your object that is dependent on construct properties. This is whereGSettings
opens its schemas, for example.constructed()
is somewhat less useful than it would have been if it was called after all properties (not just construct-only ones) were set, but we probably can’t change it at this point.