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).