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.

Sprout and the Bean

i returned from the gtk+ hackfest in brno a few days ago.

the biggest thing we dealt with is touch. you can probably read about that on various other blogs. we’re taking a somewhat minimalist approach this cycle with big plans for the future: event controllers. those are bits of code that treat sequences of events as higher level ideas. for example, a press-release mouse button sequence without substantial pointer movement is a “click”. this is somewhat different from our current model where it’s left to individual widgets to determine what constitutes this sort of interaction based on separate delivery of the individual events. naturally, this is almost the same idea as the philosophy of utouch: that most interesting touch interactions are gestures. this should make future integration of gtk and utouch rather natural.

i had a chance to meet with marek about dconf working better with nfs home directories. he’s working hard to make sure this will be working very nicely soon. i talked to emmanuele about gproperty and we came up with a solution for how we could improve the performance to the point where it’s improving all use cases and regressing none while providing what i believe to be an easier API for implementors.

the idea for removing support for grabs from gtk was met with general support. implicit grabs will be supported of course (like the sort of grab that happens when you click the mouse down until the point that you release it). the only sort of explicit grab behaviour that we care to support is the ‘popup menu’ case where you want a window to be dismissed when you click away from it. this will be offered as a simple boolean property on GtkWindow and the implementation will be done in the best way for each platform. there was an idea that we could even have a window manager hint to help us do this on X without the use of grabs (or other evil hacks like fullscreen input-only windows). there is presently nobody to tackle this task, but i may be able to find some time.

we’re going to deprecate gdk threads next cycle. (yay!)

we’re going to deprecate gtk_dialog_run() next cycle. (yay!)

there’s the increasing idea that we want to move away from using atk for accessibility purposes. it would be more efficient if we spoke to dbus directly, using our own abstraction that was kept in-tree and suited to our widgets. this abstraction could then also be made to support gtk accessibility on windows and mac in the future (instead of having an abstraction around an abstraction as would be the case if we tried to include atk as another option there). the plan for now is that it would continue to speak the same dbus protocol (which puts us pretty close to what qt is doing in this area).

i also spent some time hanging out with the documentation team who was having a hackfest next door. there seems to be a big change in focus there from working on user help to working on developer documentation (which we desperately need more of). gnome is pretty easy to use already, but i can’t tell you how many times i’ve heard that it’s difficult to develop for due to the lack of good documentation for beginners. hopefully good things will be coming soon.

air canada +1, british air -1000

this is one of those “never do business with again” posts that i promised myself i’d write. so here we go:

avoid british air at all costs.

the story goes like this: i’m travelling to budapest from toronto with heathrow in the middle. air canada takes my two bags (a gym bag and a backpack) for the first leg. i checked both just because i didn’t want to have the extra bag on the plane and air canada allows for it. the bags were tagged straight through to budapest.

when i get to heathrow i have to pick up my boarding card. british air is my connecting flight and they inform me that their allowance is one bag and there will be an extra charge. i tell them that the bags are checked through and i expect them to be carried through. no? well, fine. just *give* me one of my bags and i’ll take it as a carryon, then (either one would have been small enough). i’m informed that the only way that i can access my bags in heathrow is if i cancel my ticket (no refund, of course), cancelling my return flight home as a side-effect.

i talked to various managers and made sure i got very clear answers. one guy in particular (‘chris’) seemed to be taking extreme pleasure in the fact that ‘he was in charge here’ and was ruining my day. everyone seemed to agree on the fact that i had exactly two choices in this situation:

  • pay 35£ for the privilege of boarding a flight that i’d already paid for
  • be stranded in heathrow with all my flights cancelled

at this point i was quite enraged. i wanted nothing to do with those fuckers anymore. i went to the air canada desk and asked if they could book me on a connecting flight with one of their partners so that i could avoid ever having to look at british airways ever again in my life. i was willing to pay for this.

air canada informed me that what british airways is doing is certainly immoral, probably illegal, and that they are the only airline that does it. i was certainly shocked as hell to learn that it was possible to be in an “insert more money to continue” situation in the middle of a connection. air canada also told me to not bother wasting my money on a rebooking — they’d cover the extra charge that british airways was extracting from me. i got an incident number from an agent at the air canada desk and was told to email my details to air canada’s refund services along with the number.

i paid ba and told them that this would be the last of my money they’d ever see. i thanked the girl at the desk (who had an honest “i’m sorry, but i just work here” look) and told her to tell her manager to go fuck himself.

i emailed air canada, they wrote back once asking for a scan of the receipt from ba and three weeks later i had a cheque in the mail (for the exact amount of the charge as it appeared on my visa bill, in canadian dollars).

so, for those keeping score:

air canada +1.

british airways -1000.