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:
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);
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™:
typedef struct _MyObject MyObject;
typedef struct _MyObjectClass MyObjectClass;
struct _MyObject {
GObject parent_instance;
};
struct _MyObjectClass {
GObjectClass parent_class;
};
GType my_object_get_type (void);
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.