a gentle introduction to gobject construction

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.

g_object_construct()
calls 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 base
  • GType _init() function of each class from base to most derived
  • _set_property() function for each construct property given to g_object_new(), in order
  • _set_property() function for each construct property not given to g_object_new()
  • constructor() override (after chainup) of each class from base to most derived
  • constructed() override (before chainup) of each class from most derived to base
  • constructed() override (after chainup) from each class from base to most derived
  • _set_property() for the remaining properties passed to g_object_new()
  • whatever code you do in your _new() function after g_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 your GType _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 to g_object_new()). Consider using constructed() 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 by GObject 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 where GSettings 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.

10 Comments

  1. Posted February 26, 2012 at 6:45 pm | Permalink

    Great explanations. Shouldn’t this be included in developer.gnome.org?

  2. Matthew Barnes
    Posted February 26, 2012 at 7:26 pm | Permalink

    You should copy and paste this into the GObject API docs. Srsly.

    What’s there now doesn’t really reflect current best practices:
    http://developer.gnome.org/gobject/unstable/chapter-gobject.html#gobject-instantiation

  3. Posted February 26, 2012 at 9:19 pm | Permalink

    You touched on this but it should be made more explicit:

    If you want your class to instantiate right in binding languages and tools such as Glade don’t put any extra code in your _new convinience constuctors. Everything that needs to be setup should happen in your virtual methods or in set_property. Failure to do so means a simple call to g_object_new may not construct your object correctly.

  4. Tristan
    Posted February 27, 2012 at 3:13 am | Permalink

    One thing that I’ve always found mysterious about GObject is why non-construct properties exist at all. Why would you ever *not* want a default value to be set? Why would you ever *not* want the guarantee that the property is ready to use by the time you get to constructed()?

    Does anybody know the original motivation behind non-construct properties, or in what circumstances they should be used?

    • Posted February 27, 2012 at 6:04 am | Permalink

      i actually find the set-to-default behaviour to be somewhat obnoxious.

      the original patch for constructed() set the non-construct properties before the call, but it got shuffled around a bit before it got committed and we landed at the current suboptimal behaviour instead.

  5. Tristan
    Posted February 27, 2012 at 7:52 am | Permalink

    I forgot to say, the guide is excellent, thank you very much :-)

    Since it’s not ideal to do so in _init(), what’s the recommended way for subclasses to set different default property values from their parents? constructed() is too late, and trying to do so in constructor() in the middle of the chain-up sequence is just horrible.

    It’s easy to do by passing the new defaults to g_object_new() in the subclass’s _new() function, but that’s not ideal either…

  6. Andreas
    Posted February 29, 2012 at 12:08 pm | Permalink

    Thanks for the guide… :-)

    What I however keep wondering is how I can model the failure of object instaciation in GObject. A case that can happen especially when providing bindings to other libs…

    Could I e.g. return NULL from the construtor or can one of the properties be a GError** ?
    Currently I have my own _new(GError**), but this does not really feel correct…

    If you have an answer or best practice for such cases, that would be great !

    • Posted February 29, 2012 at 1:32 pm | Permalink

      hi Andreas,

      GObject construction never fails. Period. g_object_new() will always return a non-NULL pointer to you.

      That said, we have a very strong convention for your_obj_new() failing and an interface with conventions for this: GInitable. In short, you create the object with g_object_new() (always works) and then you call a failable initialiser on it (part of the GInitable interface). If that fails, then you return NULL from the your_obj_new() function (usually with a GError* with the reason). The reason we go about it this way is because we want you to be able to subclass objects with failable initialisation and that wouldn’t work if you just put the initialisation in the your_obj_new() functions. See http://developer.gnome.org/gio/stable/GInitable.html for the documentation.

      There is also GAsyncInitable as the convention for dealing with asynchronous (and failable) initialisation. It turns out that the cases that fail are often quite similar to the cases where you want asynchronous IO (ie: loading files, connecting sockets, etc).

  7. Andreas
    Posted February 29, 2012 at 2:29 pm | Permalink

    Thanks a lot for that clearification… Was hard to work out of the general tutorials… :-)

    Though I see the heavy use in GIO, I don’t understand, why GInitiable is defined in GIO… My guess would have been, that this may be interessting in GObject itself…

    What I also don’t really see is how to sensefully work around the name-conflict between the GInitiable_init and the init of the object subclassed from GInitiable…

    I guess it will require:
    my_object_init (): regular init stuff for my Object
    my_object_initiable_init (): the initiable virtual function implementation for my Object…
    with initiable_iface->init = my_class_initiable_init in the constructor of my Object…

    Or do I miss something here ?

    • Posted February 29, 2012 at 6:52 pm | Permalink

      That’s pretty much correct, with the minor detail that the vtable setup goes in the interface init function (which is separate from your normal class init function). Look up G_DEFINE_TYPE_WITH_CODE() and G_IMPLEMENT_INTERFACE(), btw, if you didn’t know about them already.