The King is Dead

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:

// header
typedef struct _MyObject MyObject;
typedef struct _MyObjectPrivate MyObjectPrivate;
typedef struct _MyObjectClass MyObjectClass;

struct _MyObject {
  GObject parent_instance;
  MyObjectPrivate *priv;
};

struct _MyObjectClass {
  GObjectClass parent_class;
};

GType my_object_get_type (void);

// source
struct _MyObjectPrivate
{
  int foo;
};

G_DEFINE_TYPE (MyObject, my_object, G_TYPE_OBJECT)

static void
my_object_class_init (MyObjectClass *klass)
{
  g_type_class_add_private (klass, sizeof (MyObjectPrivate));
}

static void
my_object_init (MyObject *self)
{
  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
                                            my_object_get_type (),
                                            MyObjectPrivate);
  self->priv->foo = 42;
}

boring stuff that everyone had to remember1. 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 data2. 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 GObject3:

[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 it4 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”5 to cut down the amount of lines and things to remember6, 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:

// header
typedef struct _MyObject MyObject;
typedef struct _MyObjectClass MyObjectClass;

struct _MyObject {
  GObject parent_instance;
};

struct _MyObjectClass {
  GObjectClass parent_class;
};

GType my_object_get_type (void);

// source
typedef struct {
  int foo;
} MyObjectPrivate;

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)
{
  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 — and if you care about ABI compatibility, you really should — but for new code it’s not necessary. 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.

stay tuned for the next blog post, because it’ll finally be about GProperty…

  1. or commit to a script to autogenerate it []
  2. say, for instance, if we’re re-implementing the way properties are handled in GObject []
  3. well, of any GTypeInstance, really []
  4. yes, you can do that, and it’s an impressive amount of crack, luckily for us hidden behind the valgrind.h header provided by the Valgrind folks []
  5. i.e. pre-processor macros []
  6. the port of GIO to the new macros lost around 900 lines []

This is Why We Fight

one thing that bugged me since the implementation of the Clutter animation framework in its current form was the property access API in GObject — especially the boxing/unboxing of GValues. since GLib 2.28 2.24 we had a new macro (G_VALUE_COLLECT_INIT) that avoided doing unnecessary work, but the end result of setting a property was still:

  • box it inside a GValue
  • call the set_property implementation of your class
  • unbox the GValue
  • call the public setter function
  • set the value inside the private data structure
  • free the GValue

there are at least two unnecessary steps, there: the boxing inside GValue should only be necessary for transformation purposes — i.e. taking a GValue and running the transformation function from the passed value type to the property type — but that path should not be taken for variadic arguments functions like g_object_set() since the type used to read the value from the va_list is the property type itself and no transformation is necessary or possble; and calling the set_property() implementation, which is just a huge switch on the property id used when installing the property on a class, just to then call the public accessors. now, try modifying five or so properties at the same time on a hundred objects at 60 frames per second. you can probably get away with it — but only just.

last year, Rob Staudinger proposed some code that provided property access through both accessor functions and direct structure member access through field offset. sadly, I felt that his API was still repeating the same issues of the GParamSpec API: constructors with different signatures (try remembering the arguments and their order between integer properties, enumeration properties, string and boxed properties); reliance on a big switch inside the class implementation; and a general lack of integration with GObject itself.

the last point is mostly what interested me; GParamSpec is meant to be used as a validation tool for a value; it describes the constraints, the default, the prerequisite type, etc.. it’s used to describe properties, but that’s almost a side-effect — a GParamSpec can exist in a vacuum, and can be used regardless of GObject itself. if we wanted to access an object property, the object class should be far more involved in this. we should be able to tie the property description to the structure field that backs it in the implementation; we should be able to define the functions that access it; and we should be able to even provide a default implementation of these accessor functions without requiring developers to actually write them.

another issue with the current state of affairs with GObject properties is the amount of code you have to write. let’s look at a simple class mapping a bunch of fields to properties. right now, you’d have to write every single accessor pair, with hand-written validation; you’d have to hard-code the defaults in three places (the GParamSpec, the instance init function and the accessors); and you’d have to write the set_property() and the get_property() implementation, which end up calling the public accessors anyway because you want to do validation and notification of changes in one place only.

almost all of this code could be autogenerated, even through the C pre-processor.

so, to make a long story short, during the past couple of weeks I started fleshing out the implementation of a new property API for GObject — GProperty.

GProperty is still based on GParamSpec, but it hides all the nasty so you only get a bunch of constructors with the same signature (no more trips to devhelp to check out the <code>g_param_spec_enum()</code> order of arguments) and a generic API for setting constraints like ranges, default values and prerequisite types. plus, you get to specify the field and/or the accessor functions:

  test_object_property[PROP_FOO] =
    g_int8_property_new ("foo", G_PROPERTY_READWRITE,
                         G_STRUCT_OFFSET (TestObjectPrivate, foo),
                         NULL, /* setter */
                         NULL  /* getter */);

  test_object_property[PROP_BAR] =
    g_float_property_new ("bar", G_PROPERTY_READWRITE,
                          -1,
                          test_object_set_bar,
                          test_object_get_bar);

  g_object_class_install_properties (object_class,
                                     G_N_ELEMENTS (test_object_property),
                                     test_object_property);

there are a bunch of other features (automatic atomic properties, macros for auto-generating accessors, per-class default value overriding) for which I’ll just point you again to the bug and the GLib branch; today, though, I wanted to verify whether one of the reasons for working on this — making generic property access faster — actually benefited from these changes. it turns out that it really does: calling g_object_set() in a tight loop with simple values (integers and floating point) improved from the equivalent, GParamSpec-based classic implementation by 2.10x on my Core 2 Duo; if you throw a string into the mix, the improvement is by 1.5x. my initial ballpark figures were way smaller, so I was pleasantly surprised.

hopefully, this work will get more eyeballs during this cycle — and it’ll land in time for 2.32 (and Gnome 3.2).