|  |  |  | GObject Reference Manual |  | 
|---|
      One of GObject's nice features is its generic get/set mechanism for object
      properties. When an object
      is instanciated, the object's class_init handler should be used to register
      the object's properties with g_object_class_install_property
      (implemented in gobject.c).
    
The best way to understand how object properties work is by looking at a real example on how it is used:
/************************************************/
/* Implementation                               */
/************************************************/
enum {
  MAMAN_BAR_CONSTRUCT_NAME = 1,
  MAMAN_BAR_PAPA_NUMBER,
};
static void
maman_bar_instance_init (GTypeInstance   *instance,
                         gpointer         g_class)
{
  MamanBar *self = (MamanBar *)instance;
}
static void
maman_bar_set_property (GObject      *object,
                        guint         property_id,
                        const GValue *value,
                        GParamSpec   *pspec)
{
  MamanBar *self = (MamanBar *) object;
  switch (property_id) {
  case MAMAN_BAR_CONSTRUCT_NAME: {
    g_free (self->private->name);
    self->private->name = g_value_dup_string (value);
    g_print ("maman: %s\n",self->private->name);
  }
    break;
  case MAMAN_BAR_PAPA_NUMBER: {
    self->private->papa_number = g_value_get_uchar (value);
    g_print ("papa: %u\n",self->private->papa_number);
  }
    break;
  default:
    /* We don't have any other property... */
    G_OBJECT_WARN_INVALID_PROPERTY_ID(object,property_id,pspec);
    break;
  }
}
static void
maman_bar_get_property (GObject      *object,
                        guint         property_id,
                        GValue       *value,
                        GParamSpec   *pspec)
{
  MamanBar *self = (MamanBar *) object;
  switch (property_id) {
  case MAMAN_BAR_CONSTRUCT_NAME: {
    g_value_set_string (value, self->private->name);
  }
    break;
  case MAMAN_BAR_PAPA_NUMBER: {
    g_value_set_uchar (value, self->private->papa_number);
  }
    break;
  default:
    /* We don't have any other property... */
    G_OBJECT_WARN_INVALID_PROPERTY_ID(object,property_id,pspec);
    break;
  }
}
static void
maman_bar_class_init (gpointer g_class,
                      gpointer g_class_data)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
  MamanBarClass *klass = MAMAN_BAR_CLASS (g_class);
  GParamSpec *pspec;
  gobject_class->set_property = maman_bar_set_property;
  gobject_class->get_property = maman_bar_get_property;
  pspec = g_param_spec_string ("maman-name",
                               "Maman construct prop",
                               "Set maman's name",
                               "no-name-set" /* default value */,
                               G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
  g_object_class_install_property (gobject_class,
                                   MAMAN_BAR_CONSTRUCT_NAME,
                                   pspec);
  pspec = g_param_spec_uchar ("papa-number",
                              "Number of current Papa",
                              "Set/Get papa's number",
                              0  /* minimum value */,
                              10 /* maximum value */,
                              2  /* default value */,
                              G_PARAM_READWRITE);
  g_object_class_install_property (gobject_class,
                                   MAMAN_BAR_PAPA_NUMBER,
                                   pspec);
}
/************************************************/
/* Use                                          */
/************************************************/
GObject *bar;
GValue val = {0,};
bar = g_object_new (MAMAN_TYPE_SUBBAR, NULL);
g_value_init (&val, G_TYPE_CHAR);
g_value_set_char (&val, 11);
g_object_set_property (G_OBJECT (bar), "papa-number", &val);
The client code just above looks simple but a lot of things happen under the hood:
      g_object_set_property first ensures a property
      with this name was registered in bar's class_init handler. If so, it calls
      object_set_property which first walks the class hierarchy,
      from bottom, most derived type, to top, fundamental type to find the class
      which registered that property. It then tries to convert the user-provided GValue
      into a GValue whose type if that of the associated property.
    
      If the user provides a signed char GValue, as is shown
      here, and if the object's property was registered as an unsigned int, 
      g_value_transform will try to transform the input signed char into
      an unsigned int. Of course, the success of the transformation depends on the availability
      of the required transform function. In practice, there will almost always be a transformation
        [5]
      which matches and conversion will be caried out if needed.
    
      After transformation, the GValue is validated by 
      g_param_value_validate which makes sure the user's
      data stored in the GValue matches the characteristics specified by
      the property's GParamSpec.  Here, the GParamSpec we 
      provided in class_init has a validation function which makes sure that the GValue
      contains a value which respects the minimum and maximum bounds of the 
      GParamSpec. In the example above, the client's GValue does not
      respect these constraints (it is set to 11, while the maximum is 10). As such, the
      g_object_set_property function will return with an error.
    
      If the user's GValue had been set to a valid value, g_object_set_property
      would have proceeded with calling the object's set_property class method. Here, since our
      implementation of Foo did override this method, the code path would jump to
      foo_set_property after having retrieved from the 
      GParamSpec the param_id
      [6]
      which had been stored by
      g_object_class_install_property.
    
      Once the property has been set by the object's set_property class method, the code path
      returns to g_object_set_property which calls 
	g_object_notify_queue_thaw. This function makes sure that
	the "notify" signal is emitted on the object's instance with the changed property as
	parameter unless notifications were frozen by g_object_freeze_notify.
    
      g_object_thaw_notify can be used to re-enable notification of 
      property modifications through the "notify" signal. It is important to remember that
      even if properties are changed while property change notification is frozen, the "notify"
      signal will be emitted once for each of these changed properties as soon as the property
      change notification is thawn: no property change is lost for the "notify" signal. Signal
      can only be delayed by the notification freezing mechanism.
    
      It is interesting to note that the g_object_set and 
      g_object_set_valist (vararg version) functions can be used to set
      multiple properties at once. The client code shown above can then be re-written as:
MamanBar *foo;
foo = /* */;
g_object_set (G_OBJECT (foo),
              "papa-number", 2, 
              "maman-name", "test", 
              NULL);
The code above will trigger one notify signal emission for each property modified.
      Of course, the _get versions are also available: g_object_get
      and g_object_get_valist (vararg version) can be used to get numerous
      properties at once.
    
      Really attentive readers now understand how g_object_new,
      g_object_newv and g_object_new_valist 
      work: they parse the user-provided variable number of parameters and invoke
      g_object_set on each pair of parameters only after the object has been successfully constructed.
      Of course, the "notify" signal will be emitted for each property set.
    
[5] Its behaviour might not be what you expect but it is up to you to actually avoid relying on these transformations.
[6] It should be noted that the param_id used here need only to uniquely identify each GParamSpec within the FooClass such that the switch used in the set and get methods actually works. Of course, this locally-unique integer is purely an optimization: it would have been possible to use a set of if (strcmp (a, b) == 0) {} else if (strcmp (a, b) == 0) {} statements.