Re: GProperty -- a proposal on top of GParamSpec (with code)



Hi Rob,
Am 06.12.2010 11:46, schrieb Rob Staudinger:
> Hello,
> 
> I've written out so many gobject property handlers in the last few
> years, and they are always looking similar, so the desire to prune some
> boilerplate code has been growing. The idea behind GProperty (actually
> G<Foo>Property) is to subclass a GParamSpec<Foo> and add data and code
> so the property can update the object it applies to itself. For e.g. a
> string property this would of course include freeing the previous value.
> 

You should also post this to bugzilla. I have been looking for this in bugzilla
now for quite some time. I am not sure yet, how much it will help. I small
benefit could be that it allows to supress notifies if the value has not changed
on a g_object_set.

Stefan


> What does GProperty offer over GParamSpec?
> * Remove the need for implementing GObject::get_property()
> and ::set_property() on your object (see "Boxed property access").
> * Remove the need for an enum of property-ids.
> * Remove the need for explicit property accessors with just a small
> amount of extra boilerplate code (see "Unboxed property access").
> * By subclassing GParamSpec, full compatibility is retained.
> 
> My prototype [1] is currently using the Egg namespace, implementing
> EggBooleanProperty, EggDoubleProperty, EggIntProperty and
> EggStringProperty with examples that demonstrate usage. The following
> code snippets will use EggStringProperty for being easier to read than
> "<Foo>" all over the place.
> 
> If this approach is adjudged positively I would like to import it into
> git.gnome.org's libegg module for hashing out the details and finishing
> up the implementation. Feedback would be appreciated.
> 
> 
> Boxed property access
> =====================
> 
> Let's look at how the API is going to be used first:
> 
>     typedef struct {
>       gchar *string_foo;
>     } TestOffsetPrivate;
> 
>     static void
>     test_offset_class_init (TestOffsetClass *klass)
>     {
>       GObjectClass  *object_class = G_OBJECT_CLASS (klass);
>       gpointer       pspec;
>       guint          id = 0;
> 
>       g_type_class_add_private (klass, sizeof (TestOffsetPrivate));
> 
>       object_class->get_property = egg_object_get_property_impl;
>       object_class->set_property = egg_object_set_property_impl;
> 
>       pspec = egg_string_property_for_field (
>                                   "string-foo",
>                                    G_STRUCT_OFFSET (TestOffsetPrivate,
>                                                     string_foo),
>                                    NULL,
>                                    G_PARAM_READWRITE);
>       g_object_class_install_property (object_class, ++id, pspec);
>     }
> 
> * EggStringProperty is a subclass of GParamSpecString and implements
> EggProperty:
> 
>     typedef struct {
>       GTypeInterface parent;
>       void (*set_value) (EggProperty *p, GObject *o, const GValue *v);
>       void (*get_value) (EggProperty *p, GObject *o, GValue *v);
>     } EggPropertyInterface;
> 
> * The generic GObject::get_property() and ::set_property()
> implementations egg_object_{get,set}_property_impl() are dispatching
> access through g_object_{get,set}() to the EggProperty interface. This
> makes per-class overrides and property-id enums obsolete.
> 
> * The egg_string_property_for_field() constructor takes the offset of
> the "string_foo" member in the private data blob so it knows where to
> look for the actual value in the implementation of the EggProperty
> interface methods. (Alternative property constructors are discussed
> further below.)
> 
> 
> Unboxed property access
> =======================
> 
> The code above covers boxed property access through the
> g_object_{get,set}() API. In addition, explicit property accessors are
> very common for their compile-time traits, as well as avoiding the
> boxing overhead. To that end, every property-class also implements
> unboxed accessors:
> 
>     typedef struct {
>       GParamSpecClass parent_class;
>       const gchar * (*get) (EggStringProperty *p, GObject *o);
>       void (*set) (EggStringProperty *p, GObject *o, const gchar *v);
>     } EggStringPropertyClass;
> 
> Together with unboxed accessors on GObject level we can retain a certain
> level of type safety (exactly how should become clearer just a bit
> further down):
> 
>     const gchar * egg_object_get_string (gpointer            object,
>                                          EggStringProperty  *property);
> 
>     void          egg_object_set_string (gpointer            object,
>                                          EggStringProperty  *property,
>                                          const gchar        *val);
> 
> 
> Putting it all together
> =======================
> 
> For a complete example how to use the GProperty API please look at [2]
> (files matching *unboxed-offset*).
> 
> Again, code snippet first, for a GObject called "TestUnboxedOffset":
> 
>   /* test-unboxed-offset.h */
> 
>     #define TEST_UNBOXED_OFFSET_AND_PROPERTY(obj, prop) \
>       (TEST_UNBOXED_OFFSET (obj)),                      \
>       (TEST_UNBOXED_OFFSET_GET_CLASS (obj)->prop)
> 
>     typedef struct {
>       GObjectClass parent;
>       EggStringProperty *string_foo;
>     } TestUnboxedOffsetClass;
> 
>   /* test-unboxed-offset.c */
> 
>     static void
>     test_unboxed_offset_class_init (TestUnboxedOffsetClass *klass)
>     {
>     ...
>       klass->string_foo = egg_string_property_for_field ("string-foo",
>     ...
>       g_object_class_install_property (object_class,
>                                        ++id,
>                                        klass->string_foo);
> 
>   /* test-unboxed-offset.c */
> 
>     egg_object_set_string (
>                 TEST_UNBOXED_OFFSET_AND_PROPERTY (object, string_foo),
>                 "foo");
> 
> In addition to g_object_class_install_property(), I would recommend to
> put a pointer to the property into the class structure. This makes sure
> the property is immediately at hand with an object instance. A (slightly
> quirky) new boilerplate macro like TEST_UNBOXED_OFFSET_AND_PROPERTY that
> produces two values (object and property) should help keeping the RSI
> under control. Accessing a property like this checks property name and
> type at compile time, so it's pretty much as good as an explicit setter
> test_unboxed_offset_set_string_foo() would be.
> 
> Notes
> =====
> 
> == Overriding properties
> 
> I have not investigated this in depth yet, but it should be possible to
> override a property by also overriding the property pointer in the
> parent's class, in addition to g_object_class_override_property().
> 
> == Alternative property constructors
> 
> At this point there are three flavours of properties in place:
> 
> * Properties operating on a field in the object's private data blob --
> see the *unboxed-offset* and *offset* examples.
> 
> * Properties proxying a property of an aggregate object -- see the
> *proxy* examples.
> 
> * Properties based on custom accessors, getter and setter function have
> to be passed to the property constructor -- see the *accessors*
> examples.
> 
> == Code snippets in this mail
> 
> A few instances of "const" qualifiers have been dropped and variable
> names have been changed for more compact formatting.
> 
> 
> [1] http://gitorious.org/egg-gobject
> [2] http://gitorious.org/egg-gobject/egg-gobject/trees/master/examples/
> 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]