halting problem :: The King is Dead

:: ~3 min read

I guess a lot of you, kind readers, are pretty well-acquainted with the current idiomatic way to write a GObject type. it’s the usual boilerplate, plus or minus a bunch of macros:

gobject-private-old.hdownload
typedef struct _MyObject MyObject;
typedef struct _MyObjectPrivate MyObjectPrivate;
typedef struct _MyObjectClass MyObjectClass;

// instance structure
struct _MyObject {
  GObject parent_instance;

  // pointer for quick access to instance private data
  MyObjectPrivate *priv;
};

// class structure
struct _MyObjectClass {
  GObjectClass parent_class;
};

GType my_object_get_type (void);
gobject-private-old.cdownload
struct _MyObjectPrivate
{
  int foo;
};

G_DEFINE_TYPE (MyObject, my_object, G_TYPE_OBJECT)

static void
my_object_class_init (MyObjectClass *klass)
{
  // tell the type system that the instance has private data
  g_type_class_add_private (klass, sizeof (MyObjectPrivate));
}

static void
my_object_init (MyObject *self)
{
  // G_TYPE_INSTANCE_GET_PRIVATE() is expensive, so we keep a back
  // pointer for quick access
  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
                                            my_object_get_type (),
                                            MyObjectPrivate);
  self->priv->foo = 42;
}

boring stuff that everyone had to remember. the last big change in the way people write GObject happened 10 years ago, and it was the addition of per-instance private data. it seems to me like a good way to celebrate that occasion to change this stuff all over again. ;-)

at the latest GTK+ hackfest, Alex and Ryan had a very evil, and very clever idea to solve a problem in how the per-instance private data is laid out in memory. before that, the layout was:

[[[GTypeInstance] GObject] TypeA] TypeB] [TypeAPrivate] [TypeBPrivate]

as you can see, the offset of the private data for each type changes depending at which point in the class hierarchy initialization we are, and can only be determined once the whole class hierarchy has been initialized. this makes retrieving the pointer of the private data a pretty hard problem; one way to solve it is storing the private pointer when we initialize the instance, and we spare ourselves from type checks and traversals. the main problem is that, in order to get to the private data faster, we need to rely on a specific layout of the instance structure, something that is not really nice if we want to have generic accessors to private data. for that, it would be really cool if we could only have offsets to through to G_STRUCT_MEMBER().

well, it turns out that if you’re doing memory allocations for the instance you can overallocate a bit, and return a pointer in the middle of the memory you allocated. you can actually allocate the whole private data in a decent layout, and only deal with offsets safely — after all, the type information will store all the offsets for safe access. so, here’s the new layout in memory of a GObject:

[TypeBPrivate] [TypeAPrivate] [[[[GTypeInstance] GObject] TypeA] TypeB]

that’s neat, isn’t it? now all private data can be accessed simply through offsets, and accessing it should be just as fast as a private pointer.

I can already see people using Valgrind preparing torches and pitchforks — but fear not, my fellow developers: GLib now detects if you’re running under Valgrind, and it will communicate with it about this new memory layout, as well as keeping a pointer to the beginning of the allocated region, so that you won’t get false positives in your report.

this was the state at the end of the hackfest. on top of that, I decided to contribute a bunch of “syntactic sugar” to cut down the amount of lines and things to remember, as well as providing a good base towards making GProperty work better, and with fewer headaches.

so, here’s how you create a new GObject type in the Brave New World™:

gobject-private-new.hdownload
typedef struct _MyObject MyObject;
typedef struct _MyObjectClass MyObjectClass;

struct _MyObject {
  GObject parent_instance;
};

struct _MyObjectClass {
  GObjectClass parent_class;
};

GType my_object_get_type (void);
gobject-private-new.cdownload
typedef struct {
  int foo;
} MyObjectPrivate;

// WITH_PRIVATE is like WITH_CODE (G_ADD_PRIVATE)
G_DEFINE_TYPE_WITH_PRIVATE (MyObject, my_object, G_TYPE_OBJECT)

static void
my_object_class_init (MyObjectClass *klass)
{
}

static void
my_object_init (MyObject *self)
{
  // get_instance_private() is just pointer arithmetic
  MyObjectPrivate *priv = my_object_get_instance_private (self);

  priv->foo = 42;
}

the my_object_get_instance_private() function is generated by G_DEFINE_TYPE(), so you can forget about G_TYPE_INSTANCE_GET_PRIVATE and all that jazz. also, no more g_type_class_add_private() — one less thing to remember is one less thing to screw up. you can still store the private pointer in your instance structure, but for newly written code it’s not necessary any more: getting the private data pointer is just pointer arithmetics, so you don’t pay any penalty. you can finally hide the private data structure inside your source code, instead of having the typedef in your header, sitting there, taunting you. finally, everything is just as fast as it was, as well as backward compatible.

development glib gobject

Follow me on Mastodon