mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-05 08:56:16 +01:00
738 lines
30 KiB
XML
738 lines
30 KiB
XML
<?xml version='1.0' encoding="ISO-8859-1"?>
|
|
|
|
<chapter id="chapter-gobject">
|
|
<title>GObject: what brings everything together.</title>
|
|
|
|
<para>
|
|
The two previous chapters discussed the details of Glib's Dynamic Type System
|
|
and its signal control system. The GObject library also contains an implementation
|
|
for a base fundamental type named <type>GObject</type>.
|
|
</para>
|
|
|
|
<para>
|
|
<type>GObject</type> is a fundamental classed instantiable type. It implements:
|
|
<itemizedlist>
|
|
<listitem><para>Memory management with reference counting</para></listitem>
|
|
<listitem><para>Construction/Destruction of instances</para></listitem>
|
|
<listitem><para>Generic per-object properties with set/get function pairs</para></listitem>
|
|
<listitem><para>Easy use of signals</para></listitem>
|
|
</itemizedlist>
|
|
All the GTK objects and all of the objects in Gnome libraries which use the glib type
|
|
system inherit from <type>GObject</type> which is why it is important to understand
|
|
the details of how it works.
|
|
</para>
|
|
|
|
<sect1 id="gobject-instanciation">
|
|
<title>Object instanciation</title>
|
|
|
|
<para>
|
|
The <function>g_object_new</function> family of functions can be used to instantiate any
|
|
GType which inherits from the GObject base type. All these functions make sure the class
|
|
has been correctly initialized by glib's type system and then invoke at one
|
|
point or another the constructor class method which is used to:
|
|
<itemizedlist>
|
|
<listitem><para>
|
|
Allocate memory through <function>g_type_create_instance</function>,
|
|
</para></listitem>
|
|
<listitem><para>
|
|
Initialize the object' instance with the construction properties.
|
|
</para></listitem>
|
|
</itemizedlist>
|
|
</para>
|
|
|
|
<para>
|
|
Objects which inherit from GObject are allowed to override this constructor class method:
|
|
they should however chain to their parent constructor method before doing so:
|
|
<programlisting>
|
|
GObject* (*constructor) (GType type,
|
|
guint n_construct_properties,
|
|
GObjectConstructParam *construct_properties);
|
|
</programlisting>
|
|
</para>
|
|
|
|
<para>
|
|
The example below shows how <type>MamanBar</type> overrides the parent's constructor:
|
|
<programlisting>
|
|
#define MAMAN_BAR_TYPE (maman_bar_get_type ())
|
|
#define MAMAN_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_BAR_TYPE, MamanBar))
|
|
#define MAMAN_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MAMAN_BAR_TYPE, MamanBarClass))
|
|
#define MAMAN_IS_BAR(obj) (G_TYPE_CHECK_TYPE ((obj), MAMAN_BAR_TYPE))
|
|
#define MAMAN_IS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MAMAN_BAR_TYPE))
|
|
#define MAMAN_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MAMAN_BAR_TYPE, MamanBarClass))
|
|
|
|
typedef struct _MamanBar MamanBar;
|
|
typedef struct _MamanBarClass MamanBarClass;
|
|
|
|
struct _MamanBar {
|
|
GObject parent;
|
|
/* instance members */
|
|
};
|
|
|
|
struct _MamanBarClass {
|
|
GObjectClass parent;
|
|
|
|
/* class members */
|
|
};
|
|
|
|
/* used by MAMAN_BAR_TYPE */
|
|
GType maman_bar_get_type (void);
|
|
|
|
static GObject *
|
|
maman_bar_constructor (GType type,
|
|
guint n_construct_properties,
|
|
GObjectConstructParam *construct_properties)
|
|
{
|
|
GObject *obj;
|
|
|
|
{
|
|
/* Invoke parent constructor. */
|
|
MamanBarClass *klass;
|
|
GObjectClass *parent_class;
|
|
klass = MAMAN_BAR_CLASS (g_type_class_peek (MAMAN_BAR_TYPE));
|
|
parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
|
|
obj = parent_class->constructor (type,
|
|
n_construct_properties,
|
|
construct_properties);
|
|
}
|
|
|
|
/* do stuff. */
|
|
|
|
return obj;
|
|
}
|
|
|
|
static void
|
|
maman_bar_instance_init (GTypeInstance *instance,
|
|
gpointer g_class)
|
|
{
|
|
MamanBar *self = (MamanBar *)instance;
|
|
/* do stuff */
|
|
}
|
|
|
|
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);
|
|
|
|
gobject_class->constructor = maman_bar_constructor;
|
|
}
|
|
|
|
GType maman_bar_get_type (void)
|
|
{
|
|
static GType type = 0;
|
|
if (type == 0) {
|
|
static const GTypeInfo info = {
|
|
sizeof (MamanBarClass),
|
|
NULL, /* base_init */
|
|
NULL, /* base_finalize */
|
|
maman_bar_class_init, /* class_init */
|
|
NULL, /* class_finalize */
|
|
NULL, /* class_data */
|
|
sizeof (MamanBar),
|
|
0, /* n_preallocs */
|
|
maman_bar_instance_init /* instance_init */
|
|
};
|
|
type = g_type_register_static (G_TYPE_OBJECT,
|
|
"MamanBarType",
|
|
&info, 0);
|
|
}
|
|
return type;
|
|
}
|
|
</programlisting>
|
|
If the user instantiates an object <type>MamanBar</type> with:
|
|
<programlisting>
|
|
MamanBar *bar = g_object_new (MAMAN_BAR_TYPE, NULL);
|
|
</programlisting>
|
|
If this is the first instantiation of such an object, the <function>maman_b_class_init</function>
|
|
function will be invoked after any <function>maman_b_base_class_init</function> function.
|
|
This will make sure the class structure of this new object is correctly initialized. Here,
|
|
<function>maman_bar_class_init</function> is expected to override the object's class methods
|
|
and setup the class' own methods. In the example above, the constructor method is the only
|
|
overridden method: it is set to <function>maman_bar_constructor</function>.
|
|
</para>
|
|
|
|
<para>
|
|
Once <function>g_object_new</function> has obtained a reference to an initialized
|
|
class structure, it invokes its constructor method to create an instance of the new
|
|
object. Since it has just been overridden by <function>maman_bar_class_init</function>
|
|
to <function>maman_bar_constructor</function>, the latter is called and, because it
|
|
was implemented correctly, it chains up to its parent's constructor. The problem here
|
|
is how we can find the parent constructor. An approach (used in GTK+ source code) would be
|
|
to save the original constructor in a static variable from <function>maman_bar_class_init</function>
|
|
and then to re-use it from <function>maman_bar_constructor</function>. This is clearly possible
|
|
and very simple but I was told it was not nice and the prefered way is to use the
|
|
<function>g_type_class_peek</function> and <function>g_type_class_peek_parent</function> functions.
|
|
</para>
|
|
|
|
<para>
|
|
Finally, at one point or another, <function>g_object_constructor</function> is invoked
|
|
by the last constructor in the chain. This function allocates the object's instance' buffer
|
|
through <function>g_type_create_instance</function>
|
|
which means that the instance_init function is invoked at this point if one
|
|
was registered. After instance_init returns, the object is fully initialized and should be
|
|
ready to answer any user-request. When <function>g_type_create_instance</function>
|
|
returns, <function>g_object_constructor</function> sets the construction properties
|
|
(ie: the properties which were given to <function>g_object_new</function>) and returns
|
|
to the user's constructor which is then allowed to do useful instance initialization...
|
|
</para>
|
|
|
|
<para>
|
|
The process described above might seem a bit complicated (it <emphasis>is</emphasis> actually
|
|
overly complicated in my opinion..) but it can be summarized easily by the table below which
|
|
lists the functions invoked by <function>g_object_new</function> and their order of
|
|
invocation.
|
|
</para>
|
|
|
|
<para>
|
|
The array below lists the functions invoked by <function>g_object_new</function> and
|
|
their order of invocation:
|
|
|
|
<table id="gobject-construction-table">
|
|
<title><function>g_object_new</function></title>
|
|
<tgroup cols="3">
|
|
<colspec colwidth="*" colnum="1" align="left"/>
|
|
<colspec colwidth="*" colnum="2" align="left"/>
|
|
<colspec colwidth="8*" colnum="3" align="left"/>
|
|
|
|
<thead>
|
|
<row>
|
|
<entry>Invocation time</entry>
|
|
<entry>Function Invoked</entry>
|
|
<entry>Function's parameters</entry>
|
|
<entry>Remark</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>First call to <function>g_object_new</function> for target type</entry>
|
|
<entry>target type's base_init function</entry>
|
|
<entry>On the inheritance tree of classes from fundamental type to target type.
|
|
base_init is invoked once for each class structure.</entry>
|
|
<entry>
|
|
I have no real idea on how this can be used. If you have a good real-life
|
|
example of how a class' base_init can be used, please, let me know.
|
|
</entry>
|
|
</row>
|
|
<row>
|
|
<entry>First call to <function>g_object_new</function> for target type</entry>
|
|
<entry>target type's class_init function</entry>
|
|
<entry>On target type's class structure</entry>
|
|
<entry>
|
|
Here, you should make sure to initialize or override class methods (that is,
|
|
assign to each class' method its function pointer) and create the signals and
|
|
the properties associated to your object.
|
|
</entry>
|
|
</row>
|
|
<row>
|
|
<entry>First call to <function>g_object_new</function> for target type</entry>
|
|
<entry>interface' base_init function</entry>
|
|
<entry>On interface' vtable</entry>
|
|
<entry></entry>
|
|
</row>
|
|
<row>
|
|
<entry>First call to <function>g_object_new</function> for target type</entry>
|
|
<entry>interface' interface_init function</entry>
|
|
<entry>On interface' vtable</entry>
|
|
<entry></entry>
|
|
</row>
|
|
<row>
|
|
<entry>Each call to <function>g_object_new</function> for target type</entry>
|
|
<entry>target type's class constructor method: GObjectClass->constructor</entry>
|
|
<entry>On object's instance</entry>
|
|
<entry>
|
|
If you need to complete the object initialization after all the construction properties
|
|
are set, override the constructor method and make sure to chain up to the object's
|
|
parent class before doing your own initialization.
|
|
In doubt, do not override the constructor method.
|
|
</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Each call to <function>g_object_new</function> for target type</entry>
|
|
<entry>type's instance_init function</entry>
|
|
<entry>On the inheritance tree of classes from fundamental type to target type.
|
|
the instance_init provided for each type is invoked once for each instance
|
|
structure.</entry>
|
|
<entry>
|
|
Provide an instance_init function to initialize your object before its construction
|
|
properties are set. This is the preferred way to initialize a GObject instance.
|
|
This function is equivalent to C++ constructors.
|
|
</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
</para>
|
|
|
|
<para>
|
|
Readers should feel concerned about one little twist in the order in which functions
|
|
are invoked: while, technically, the class' constructor method is called
|
|
<emphasis>before</emphasis> the GType's instance_init function (since
|
|
<function>g_type_create_instance</function> which calls instance_init is called by
|
|
<function>g_object_constructor</function> which is the top-level class
|
|
constructor method and to which users are expected to chain to), the user's code
|
|
which runs in a user-provided constructor will always run <emphasis>after</emphasis>
|
|
GType's instance_init function since the user-provided constructor
|
|
<emphasis>must</emphasis> (you've been warned) chain up <emphasis>before</emphasis>
|
|
doing anything useful.
|
|
</para>
|
|
</sect1>
|
|
|
|
<sect1 id="gobject-memory">
|
|
<title>Object memory management</title>
|
|
|
|
<para>
|
|
The memory-management API for GObjects is a bit complicated but the idea behind it
|
|
is pretty simple: the goal is to provide a flexible model based on reference counting
|
|
which can be integrated in applications which use or require different memory management
|
|
models (such as garbage collection, aso...)
|
|
<programlisting>
|
|
/*
|
|
Refcounting
|
|
*/
|
|
gpointer g_object_ref (gpointer object);
|
|
void g_object_unref (gpointer object);
|
|
|
|
/*
|
|
Weak References
|
|
*/
|
|
typedef void (*GWeakNotify) (gpointer data,
|
|
GObject *where_the_object_was);
|
|
void g_object_weak_ref (GObject *object,
|
|
GWeakNotify notify,
|
|
gpointer data);
|
|
void g_object_weak_unref (GObject *object,
|
|
GWeakNotify notify,
|
|
gpointer data);
|
|
void g_object_add_weak_pointer (GObject *object,
|
|
gpointer *weak_pointer_location);
|
|
void g_object_remove_weak_pointer (GObject *object,
|
|
gpointer *weak_pointer_location);
|
|
/*
|
|
Cycle handling
|
|
*/
|
|
void g_object_run_dispose (GObject *object);
|
|
</programlisting>
|
|
</para>
|
|
|
|
<sect2 id="gobject-memory-refcount">
|
|
<title>Reference count</title>
|
|
|
|
<para>
|
|
<function>g_object_ref</function>/<function>g_object_unref</function> respectively
|
|
increase and decrease the reference count. None of these function is thread-safe.
|
|
The reference count is, unsurprisingly, initialized to one by
|
|
<function>g_object_new</function>. When the reference count reaches zero, that is,
|
|
when <function>g_object_unref</function> is called by the last client holding
|
|
a reference to the object, the <emphasis>dispose</emphasis> and the
|
|
<emphasis>finalize</emphasis> class methods are invoked.
|
|
</para>
|
|
<para>
|
|
Finally, after <emphasis>finalize</emphasis> is invoked,
|
|
<function>g_type_free_instance</function> is called to free the object instance.
|
|
Depending on the memory allocation policy decided when the type was registered (through
|
|
one of the <function>g_type_register_*</function> functions), the object's instance
|
|
memory will be freed or returned to the object pool for this type.
|
|
Once the object has been freed, if it was the last instance of the type, the type's class
|
|
will be destroyed as described in <xref linkend="gtype-instantiable-classed"></xref> and
|
|
<xref linkend="gtype-non-instantiable-classed"></xref>.
|
|
</para>
|
|
|
|
<para>
|
|
The table below summarizes the destruction process of a GObject:
|
|
<table id="gobject-destruction-table">
|
|
<title><function>g_object_unref</function></title>
|
|
<tgroup cols="3">
|
|
<colspec colwidth="*" colnum="1" align="left"/>
|
|
<colspec colwidth="*" colnum="2" align="left"/>
|
|
<colspec colwidth="8*" colnum="3" align="left"/>
|
|
|
|
<thead>
|
|
<row>
|
|
<entry>Invocation time</entry>
|
|
<entry>Function Invoked</entry>
|
|
<entry>Function's parameters</entry>
|
|
<entry>Remark</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>Last call to <function>g_object_unref</function> for an instance
|
|
of target type</entry>
|
|
<entry>target type's dispose class function</entry>
|
|
<entry>GObject instance</entry>
|
|
<entry>
|
|
When dispose ends, the object should not hold any reference to any other
|
|
member object. The object is also expected to be able to answer client
|
|
method invocations (with possibly an error code but no memory violation)
|
|
until finalize is executed. dispose can be executed more than once.
|
|
dispose should chain up to its parent implementation just before returning
|
|
to the caller.
|
|
</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Last call to <function>g_object_unref</function> for an instance
|
|
of target type
|
|
</entry>
|
|
<entry>target type's finalize class function</entry>
|
|
<entry>GObject instance</entry>
|
|
<entry>
|
|
Finalize is expected to complete the destruction process initiated by
|
|
dispose. It should complete the object's destruction. finalize will be
|
|
executed only once.
|
|
finalize should chain up to its parent implementation just before returning
|
|
to the caller.
|
|
The reason why the destruction process is split is two different phases is
|
|
explained in <xref linkend="gobject-memory-cycles"></xref>.
|
|
</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Last call to <function>g_object_unref</function> for the last
|
|
instance of target type</entry>
|
|
<entry>interface' interface_finalize function</entry>
|
|
<entry>On interface' vtable</entry>
|
|
<entry>Never used in practice. Unlikely you will need it.</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Last call to <function>g_object_unref</function>for the last
|
|
instance of target type</entry>
|
|
<entry>interface' base_finalize function</entry>
|
|
<entry>On interface' vtable</entry>
|
|
<entry>Never used in practice. Unlikely you will need it.</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Last call to <function>g_object_unref</function> for the last
|
|
instance of target type</entry>
|
|
<entry>target type's class_finalize function</entry>
|
|
<entry>On target type's class structure</entry>
|
|
<entry>Never used in practice. Unlikely you will need it.</entry>
|
|
</row>
|
|
<row>
|
|
<entry>Last call to <function>g_object_unref</function> for the last
|
|
instance of target type</entry>
|
|
<entry>type's base_finalize function</entry>
|
|
<entry>On the inheritance tree of classes from fundamental type to target type.
|
|
base_init is invoked once for each class structure.</entry>
|
|
<entry>Never used in practice. Unlikely you will need it.</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
<sect2 id="gobject-memory-weakref">
|
|
<title>Weak References</title>
|
|
|
|
<para>
|
|
Weak References are used to monitor object finalization:
|
|
<function>g_object_weak_ref</function> adds a monitoring callback which does
|
|
not hold a reference to the object but which is invoked when the object runs
|
|
its dispose method. As such, each weak ref can be invoked more than once upon
|
|
object finalization (since dispose can run more than once during object
|
|
finalization).
|
|
</para>
|
|
|
|
<para>
|
|
<function>g_object_weak_unref</function> can be used to remove a monitoring
|
|
callback from the object.
|
|
</para>
|
|
|
|
<para>
|
|
Weak References are also used to implement <function>g_object_add_weak_pointer</function>
|
|
and <function>g_object_remove_weak_pointer</function>. These functions add a weak reference
|
|
to the object they are applied to which makes sure to nullify the pointer given by the user
|
|
when object is finalized.
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
<sect2 id="gobject-memory-cycles">
|
|
<title>Reference counts and cycles</title>
|
|
|
|
<para>
|
|
Note: the following section was inspired by James Henstridge. I guess this means that
|
|
all praise and all curses will be directly forwarded to him.
|
|
</para>
|
|
|
|
<para>
|
|
GObject's memory management model was designed to be easily integrated in existing code
|
|
using garbage collection. This is why the destruction process is split in two phases:
|
|
the first phase, executed in the dispose handler is supposed to release all references
|
|
to other member objects. The second phase, executed by the finalize handler is supposed
|
|
to complete the object's destruction process. Object methods should be able to run
|
|
without program error (that is, without segfault :) in-between the two phases.
|
|
</para>
|
|
|
|
<para>
|
|
This two-step destruction process is very useful to break reference counting cycles.
|
|
While the detection of the cycles is up to the external code, once the cycles have been
|
|
detected, the external code can invoke <function>g_object_dispose</function> which
|
|
will indeed break any existing cycles since it will run the dispose handler associated
|
|
to the object and thus release all references to other objects.
|
|
</para>
|
|
|
|
<para>
|
|
Attentive readers might now have understood one of the rules about the dispose handler
|
|
we stated a bit sooner: the dispose handler can be invoked multiple times. Let's say we
|
|
have a reference count cycle: object A references B which itself references object A.
|
|
Let's say we have detected the cycle and we want to destroy the two objects. One way to
|
|
do this would be to invoke <function>g_object_dispose</function> on one of the
|
|
objects.
|
|
</para>
|
|
|
|
<para>
|
|
If object A releases all its references to all objects, this means it releases its
|
|
reference to object B. If object B was not owned by anyone else, this is its last
|
|
reference count which means this last unref runs B's dispose handler which, in turn,
|
|
releases B's reference on object A. If this is A's last reference count, this last
|
|
unref runs A's dispose handler which is running for the second time before
|
|
A's finalize handler is invoked !
|
|
</para>
|
|
|
|
<para>
|
|
The above example, which might seem a bit contrived can really happen if your
|
|
GObject's are being by language bindings. I would thus suggest the rules stated above
|
|
for object destruction are closely followed. Otherwise, <emphasis>Bad Bad Things</emphasis>
|
|
will happen.
|
|
</para>
|
|
</sect2>
|
|
</sect1>
|
|
|
|
<sect1 id="gobject-properties">
|
|
<title>Object properties</title>
|
|
|
|
<para>
|
|
One of GObject's nice features is its generic get/set mechanism. When an object
|
|
is instanciated, the object's class_init handler should be used to register
|
|
the object's properties with <function>g_object_class_install_property</function>
|
|
(implemented in <filename>gobject.c</filename>).
|
|
</para>
|
|
|
|
<para>
|
|
The best way to understand how object properties work is by looking at a real example
|
|
on how it is used:
|
|
<programlisting>
|
|
/************************************************/
|
|
/* Implementation */
|
|
/************************************************/
|
|
|
|
enum {
|
|
MAMAN_BAR_CONSTRUCT_NAME = 1,
|
|
MAMAN_BAR_PAPA_NUMBER = 2,
|
|
};
|
|
|
|
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_assert (FALSE);
|
|
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_assert (FALSE);
|
|
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_SUBBAR_TYPE, NULL);
|
|
g_value_init (&val, G_TYPE_CHAR);
|
|
g_value_set_char (&val, 11);
|
|
g_object_set_property (G_OBJECT (bar), "papa-number", &val);
|
|
</programlisting>
|
|
The client code just above looks simple but a lot of things happen under the hood:
|
|
</para>
|
|
|
|
<para>
|
|
<function>g_object_set_property</function> first ensures a property
|
|
with this name was registered in bar's class_init handler. If so, it calls
|
|
<function>object_set_property</function> 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.
|
|
</para>
|
|
|
|
<para>
|
|
If the user provides a signed char GValue, as is shown
|
|
here, and if the object's property was registered as an unsigned int,
|
|
<function>g_value_transform</function> 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
|
|
<footnote>
|
|
<para>Its behaviour might not be what you expect but it is up to you to actually avoid
|
|
relying on these transformations.
|
|
</para>
|
|
</footnote>
|
|
which matches and conversion will be caried out if needed.
|
|
</para>
|
|
|
|
<para>
|
|
After transformation, the <type>GValue</type> is validated by
|
|
<function>g_param_value_validate</function> which makes sure the user's
|
|
data stored in the <type>GValue</type> matches the characteristics specified by
|
|
the property's <type>GParamSpec</type>. Here, the <type>GParamSpec</type> 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
|
|
<type>GParamSpec</type>. 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
|
|
<function>g_object_set_property</function> function will return with an error.
|
|
</para>
|
|
|
|
<para>
|
|
If the user's GValue had been set to a valid value, <function>object_set_property</function>
|
|
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
|
|
<function>foo_set_property</function> after having retrieved from the
|
|
<type>GParamSpec</type> the <emphasis>param_id</emphasis>
|
|
<footnote>
|
|
<para>
|
|
It should be noted that the param_id used here need only to uniquely identify each
|
|
<type>GParamSpec</type> within the <type>FooClass</type> 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
|
|
<emphasis>if (strcmp (a, b) == 0) {} else if (strcmp (a, b) == 0) {}</emphasis> statements.
|
|
</para>
|
|
</footnote>
|
|
which had been stored by
|
|
<function>g_object_class_install_property</function>.
|
|
</para>
|
|
|
|
<para>
|
|
Once the property has been set by the object's set_property class method, the code path
|
|
returns to <function>g_object_set_property</function> which calls
|
|
<function>g_object_notify_queue_thaw</function>. 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 <function>g_object_freeze_notify</function>.
|
|
</para>
|
|
|
|
<para>
|
|
<function>g_object_thaw_notify</function> 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.
|
|
</para>
|
|
|
|
<para>
|
|
It is interesting to note that the <function>g_object_set</function> and
|
|
<function>g_object_set_valist</function> (vararg version) functions can be used to set
|
|
multiple properties at once. The client code shown above can then be re-written as:
|
|
<programlisting>
|
|
MamanBar *foo;
|
|
foo = /* */;
|
|
g_object_set (G_OBJECT (foo),
|
|
"papa-number", 2,
|
|
"maman-name", "test",
|
|
NULL);
|
|
</programlisting>
|
|
The code above will trigger one notify signal emission for each property modified.
|
|
</para>
|
|
|
|
<para>
|
|
Of course, the _get versions are also available: <function>g_object_get</function>
|
|
and <function>g_object_get_valist</function> (vararg version) can be used to get numerous
|
|
properties at once.
|
|
</para>
|
|
|
|
<para>
|
|
Really attentive readers now understand how <function>g_object_new</function>,
|
|
<function>g_object_newv</function> and <function>g_object_new_valist</function>
|
|
work: they parse the user-provided variable number of parameters and invoke
|
|
<function>g_object_set</function> 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.
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
</chapter>
|
|
|
|
|
|
|
|
|