GProperty — GObject properties made easy

Implementing GObject properties may be considered cumbersome to the uninitiated, but once you’ve got a few implementations under your belt it’s actually quite repetitive boilerplate code. On top of that, if you want to provide explicit property accessors in addition to the g_object_get/set API, they need to be implemented as well, which means more of the same. It would be nice to implement the boilerplate code once for all, so here comes …

GProperty is a subclass of GParamSpec¹ that provides common ways of implementing properties. And by being based on GParamSpec it retains compatibility and works exactly like expected when looking at a GObject from the outside. The proof of concept implementation also provides egg_object_get_property() and egg_object_set_property() GObject methods, that dispatch property access to the respective GProperty.

Field-based properties

Probably the most common way is to store the value of a property in a field of the object’s private data blob. GProperty provides a ready-made implementation that manages access to that field. If you need to react on property changes you’d just hook up to the property-changed notification.

The following snippet implements an int and a string property backed by the respective field in the private struct. No need for a property-id enum or implementation of g_object_get/set_property().

typedef struct
{
  gint   number;
  gchar *text;
} EggTestOffsetPrivate;

static void
egg_test_offset_class_init (EggTestOffsetClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  GParamSpec  *pspec;
  guint        id = 0;

  g_type_class_add_private (klass, sizeof (EggTestOffsetPrivate));

  object_class->get_property = egg_object_get_property;
  object_class->set_property = egg_object_set_property;

  pspec = egg_int_property_for_field ("number",
                                      G_STRUCT_OFFSET (EggTestOffsetPrivate, number),
                                      G_MININT32, G_MAXINT32, 1,
                                      G_PARAM_READWRITE);
  g_object_class_install_property (object_class, ++id, pspec);

  pspec = egg_string_property_for_field ("text",
                                         G_STRUCT_OFFSET (EggTestOffsetPrivate, text),
                                         NULL,
                                         G_PARAM_READWRITE);
  g_object_class_install_property (object_class, ++id, pspec);
}

Accessor-based properties

Another way of using GProperty is with accessor functions that are explicitly managing a property. The following snippet creates GProperties that are dispatching access from g_object_get/set() to the custom accessors, again saving custom g_object_get/set_property() overrides.

typedef struct
{
  gint   number;
  gchar *text;
} EggTestAccessorsPrivate;

static void
egg_test_accessors_class_init (EggTestAccessorsClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  GParamSpec  *pspec;
  guint        id = 0;

  g_type_class_add_private (klass, sizeof (EggTestAccessorsPrivate));

  object_class->get_property = egg_object_get_property;
  object_class->set_property = egg_object_set_property;

  pspec = egg_int_property_for_accessors (
            "number",
            (EggIntPropertyGetterFunc) egg_test_accessors_get_number,
            (EggIntPropertySetterFunc) egg_test_accessors_set_number,
            G_MININT32, G_MAXINT32, 1,
            G_PARAM_READWRITE);
  g_object_class_install_property (object_class, ++id, pspec);

  pspec = egg_string_property_for_accessors (
            "text",
            (EggStringPropertyGetterFunc) egg_test_accessors_get_text,
            (EggStringPropertySetterFunc) egg_test_accessors_set_text,
            NULL,
            G_PARAM_READWRITE);
  g_object_class_install_property (object_class, ++id, pspec);
}

/* standard getter and setter functions omitted */

Property proxies

Maybe slightly more obscure than the above, but hopefully still useful are proxied properties. The goal is to transparently export properties of an aggregate object to the outside.

The following snippet implements a custom gtk widget using a button and spinner inside a hbox, and exports the button’s label and the spinner’s value as properties of the toplevel hbox subclass.

typedef struct
{
  GtkWidget *button;
  GtkWidget *spinner;
} EggTestProxyPrivate;

static void
egg_test_proxy_class_init (EggTestProxyClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  GParamSpec  *pspec;
  guint        id = 0;

  g_type_class_add_private (klass, sizeof (EggTestProxyPrivate));

  object_class->get_property = egg_object_get_property;
  object_class->set_property = egg_object_set_property;

  pspec = egg_string_property_proxy_for_object_offset (
                      "label",
                      G_STRUCT_OFFSET (EggTestProxyPrivate, button),
                      "label",
                      NULL,
                      G_PARAM_READWRITE);
  g_object_class_install_property (object_class, ++id, pspec);

  pspec = egg_double_property_proxy_for_object_offset (
                      "value",
                      G_STRUCT_OFFSET (EggTestProxyPrivate, spinner),
                      "value",
                      0.0, 10.0, 1.0,
                      G_PARAM_READWRITE);
  g_object_class_install_property (object_class, ++id, pspec);
}

The code with full examples can be found at http://gitorious.org/egg-gobject At the moment only properties of type double, int and string are implemented, but chances for broader support are not all that bad. If feedback is positive I’d like to consider migration to libegg in gnome’s git.

¹ actually G<Foo>Property is a subclass of GParamSpec<Foo>

If I had five wishes … to (GC)C maintainers

C puts the bread on the table for many of us, so every once in a while, when not just bashing C++, endulging in the awesomeness of Ruby, or wondering why PHP is still powering a good deal of the modern interwebs, the topic comes to C, its greatness, and how C99 is still not ubiquitous.

Personally I think, with the year being 2010 and all, just five humble features would make a whole lot of difference and put the language firmly in the 21st century, while not sacrificing any of the spirit of being a high-level assembly language. So here goes (in no particular order) …

Extended support for anonymous aggregates

C99’s anonymous aggregates are useful, for example when passing a one-off compound datatype to a function. What would be even more helpful would be the possibility to return an anonymous aggregate from a function, this would essentially allow returning multiple values:

struct { float width; float height; }
clutter_actor_get_size (ClutterActor *actor) {
  struct { float width; float height; } size;
  /* Fill size.width and size.height */
  return size;
}
[...]
struct { float width; float height; } size;
size = clutter_actor_get_size (actor);

This feature would be even more helpful with

Variable declarations using “auto”

When a variable is initialised in place it should be possible for today’s compilers to figure out the data type, there’s even things like the “L” suffix to numbers for specifying the desired range. Leveraging “auto” the above example would become a bit less verbose:

struct { float width; float height; }
clutter_actor_get_size (ClutterActor *actor) {
  struct { float width; float height; } size;
  /* Fill size.width and size.height */
  return size;
}
[...]
auto size = clutter_actor_get_size (actor);

Lambda functions

Along the lines of anonymous aggregates it would seem very natural to do lambda functions by basically “casting” a block of statements. The formal parameters would be derived from the “cast” operator. This approach, in my opinion, would be more natural to C than llvm’s block syntax using ^.

clutter_container_foreach (container,
                           (void (*)(ClutterActor *actor, gpointer data))
                           {
                             printf ("%s\n", clutter_actor_get_name (actor));
                           },
                           NULL);

Type extension

GObject based C code is typically interspersed with type casts, but this does not seem strictly necessary from a semantic point of view. A pointer to a compound instance in C is by definition also a pointer to the first attribute. It should be fairly straight forward to account for that in the compiler, and thus allow for implicit “upcasting”, i.e. assigning a pointer of a “derived” type to a pointer of type of an (first-member) embedded struct. There would be no need for the C compiler to warn about pointer types not matching, because the example is actually semantically correct.

/* Lots of GObject boilerplate code omitted. */
typedef struct {
  ClutterActor parent;
} FooActor;
[...]
FooActor *actor = foo_actor_new ();
clutter_actor_set_x (actor, 100.0);

An #import preprocessor directive

In this day and age it seems redundant having to type function signatures twice, once in the header and once again the the C file. It would be very handy if preprocessors could import symbols from other C files, without doing the verbatim insertion that is #include. For libraries which want to install headers I would imagine a compiler option that extracts definements and non-static symbols from a C file, possibly supporting filtering on prefixes, so the headers could be generated on the fly by the build system.

That might do for the next 30 years or so, I suppose.