mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-10 11:26:16 +01:00
Initial revision
This commit is contained in:
parent
1107a9f41a
commit
e992ea6fb6
737
docs/reference/gobject/tut_gobject.xml
Normal file
737
docs/reference/gobject/tut_gobject.xml
Normal file
@ -0,0 +1,737 @@
|
||||
<?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>
|
||||
|
||||
|
||||
|
||||
|
520
docs/reference/gobject/tut_gsignal.xml
Normal file
520
docs/reference/gobject/tut_gsignal.xml
Normal file
@ -0,0 +1,520 @@
|
||||
<?xml version='1.0' encoding="ISO-8859-1"?>
|
||||
<chapter id="chapter-signal">
|
||||
<title>Signals</title>
|
||||
|
||||
<sect1 id="closure">
|
||||
<title>Closures</title>
|
||||
|
||||
<para>
|
||||
Closures are central to the concept of asynchronous signal delivery
|
||||
which is widely used throughout GTK+ and Gnome applications. A Closure is an
|
||||
abstraction, a generic representation of a callback. It is a small structure
|
||||
which contains three objects:
|
||||
<itemizedlist>
|
||||
<listitem><para>a function pointer (the callback itself) whose prototype looks like:
|
||||
<programlisting>
|
||||
return_type function_callback (... , gpointer user_data);
|
||||
</programlisting>
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
the user_data pointer which is passed to the callback upon invocation of the closure
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
a function pointer which represents the destructor of the closure: whenever the
|
||||
closure's refcount reaches zero, this function will be called before the closure
|
||||
structure is freed.
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The <type>GClosure</type> structure represents the common functionality of all
|
||||
closure implementations: there exist a different Closure implementation for
|
||||
each separate runtime which wants to use the GObject type system.
|
||||
<footnote><para>
|
||||
In Practice, Closures sit at the boundary of language runtimes: if you are
|
||||
writing python code and one of your Python callback receives a signal from
|
||||
one of GTK+ widgets, the C code in GTK+ needs to execute your Python
|
||||
code. The Closure invoked by the GTK+ object invokes the Python callback:
|
||||
it behaves as a normal C object for GTK+ and as a normal Python object for
|
||||
python code.
|
||||
</para></footnote>
|
||||
The GObject library provides a simple <type>GCClosure</type> type which
|
||||
is a specific implementation of closures to be used with C/C++ callbacks.
|
||||
</para>
|
||||
<para>
|
||||
A <type>GClosure</type> provides simple services:
|
||||
<itemizedlist>
|
||||
<listitem><para>
|
||||
Invocation (<function>g_closure_invoke</function>): this is what closures
|
||||
were created for: they hide the details of callback invocation from the
|
||||
callback invocator.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
Notification: the closure notifies listeners of certain events such as
|
||||
closure invocation, closure invalidation and closure finalization. Listeners
|
||||
can be registered with <function>g_closure_add_finalize_notifier</function>
|
||||
(finalization notification), <function>g_closure_add_invalidate_notifier</function>
|
||||
(invalidation notification) and
|
||||
<function>g_closure_add_marshal_guards</function> (invocation notification).
|
||||
There exist symmetric de-registration functions for finalization and invalidation
|
||||
events (<function>g_closure_remove_finalize_notifier</function> and
|
||||
<function>g_closure_remove_invalidate_notifier</function>) but not for the invocation
|
||||
process.
|
||||
<footnote><para>
|
||||
Closures are refcounted and notify listeners of their destruction in a two-stage
|
||||
process: the invalidation notifiers are invoked before the finalization notifiers.
|
||||
</para></footnote>
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<sect2>
|
||||
<title>C Closures</title>
|
||||
|
||||
<para>
|
||||
If you are using C or C++
|
||||
to connect a callback to a given event, you will either use the simple <type>GCClosure</type>s
|
||||
which have a pretty minimal API or the even simpler <function>g_signal_connect</function>
|
||||
functions (which will be presented a bit later :).
|
||||
<programlisting>
|
||||
GClosure* g_cclosure_new (GCallback callback_func,
|
||||
gpointer user_data,
|
||||
GClosureNotify destroy_data);
|
||||
GClosure* g_cclosure_new_swap (GCallback callback_func,
|
||||
gpointer user_data,
|
||||
GClosureNotify destroy_data);
|
||||
GClosure* g_signal_type_cclosure_new (GType itype,
|
||||
guint struct_offset);
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<function>g_cclosure_new</function> will create a new closure which can invoke the
|
||||
user-provided callback_func with the user-provided user_data as last parameter. When the closure
|
||||
is finalized (second stage of the destruction process), it will invoke the destroy_data function
|
||||
if the user has supplied one.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<function>g_cclosure_new_swap</function> will create a new closure which can invoke the
|
||||
user-provided callback_func with the user-provided user_data as first parameter (instead of being the
|
||||
last parameter as with <function>g_cclosure_new</function>). When the closure
|
||||
is finalized (second stage of the destruction process), it will invoke the destroy_data
|
||||
function if the user has supplied one.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2>
|
||||
<title>non-C closures (for the fearless).</title>
|
||||
|
||||
<para>
|
||||
As was explained above, Closures hide the details of callback invocation. In C,
|
||||
callback invocation is just like function invocation: it is a matter of creating
|
||||
the correct stack frame for the called function and executing a <emphasis>call</emphasis>
|
||||
assembly instruction.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
C closure marshallers transform the array of GValues which represent
|
||||
the parameters to the target function into a C-style function parameter list, invoke
|
||||
the user-supplied C function with this new parameter list, get the return value of the
|
||||
function, transform it into a GValue and return this GValue to the marshaller caller.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The following code implements a simple marshaller in C for a C function which takes an
|
||||
integer as first parameter and returns void.
|
||||
<programlisting>
|
||||
g_cclosure_marshal_VOID__INT (GClosure *closure,
|
||||
GValue *return_value,
|
||||
guint n_param_values,
|
||||
const GValue *param_values,
|
||||
gpointer invocation_hint,
|
||||
gpointer marshal_data)
|
||||
{
|
||||
typedef void (*GMarshalFunc_VOID__INT) (gpointer data1,
|
||||
gint arg_1,
|
||||
gpointer data2);
|
||||
register GMarshalFunc_VOID__INT callback;
|
||||
register GCClosure *cc = (GCClosure*) closure;
|
||||
register gpointer data1, data2;
|
||||
|
||||
g_return_if_fail (n_param_values == 2);
|
||||
|
||||
data1 = g_value_peek_pointer (param_values + 0);
|
||||
data2 = closure->data;
|
||||
|
||||
callback = (GMarshalFunc_VOID__INT) (marshal_data ? marshal_data : cc->callback);
|
||||
|
||||
callback (data1,
|
||||
g_marshal_value_peek_int (param_values + 1),
|
||||
data2);
|
||||
}
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Of course, there exist other kinds of marshallers. For example, James Henstridge
|
||||
wrote a generic Python marshaller which is used by all python Closures (a python closure
|
||||
is used to have python-based callback be invoked by the closure invocation process).
|
||||
This python marshaller transforms the input GValue list representing the function
|
||||
parameters into a Python tupple which is the equivalent structure in python (you can
|
||||
look in <function>pyg_closure_marshal</function> in <filename>pygtype.c</filename>
|
||||
in the <emphasis>pygtk</emphasis> module in Gnome cvs server).
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="signal">
|
||||
<title>Signals</title>
|
||||
|
||||
<para>
|
||||
GObject's signals have nothing to do with standard UNIX signals: they connect
|
||||
arbitrary application-specific events with any number of listeners.
|
||||
For example, in GTK, every user event (keystroke or mouse move) is received
|
||||
from the X server and generates a GTK+ event under the form of a signal emission
|
||||
on a given object instance.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Each signal is registered in the type system together with the type on which
|
||||
it can be emitted: users of the type are said to <emphasis>connect</emphasis>
|
||||
to the signal on a given type instance when they register a closure to be
|
||||
invoked upon the signal emission. Users can also emit the signal by themselves
|
||||
or stop the emission of the signal from within one of the closures connected
|
||||
to the signal.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When a signal is emitted on a given type instance, all the closures
|
||||
connected to this signal on this type instance will be invoked. All the closures
|
||||
connected to such a signal represent callbacks whose signature looks like:
|
||||
<programlisting>
|
||||
return_type function_callback (gpointer instance, ... , gpointer user_data);
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<sect2 id="signal-registration">
|
||||
<title>Signal registration</title>
|
||||
|
||||
<para>
|
||||
To register a new signal on an existing type, we can use any of <function>g_signal_newv</function>,
|
||||
<function>g_signal_new_valist</function> or <function>g_signal_new</function> functions:
|
||||
<programlisting>
|
||||
guint g_signal_newv (const gchar *signal_name,
|
||||
GType itype,
|
||||
GSignalFlags signal_flags,
|
||||
GClosure *class_closure,
|
||||
GSignalAccumulator accumulator,
|
||||
gpointer accu_data,
|
||||
GSignalCMarshaller c_marshaller,
|
||||
GType return_type,
|
||||
guint n_params,
|
||||
GType *param_types);
|
||||
</programlisting>
|
||||
The number of parameters to these functions is a bit intimidating but they are relatively
|
||||
simple:
|
||||
<itemizedlist>
|
||||
<listitem><para>
|
||||
signal_name: is a string which can be used to uniquely identify a given signal.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
itype: is the instance type on which this signal can be emitted.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
signal_flags: partly defines the order in which closures which were connected to the
|
||||
signal are invoked.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
class_closure: this is the default closure for the signal: if it is not NULL upon
|
||||
the signal emission, it will be invoked upon this emission of the signal. The
|
||||
moment where this closure is invoked compared to other closures connected to that
|
||||
signal depends partly on the signal_flags.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
accumulator: this is a function pointer which is invoked after each closure
|
||||
has been invoked. If it returns FALSE, signal emission is stopped. If it returns
|
||||
TRUE, signal emission proceeds normally. It is also used to compute the return
|
||||
value of the signal based on the return value of all the invoked closures.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
accumulator_data: this pointer will be passed down to each invocation of the
|
||||
accumulator during emission.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
c_marshaller: this is the default C marshaller for any closure which is connected to
|
||||
this signal.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
return_type: this is the type of the return value of the signal.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
n_params: this is the number of parameters this signal takes.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
param_types: this is an array of GTypes which indicate the type of each parameter
|
||||
of the signal. The length of this array is indicated by n_params.
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
As you can see from the above definition, a signal is basically a description
|
||||
of the closures which can be connected to this signal and a description of the
|
||||
order in which the closures connected to this signal will be invoked.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="signal-connection">
|
||||
<title>Signal connection</title>
|
||||
|
||||
<para>
|
||||
If you want to connect to a signal with a closure, you have three possibilities:
|
||||
<itemizedlist>
|
||||
<listitem><para>
|
||||
You can register a class closure at signal registration: this is a
|
||||
system-wide operation. i.e.: the class_closure will be invoked during each emission
|
||||
of a given signal on all the instances of the type which supports that signal.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
You can use <function>g_signal_override_class_closure</function> which
|
||||
overrides the class_closure of a given type. It is possible to call this function
|
||||
only on a derived type of the type on which the signal was registered.
|
||||
This function is of use only to language bindings.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
You can register a closure with the <function>g_signal_connect</function>
|
||||
family of functions. This is an instance-specific operation: the closure
|
||||
will be invoked only during emission of a given signal on a given instance.
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
It is also possible to connect a different kind of callback on a given signal:
|
||||
emission hooks are invoked whenever a given signal is emitted whatever the instance on
|
||||
which it is emitted. Emission hooks are used for example to get all mouse_clicked
|
||||
emissions in an application to be able to emit the small mouse click sound.
|
||||
Emission hooks are connected with <function>g_signal_add_emission_hook</function>
|
||||
and removed with <function>g_signal_remove_emission_hook</function>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="signal-emission">
|
||||
<title>Signal emission</title>
|
||||
|
||||
<para>
|
||||
Signal emission is done through the use of the <function>g_signal_emit</function> family
|
||||
of functions.
|
||||
<programlisting>
|
||||
void g_signal_emitv (const GValue *instance_and_params,
|
||||
guint signal_id,
|
||||
GQuark detail,
|
||||
GValue *return_value);
|
||||
</programlisting>
|
||||
<itemizedlist>
|
||||
<listitem><para>
|
||||
The instance_and_params array of GValues contains the list of input
|
||||
parameters to the signal. The first element of the array is the
|
||||
instance pointer on which to invoke the signal. The following elements of
|
||||
the array contain the list of parameters to the signal.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
signal_id identifies the signal to invoke.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
detail identifies the specific detail of the signal to invoke. A detail is a kind of
|
||||
magic token/argument which is passed around during signal emission and which is used
|
||||
by closures connected to the signal to filter out unwanted signal emissions. In most
|
||||
cases, you can safely set this value to zero. See <xref linkend="signal-detail"/> for
|
||||
more details about this parameter.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
return_value holds the return value of the last closure invoked during emission if
|
||||
no accumulator was specified. If an accumulator was specified during signal creation,
|
||||
this accumulator is used to calculate the return_value as a function of the return
|
||||
values of all the closures invoked during emission.
|
||||
<footnote><para>
|
||||
James (again!!) gives a few non-trivial examples of accumulators:
|
||||
<quote>
|
||||
For instance, you may have an accumulator that ignores NULL returns from
|
||||
closures, and only accumulates the non-NULL ones. Another accumulator may try
|
||||
to return the list of values returned by the closures.
|
||||
</quote>
|
||||
</para></footnote>
|
||||
If no closure is invoked during
|
||||
emission, the return_value is nonetheless initialized to zero/null.
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Internally, the GValue array is passed to the emission function proper,
|
||||
<function>signal_emit_unlocked_R</function> (implemented in <filename>gsignal.c</filename>).
|
||||
Signal emission can be decomposed in 5 steps:
|
||||
<itemizedlist>
|
||||
<listitem><para>
|
||||
<emphasis>RUN_FIRST</emphasis>: if the G_SIGNAL_RUN_FIRST flag was used
|
||||
during signal registration and if there exist a class_closure for this signal,
|
||||
the class_closure is invoked. Jump to <emphasis>EMISSION_HOOK</emphasis> state.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
<emphasis>EMISSION_HOOK</emphasis>: if any emission hook was added to
|
||||
the signal, they are invoked from first to last added. Accumulate return values
|
||||
and jump to <emphasis>HANDLER_RUN_FIRST</emphasis> state.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
<emphasis>HANDLER_RUN_FIRST</emphasis>: if any closure were connected
|
||||
with the <function>g_signal_connect</function> family of
|
||||
functions, and if they are not blocked (with the <function>g_signal_handler_block</function>
|
||||
family of functions) they are run here, from first to last connected.
|
||||
Jump to <emphasis>RUN_LAST</emphasis> state.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
<emphasis>RUN_LAST</emphasis>: if the G_SIGNAL_RUN_LAST
|
||||
flag was set during registration and if a class_closure
|
||||
was set, it is invoked here. Jump to
|
||||
<emphasis>HANDLER_RUN_LAST</emphasis> state.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
<emphasis>HANDLER_RUN_LAST</emphasis>: if any closure were connected
|
||||
with the <function>g_signal_connect_after</function> family of
|
||||
functions, if they were not invoked during HANDLER_RUN_FIRST and if they
|
||||
are not blocked, they are run here, from first to last connected.
|
||||
Jump to <emphasis>RUN_CLEANUP</emphasis> state.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
<emphasis>RUN_CLEANUP</emphasis>: if the G_SIGNAL_RUN_CLEANUP flag
|
||||
was set during registration and if a class_closure was set,
|
||||
it is invoked here. Signal emission is completed here.
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If, at any point during emission (except in RUN_CLEANUP state), one of the
|
||||
closures or emission hook stops the signal emission with
|
||||
<function>g_signal_stop</function>, emission jumps to CLEANUP state.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If, at any point during emission, one of the closures or emission hook
|
||||
emits the same signal on the same instance, emission is restarted from
|
||||
the RUN_FIRST state.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The accumulator function is invoked in all states, after invocation
|
||||
of each closure (except in EMISSION_HOOK and CLEANUP). It accumulates
|
||||
the closure return value into the signal return value and returns TRUE or
|
||||
FALSE. If, at any point, it does not return TRUE, emission jumps to CLEANUP state.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If no accumulator function was provided, the value returned by the last handler
|
||||
run will be returned by <function>g_signal_emit</function>.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
|
||||
<sect2 id="signal-detail">
|
||||
<title>The <emphasis>detail</emphasis> argument</title>
|
||||
|
||||
<para>All the functions related to signal emission or signal connection have a parameter
|
||||
named the <emphasis>detail</emphasis>. Sometimes, this parameter is hidden by the API
|
||||
but it is always there, under one form or another.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Of the three main connection functions,
|
||||
only one has an explicit detail parameter as a <type>GQuark</type>
|
||||
<footnote>
|
||||
<para>A GQuark is an integer which uniquely represents a string. It is possible to transform
|
||||
back and forth between the integer and string representations with the functions
|
||||
<function>g_quark_from_string</function> and <function>g_quark_to_string</function>.
|
||||
</para>
|
||||
</footnote>:
|
||||
<programlisting>
|
||||
gulong g_signal_connect_closure_by_id (gpointer instance,
|
||||
guint signal_id,
|
||||
GQuark detail,
|
||||
GClosure *closure,
|
||||
gboolean after);
|
||||
</programlisting>
|
||||
The two other functions hide the detail parameter in the signal name identification:
|
||||
<programlisting>
|
||||
gulong g_signal_connect_closure (gpointer instance,
|
||||
const gchar *detailed_signal,
|
||||
GClosure *closure,
|
||||
gboolean after);
|
||||
gulong g_signal_connect_data (gpointer instance,
|
||||
const gchar *detailed_signal,
|
||||
GCallback c_handler,
|
||||
gpointer data,
|
||||
GClosureNotify destroy_data,
|
||||
GConnectFlags connect_flags);
|
||||
</programlisting>
|
||||
Their detailed_signal parameter is a string which identifies the name of the signal
|
||||
to connect to. However, the format of this string is structured to look like
|
||||
<emphasis>signal_name::detail_name</emphasis>. Connecting to the signal
|
||||
named <emphasis>notify::cursor_position</emphasis> will actually connect to the signal
|
||||
named <emphasis>notify</emphasis> with the <emphasis>cursor_position</emphasis> name.
|
||||
Internally, the detail string is transformed to a GQuark if it is present.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Of the four main signal emission functions, three have an explicit detail parameter as a
|
||||
<type>GQuark</type> again:
|
||||
<programlisting>
|
||||
void g_signal_emitv (const GValue *instance_and_params,
|
||||
guint signal_id,
|
||||
GQuark detail,
|
||||
GValue *return_value);
|
||||
void g_signal_emit_valist (gpointer instance,
|
||||
guint signal_id,
|
||||
GQuark detail,
|
||||
va_list var_args);
|
||||
void g_signal_emit (gpointer instance,
|
||||
guint signal_id,
|
||||
GQuark detail,
|
||||
...);
|
||||
</programlisting>
|
||||
The fourth function hides it in its signal name parameter:
|
||||
<programlisting>
|
||||
void g_signal_emit_by_name (gpointer instance,
|
||||
const gchar *detailed_signal,
|
||||
...);
|
||||
</programlisting>
|
||||
The format of the detailed_signal parameter is exactly the same as the format used by
|
||||
the <function>g_signal_connect</function> functions: <emphasis>signal_name::detail_name</emphasis>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If a detail is provided by the user to the emission function, it is used during emission to match
|
||||
against the closures which also provide a detail. The closures which provided a detail will not
|
||||
be invoked (even though they are connected to a signal which is being emitted) if their detail
|
||||
does not match the detail provided by the user.
|
||||
</para>
|
||||
|
||||
<para>This completely optional filtering mechanism is mainly used as an optimization for signals
|
||||
which are often emitted for many different reasons: the clients can filter out which events they are
|
||||
interested into before the closure's marshalling code runs. For example, this is used extensively
|
||||
by the <emphasis>notify</emphasis> signal of GObject: whenever a property is modified on a GObject,
|
||||
instead of just emitting the <emphasis>notify</emphasis> signal, GObject associates as a detail to this
|
||||
signal emission the name of the property modified. This allows clients who wish to be notified of changes
|
||||
to only one property to filter most events before receiving them.
|
||||
</para>
|
||||
|
||||
<para>As a simple rule, users can and should set the detail parameter to zero: this will disable completely
|
||||
this optional filtering.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
</chapter>
|
907
docs/reference/gobject/tut_gtype.xml
Normal file
907
docs/reference/gobject/tut_gtype.xml
Normal file
@ -0,0 +1,907 @@
|
||||
<?xml version='1.0' encoding="ISO-8859-1"?>
|
||||
<chapter>
|
||||
<title>The Glib Dynamic Type System</title>
|
||||
|
||||
<para>
|
||||
A type, as manipulated by the Glib type system, is much more generic than what
|
||||
is usually understood as an Object type. It is best explained by looking at the
|
||||
structure and the functions used to register new types in the type system.
|
||||
<programlisting>
|
||||
typedef struct _GTypeInfo GTypeInfo;
|
||||
struct _GTypeInfo
|
||||
{
|
||||
/* interface types, classed types, instantiated types */
|
||||
guint16 class_size;
|
||||
|
||||
GBaseInitFunc base_init;
|
||||
GBaseFinalizeFunc base_finalize;
|
||||
|
||||
/* classed types, instantiated types */
|
||||
GClassInitFunc class_init;
|
||||
GClassFinalizeFunc class_finalize;
|
||||
gconstpointer class_data;
|
||||
|
||||
/* instantiated types */
|
||||
guint16 instance_size;
|
||||
guint16 n_preallocs;
|
||||
GInstanceInitFunc instance_init;
|
||||
|
||||
/* value handling */
|
||||
const GTypeValueTable *value_table;
|
||||
};
|
||||
GType g_type_register_static (GType parent_type,
|
||||
const gchar *type_name,
|
||||
const GTypeInfo *info,
|
||||
GTypeFlags flags);
|
||||
GType g_type_register_fundamental (GType type_id,
|
||||
const gchar *type_name,
|
||||
const GTypeInfo *info,
|
||||
const GTypeFundamentalInfo *finfo,
|
||||
GTypeFlags flags);
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<function>g_type_register_static</function> and
|
||||
<function>g_type_register_fundamental</function>
|
||||
are the C functions, defined in
|
||||
<filename>gtype.h</filename> and implemented in <filename>gtype.c</filename>
|
||||
which you should use to register a new type in the program's type system.
|
||||
It is not likely you will ever need to use
|
||||
<function>g_type_register_fundamental</function> (you have to be Tim Janik
|
||||
to do that) but in case you want to, the last chapter explains how to create
|
||||
new fundamental types.
|
||||
<footnote>
|
||||
<para>
|
||||
Please, note that there exist another registration function: the
|
||||
<function>g_type_register_dynamic</function>. We will not discuss this
|
||||
function here since its use is very similar to the <function>_static</function>
|
||||
version.
|
||||
</para>
|
||||
</footnote>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Fundamental types are top-level types which do not derive from any other type
|
||||
while other non-fundamental types derive from other types.
|
||||
Upon initialization by <function>g_type_init</function>, the type system not
|
||||
only initializes its internal data structures but it also registers a number of core
|
||||
types: some of these are fundamental types. Others are types derived from these
|
||||
fundamental types.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Fundamental and non-Fundamental types are defined by:
|
||||
<itemizedlist>
|
||||
<listitem><para>
|
||||
class size: the class_size field in <type>GTypeInfo</type>.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
class initialization functions (C++ constructor): the base_init and
|
||||
class_init fields in <type>GTypeInfo</type>.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
class destruction functions (C++ destructor): the base_finalize and
|
||||
class_finalize fields in <type>GTypeInfo</type>.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
instance size (C++ parameter to new): the instance_size field in
|
||||
<type>GTypeInfo</type>.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
instanciation policy (C++ type of new operator): the n_preallocs
|
||||
field in <type>GTypeInfo</type>.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
copy functions (C++ copy operators): the value_table field in
|
||||
<type>GTypeInfo</type>.
|
||||
</para></listitem>
|
||||
<listitem><para>
|
||||
XXX: <type>GTypeFlags</type>.
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
Fundamental types are also defined by a set of <type>GTypeFundamentalFlags</type>
|
||||
which are stored in a <type>GTypeFundamentalInfo</type>.
|
||||
Non-Fundamental types are furthermore defined by the type of their parent which is
|
||||
passed as the parent_type parameter to <function>g_type_register_static</function>
|
||||
and <function>g_type_register_dynamic</function>.
|
||||
</para>
|
||||
|
||||
<sect1 id="gtype-copy">
|
||||
<title>Copy functions</title>
|
||||
|
||||
<para>
|
||||
The major common point between <emphasis>all</emphasis> glib types (fundamental and
|
||||
non-fundamental, classed and non-classed, instantiable and non-instantiable) is that
|
||||
they can all be manipulated through a single API to copy/assign them.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The <type>GValue</type> structure is used as an abstract container for all of these
|
||||
types. Its simplistic API (defined in <filename>gobject/gvalue.h</filename>) can be
|
||||
used to invoke the value_table functions registered
|
||||
during type registration: for example <function>g_value_copy</function> copies the
|
||||
content of a <type>GValue</type> to another <type>GValue</type>. This is similar
|
||||
to a C++ assignment which invokes the C++ copy operator to modify the default
|
||||
bit-by-bit copy semantics of C++/C structures/classes.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The following code shows shows you can copy around a 64 bit integer, as well as a <type>GObject</type>
|
||||
instance pointer (sample code for this is located in the source tarball for this document in
|
||||
<filename>sample/gtype/test.c</filename>):
|
||||
<programlisting>
|
||||
static void test_int (void)
|
||||
{
|
||||
GValue a_value = {0, };
|
||||
GValue b_value = {0, };
|
||||
guint64 a, b;
|
||||
|
||||
a = 0xdeadbeaf;
|
||||
|
||||
g_value_init (&a_value, G_TYPE_UINT64);
|
||||
g_value_set_uint64 (&a_value, a);
|
||||
|
||||
g_value_init (&b_value, G_TYPE_UINT64);
|
||||
g_value_copy (&a_value, &b_value);
|
||||
|
||||
b = g_value_get_uint64 (&b_value);
|
||||
|
||||
if (a == b) {
|
||||
g_print ("Yay !! 10 lines of code to copy around a uint64.\n");
|
||||
} else {
|
||||
g_print ("Are you sure this is not a Z80 ?\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void test_object (void)
|
||||
{
|
||||
GObject *obj;
|
||||
GValue obj_vala = {0, };
|
||||
GValue obj_valb = {0, };
|
||||
obj = g_object_new (MAMAN_BAR_TYPE, NULL);
|
||||
|
||||
g_value_init (&obj_vala, MAMAN_BAR_TYPE);
|
||||
g_value_set_object (&obj_vala, obj);
|
||||
|
||||
g_value_init (&obj_valb, G_TYPE_OBJECT);
|
||||
|
||||
/* g_value_copy's semantics for G_TYPE_OBJECT types is to copy the reference.
|
||||
This function thus calls g_object_ref.
|
||||
It is interesting to note that the assignment works here because
|
||||
MAMAN_BAR_TYPE is a G_TYPE_OBJECT.
|
||||
*/
|
||||
g_value_copy (&obj_vala, &obj_valb);
|
||||
|
||||
g_object_unref (G_OBJECT (obj));
|
||||
g_object_unref (G_OBJECT (obj));
|
||||
}
|
||||
</programlisting>
|
||||
The important point about the above code is that the exact semantic of the copy calls
|
||||
is undefined since they depend on the implementation of the copy function. Certain
|
||||
copy functions might decide to allocate a new chunk of memory and then to copy the
|
||||
data from the source to the destination. Others might want to simply increment
|
||||
the reference count of the instance and copy the reference to the new GValue.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The value_table used to specify these assignment functions is defined in
|
||||
<filename>gtype.h</filename> and is thoroughly described in the
|
||||
API documentation provided with GObject (for once ;-) which is why we will
|
||||
not detail its exact semantics.
|
||||
<programlisting>
|
||||
typedef struct _GTypeValueTable GTypeValueTable;
|
||||
struct _GTypeValueTable
|
||||
{
|
||||
void (*value_init) (GValue *value);
|
||||
void (*value_free) (GValue *value);
|
||||
void (*value_copy) (const GValue *src_value,
|
||||
GValue *dest_value);
|
||||
/* varargs functionality (optional) */
|
||||
gpointer (*value_peek_pointer) (const GValue *value);
|
||||
gchar *collect_format;
|
||||
gchar* (*collect_value) (GValue *value,
|
||||
guint n_collect_values,
|
||||
GTypeCValue *collect_values,
|
||||
guint collect_flags);
|
||||
gchar *lcopy_format;
|
||||
gchar* (*lcopy_value) (const GValue *value,
|
||||
guint n_collect_values,
|
||||
GTypeCValue *collect_values,
|
||||
guint collect_flags);
|
||||
};
|
||||
</programlisting>
|
||||
Interestingly, it is also very unlikely
|
||||
you will ever need to specify a value_table during type registration
|
||||
because these value_tables are inherited from the parent types for
|
||||
non-fundamental types which means that unless you want to write a
|
||||
fundamental type (not a great idea !), you will not need to provide
|
||||
a new value_table since you will inherit the value_table structure
|
||||
from your parent type.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="gtype-conventions">
|
||||
<title>Conventions</title>
|
||||
|
||||
|
||||
<para>
|
||||
There are a number of conventions users are expected to follow when creating new types
|
||||
which are to be exported in a header file:
|
||||
<itemizedlist>
|
||||
<listitem><para>
|
||||
Use the <function>object_method</function> pattern for function names: to invoke
|
||||
the method named foo on an instance of object type bar, call
|
||||
<function>bar_foo</function>.
|
||||
</para></listitem>
|
||||
<listitem><para>Use prefixing to avoid namespace conflicts with other projects.
|
||||
If your library (or application) is named <emphasis>Maman</emphasis>,
|
||||
prefix all your function names with <emphasis>maman_</emphasis>.
|
||||
For example: <function>maman_object_method</function>.
|
||||
</para></listitem>
|
||||
<listitem><para>Create a macro named <function>PREFIX_OBJECT_TYPE</function> which always
|
||||
returns the Gtype for the associated object type. For an object of type
|
||||
<emphasis>Bar</emphasis> in a libray prefixed by <emphasis>maman</emphasis>,
|
||||
use: <function>MAMAN_BAR_TYPE</function>.
|
||||
It is common although not a convention to implement this macro using either a global
|
||||
static variable or a function named <function>prefix_object_get_type</function>.
|
||||
We will follow the function pattern wherever possible in this document.
|
||||
</para></listitem>
|
||||
<listitem><para>Create a macro named <function>PREFIX_OBJECT (obj)</function> which
|
||||
returns a pointer of type <type>PrefixObject</type>. This macro is used to enforce
|
||||
static type safety by doing explicit casts wherever needed. It also enforces
|
||||
dynamic type safety by doing runtime checks. It is expected that in production
|
||||
builds, the dynamic type checks are disabled: they should be used only in
|
||||
development environments. For example, we would create
|
||||
<function>MAMAN_BAR (obj)</function> to keep the previous example.
|
||||
</para></listitem>
|
||||
<listitem><para>If the type is classed, create a macro named
|
||||
<function>PREFIX_OBJECT_CLASS (klass)</function>. This macro
|
||||
is strictly equivalent to the previous casting macro: it does static casting with
|
||||
dynamic type checking of class structures. It is expected to return a pointer
|
||||
to a class structure of type <type>PrefixObjectClass</type>. Again, an example is:
|
||||
<function>MAMAN_BAR_CLASS</function>.</para></listitem>
|
||||
<listitem><para>Create a macro named <function>PREFIX_IS_BAR (obj)</function>: this macro is expected
|
||||
to return a <type>gboolean</type> which indicates whether or not the input
|
||||
object instance pointer of type BAR.</para></listitem>
|
||||
<listitem><para>If the type is classed, create a macro named
|
||||
<function>PREFIX_IS_OBJECT_CLASS (klass)</function> which, as above, returns a boolean
|
||||
if the input class pointer is a pointer to a class of type OBJECT.
|
||||
</para></listitem>
|
||||
<listitem><para>If the type is classed, create a macro named
|
||||
<function>PREFIX_OBJECT_GET_CLASS (obj)</function>
|
||||
which returns the class pointer associated to an instance of a given type. This macro
|
||||
is used for static and dynamic type safety purposes (just like the previous casting
|
||||
macros).</para></listitem>
|
||||
</itemizedlist>
|
||||
The implementation of these macros is pretty straightforward: a number of simple-to-use
|
||||
macros are provided in <filename>gtype.h</filename>. For the example we used above, we would
|
||||
write the following trivial code to declare the macros:
|
||||
<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))
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The following code shows how to implement the <function>maman_bar_get_type</function>
|
||||
function:
|
||||
<programlisting>
|
||||
GType maman_bar_get_type (void)
|
||||
{
|
||||
static GType type = 0;
|
||||
if (type == 0) {
|
||||
static const GTypeInfo info = {
|
||||
/* You fill this structure. */
|
||||
};
|
||||
type = g_type_register_static (G_TYPE_OBJECT,
|
||||
"MamanBarType",
|
||||
&info, 0);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="gtype-non-instantiable">
|
||||
<title>Non-Instantiable non-classed fundamental types</title>
|
||||
|
||||
<para>
|
||||
A lot of types are not instantiable by the type system and do not have
|
||||
a class. Most of these types are fundamental trivial types such as <emphasis>gchar</emphasis>,
|
||||
registered in <function>g_value_types_init</function> (in <filename>gvaluetypes.c</filename>).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To register such a type in the type system, you just need to fill the
|
||||
<type>GTypeInfo</type> structure with zeros since these types are also most of the time
|
||||
fundamental:
|
||||
<programlisting>
|
||||
GTypeInfo info = {
|
||||
0, /* class_size */
|
||||
NULL, /* base_init */
|
||||
NULL, /* base_destroy */
|
||||
NULL, /* class_init */
|
||||
NULL, /* class_destroy */
|
||||
NULL, /* class_data */
|
||||
0, /* instance_size */
|
||||
0, /* n_preallocs */
|
||||
NULL, /* instance_init */
|
||||
NULL, /* value_table */
|
||||
};
|
||||
static const GTypeValueTable value_table = {
|
||||
value_init_long0, /* value_init */
|
||||
NULL, /* value_free */
|
||||
value_copy_long0, /* value_copy */
|
||||
NULL, /* value_peek_pointer */
|
||||
"i", /* collect_format */
|
||||
value_collect_int, /* collect_value */
|
||||
"p", /* lcopy_format */
|
||||
value_lcopy_char, /* lcopy_value */
|
||||
};
|
||||
info.value_table = &value_table;
|
||||
type = g_type_register_fundamental (G_TYPE_CHAR, "gchar", &info, &finfo, 0);
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
Having non-instantiable types might seem a bit useless: what good is a type
|
||||
if you cannot instanciate an instance of that type ? Most of these types
|
||||
are used in conjunction with <type>GValue</type>s: a GValue is initialized
|
||||
with an integer or a string and it is passed around by using the registered
|
||||
type's value_table. <type>GValue</type>s (and by extension these trivial fundamental
|
||||
types) are most useful when used in conjunction with object properties and signals.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="gtype-instantiable-classed">
|
||||
<title>Instantiable classed types: objects</title>
|
||||
|
||||
<para>
|
||||
Types which are registered with a class and are declared instantiable are
|
||||
what most closely resembles an <emphasis>object</emphasis>. The code below
|
||||
shows how you could register such a type in the type system:
|
||||
<programlisting>
|
||||
typedef struct {
|
||||
Object parent;
|
||||
/* instance members */
|
||||
int field_a;
|
||||
} MamanBar;
|
||||
|
||||
struct _MamanBarClass {
|
||||
GObjectClass parent;
|
||||
/* class members */
|
||||
void (*do_action_public_virtual) (MamanBar *self, guint8 i);
|
||||
|
||||
void (*do_action_public_pure_virtual) (MamanBar *self, guint8 i);
|
||||
};
|
||||
|
||||
#define MAMAN_BAR_TYPE (maman_bar_get_type ())
|
||||
|
||||
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 */
|
||||
(GClassInitFunc) foo_class_init,
|
||||
NULL, /* class_finalize */
|
||||
NULL, /* class_data */
|
||||
sizeof (MamanBar),
|
||||
0, /* n_preallocs */
|
||||
(GInstanceInitFunc) NULL /* instance_init */
|
||||
};
|
||||
type = g_type_register_fundamental (G_TYPE_OBJECT,
|
||||
"BarType",
|
||||
&info, 0);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
</programlisting>
|
||||
Upon the first call to <function>maman_bar_get_type</function>, the type named
|
||||
<emphasis>BarType</emphasis> will be registered in the type system as inheriting
|
||||
from the type <emphasis>G_TYPE_OBJECT</emphasis>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Every object must define two structures: its class structure and its
|
||||
instance structure. All class structures must contain as first member
|
||||
a <type>GTypeClass</type> structure. All instance structures must contain as first
|
||||
member a <type>GTypeInstance</type> structure. The declaration of these C types,
|
||||
coming from <filename>gtype.h</filename> is shown below:
|
||||
<programlisting>
|
||||
struct _GTypeClass
|
||||
{
|
||||
GType g_type;
|
||||
};
|
||||
struct _GTypeInstance
|
||||
{
|
||||
GTypeClass *g_class;
|
||||
};
|
||||
</programlisting>
|
||||
These constraints allow the type system to make sure that every object instance
|
||||
(identified by a pointer to the object's instance structure) contains in its
|
||||
first bytes a pointer to the object's class structure.
|
||||
</para>
|
||||
<para>
|
||||
This relationship is best explained by an example: let's take object B which
|
||||
inherits from object A:
|
||||
<programlisting>
|
||||
/* A definitions */
|
||||
typedef struct {
|
||||
GTypeInstance parent;
|
||||
int field_a;
|
||||
int field_b;
|
||||
} A;
|
||||
typedef struct {
|
||||
GTypeClass parent_class;
|
||||
void (*method_a) (void);
|
||||
void (*method_b) (void);
|
||||
} AClass;
|
||||
|
||||
/* B definitions. */
|
||||
typedef struct {
|
||||
A parent;
|
||||
int field_c;
|
||||
int field_d;
|
||||
} B;
|
||||
typedef struct {
|
||||
AClass parent_class;
|
||||
void (*method_c) (void);
|
||||
void (*method_d) (void);
|
||||
} BClass;
|
||||
</programlisting>
|
||||
The C standard mandates that the first field of a C structure is stored starting
|
||||
in the first byte of the buffer used to hold the structure's fields in memory.
|
||||
This means that the first field of an instance of an object B is A's first field
|
||||
which in turn is GTypeInstance's first field which in turn is g_class, a pointer
|
||||
to B's class structure.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Thanks to these simple conditions, it is possible to detect the type of every
|
||||
object instance by doing:
|
||||
<programlisting>
|
||||
B *b;
|
||||
b->parent.parent_class->g_class.g_type
|
||||
</programlisting>
|
||||
or, more quickly:
|
||||
<programlisting>
|
||||
B *b;
|
||||
((GTypeInstance*)b)->g_class.g_type
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Instanciation of these types can be done with <function>g_type_create_instance</function>:
|
||||
<programlisting>
|
||||
GTypeInstance* g_type_create_instance (GType type);
|
||||
void g_type_free_instance (GTypeInstance *instance);
|
||||
</programlisting>
|
||||
<function>g_type_create_instance</function> will lookup the type information
|
||||
structure associated to the type requested. Then, the instance size and instanciation
|
||||
policy (if the n_preallocs field is set to a non-zero value, the type system allocates
|
||||
the object's instance structures in chunks rather than mallocing for every instance)
|
||||
declared by the user are used to get a buffer to hold the object's instance
|
||||
structure.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If this is the first instance of the object ever created, the type system must create
|
||||
a class structure: it allocates a buffer to hold the object's class structure and
|
||||
initializes it. It first copies the parent's class structure over this structure
|
||||
(if there is no parent, it initializes it to zero). It then invokes the
|
||||
base_class_initialization functions (<type>GBaseInitFunc</type>) from topmost
|
||||
fundamental object to bottom-most most derived object. The object's class_init
|
||||
(<type>GClassInitFunc</type>) function is invoked afterwards to complete
|
||||
initialization of the class structure.
|
||||
Finally, the object's interfaces are initialized (we will discuss interface initialization
|
||||
in more detail later).
|
||||
<footnote id="class-init">
|
||||
<para>
|
||||
The class initialization process is entirely implemented in
|
||||
<function>type_class_init_Wm</function> in <filename>gtype.c</filename>.
|
||||
</para>
|
||||
</footnote>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Once the type system has a pointer to an initialized class structure, it sets the object's
|
||||
instance class pointer to the object's class structure and invokes the object's
|
||||
instance_init (<type>GInstanceInitFunc</type>)functions, from top-most fundamental
|
||||
type to bottom-most most derived type.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Object instance destruction through <function>g_type_free_instance</function> is very simple:
|
||||
the instance structure is returned to the instance pool if there is one and if this was the
|
||||
last living instance of the object, the class is destroyed.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Class destruction (called finalization in Gtype) is the symmetric process of the initialization:
|
||||
it is implemented in <function>type_data_finalize_class_U</function> (in <filename>gtype.c
|
||||
</filename>, as usual...). Interfaces are first destroyed. Then, the most derived
|
||||
class_finalize (<type>ClassFinalizeFunc</type>) function is invoked. The
|
||||
base_class_finalize (<type>GBaseFinalizeFunc</type>) functions are
|
||||
Finally invoked from bottom-most most-derived type to top-most fundamental type and
|
||||
the class structure is freed.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
As many readers have now understood it, the base initialization/finalization process is
|
||||
very similar to the C++ Constructor/Destructor paradigm. However, it is very different
|
||||
in that, in C++, class constructors are automatically edited at compile
|
||||
time by the compiler to ensure that classes are correctly initialized before
|
||||
running the user-provided constructor code itself. With GObject, users must provide both
|
||||
the class and instance initialization functions.
|
||||
Similarly, GTypes have no instance destruction mechanism. It is
|
||||
the user's responsibility to implement correct destruction semantics on top
|
||||
of the existing GType code. (this is what GObject does. See
|
||||
<xref linkend="chapter-gobject"></xref>)
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The instanciation/finalization process can be summarized as follows:
|
||||
<table>
|
||||
<title>GType Instantiation/Finalization</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>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>First call to <function>g_type_create_instance</function> for target type</entry>
|
||||
<entry>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>
|
||||
</row>
|
||||
<row>
|
||||
<entry>First call to <function>g_type_create_instance</function> for target type</entry>
|
||||
<entry>target type's class_init function</entry>
|
||||
<entry>On target type's class structure</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>First call to <function>g_type_create_instance</function> for target type</entry>
|
||||
<entry colspan="2">interface initialization, see
|
||||
<xref linkend="gtype-non-instantiable-classed-init"></xref></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>Each call to <function>g_type_create_instance</function> for target type</entry>
|
||||
<entry>target type's instance_init function</entry>
|
||||
<entry>On object's instance</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>Last call to <function>g_type_free_instance</function> for target type</entry>
|
||||
<entry colspan="2">interface destruction, see
|
||||
<xref linkend="gtype-non-instantiable-classed-dest"></xref></entry>
|
||||
<entry></entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>Last call to <function>g_type_free_instance</function> for target type</entry>
|
||||
<entry>target type's class_finalize function</entry>
|
||||
<entry>On target type's class structure</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>Last call to <function>g_type_free_instance</function> for 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>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="gtype-non-instantiable-classed">
|
||||
<title>Non-instantiable classed types: Interfaces.</title>
|
||||
|
||||
<para>
|
||||
GType's Interfaces are very similar to Java's interfaces. To declare one of these
|
||||
you have to register a non-instantiable classed type which derives from
|
||||
GTypeInterface. The following piece of code declares such an interface.
|
||||
<programlisting>
|
||||
#define MAMAN_IBAZ_TYPE (maman_ibaz_get_type ())
|
||||
#define MAMAN_IBAZ(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_IBAZ_TYPE, MamanIbaz))
|
||||
#define MAMAN_IBAZ_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), MAMAN_IBAZ_TYPE, MamanIbazClass))
|
||||
#define MAMAN_IS_IBAZ(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_IBAZ_TYPE))
|
||||
#define MAMAN_IS_IBAZ_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), MAMAN_IBAZ_TYPE))
|
||||
#define MAMAN_IBAZ_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), MAMAN_IBAZ_TYPE, MamanIbazClass))
|
||||
|
||||
typedef struct _MamanIbaz MamanIbaz; /* dummy object */
|
||||
typedef struct _MamanIbazClass MamanIbazClass;
|
||||
|
||||
struct _MamanIbazClass {
|
||||
GTypeInterface parent;
|
||||
|
||||
void (*do_action) (MamanIbaz *self);
|
||||
};
|
||||
|
||||
GType maman_ibaz_get_type (void);
|
||||
|
||||
void maman_ibaz_do_action (MamanIbaz *self);
|
||||
</programlisting>
|
||||
The interface function, <function>maman_ibaz_do_action</function> is implemented
|
||||
in a pretty simple way:
|
||||
<programlisting>
|
||||
void maman_ibaz_do_action (MamanIbaz *self)
|
||||
{
|
||||
MAMAN_IBAZ_GET_CLASS (self)->do_action (self);
|
||||
}
|
||||
</programlisting>
|
||||
<function>maman_ibaz_get_gtype</function> registers a type named <emphasis>MamanIBaz</emphasis>
|
||||
which inherits from G_TYPE_INTERFACE. All interfaces must be children of G_TYPE_INTERFACE in the
|
||||
inheritance tree.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
An interface is defined by only one structure which must contain as first member
|
||||
a <type>GTypeInterface</type> structure. The interface structure is expected to
|
||||
contain the function pointers of the interface methods. It is good style to
|
||||
define helper functions for each of the interface methods which simply call
|
||||
the interface' method directly: <function>maman_ibaz_do_action</function>
|
||||
is one of these.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Once an interface type is registered, you must register implementations for these
|
||||
interfaces. The function named <function>maman_baz_get_type</function> registers
|
||||
a new GType named MamanBaz which inherits from <type>GObject</type> and which
|
||||
implements the interface <type>MamanIBaz</type>.
|
||||
<programlisting>
|
||||
static void maman_baz_do_action (MamanIbaz *self)
|
||||
{
|
||||
g_print ("Baz implementation of IBaz interface Action.\n");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
baz_interface_init (gpointer g_iface,
|
||||
gpointer iface_data)
|
||||
{
|
||||
MamanIbazClass *klass = (MamanIbazClass *)g_iface;
|
||||
klass->do_action = maman_baz_do_action;
|
||||
}
|
||||
|
||||
GType
|
||||
maman_baz_get_type (void)
|
||||
{
|
||||
static GType type = 0;
|
||||
if (type == 0) {
|
||||
static const GTypeInfo info = {
|
||||
sizeof (MamanBazClass),
|
||||
NULL, /* base_init */
|
||||
NULL, /* base_finalize */
|
||||
NULL, /* class_init */
|
||||
NULL, /* class_finalize */
|
||||
NULL, /* class_data */
|
||||
sizeof (MamanBaz),
|
||||
0, /* n_preallocs */
|
||||
NULL /* instance_init */
|
||||
};
|
||||
static const GInterfaceInfo ibaz_info = {
|
||||
(GInterfaceInitFunc) baz_interface_init, /* interface_init */
|
||||
NULL, /* interface_finalize */
|
||||
NULL /* interface_data */
|
||||
};
|
||||
type = g_type_register_static (G_TYPE_OBJECT,
|
||||
"MamanBazType",
|
||||
&info, 0);
|
||||
g_type_add_interface_static (type,
|
||||
MAMAN_IBAZ_TYPE,
|
||||
&ibaz_info);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<function>g_type_add_interface_static</function> records in the type system that
|
||||
a given type implements also <type>FooInterface</type>
|
||||
(<function>foo_interface_get_type</function> returns the type of
|
||||
<type>FooInterface</type>). The <type>GInterfaceInfo</type> structure holds
|
||||
information about the implementation of the interface:
|
||||
<programlisting>
|
||||
struct _GInterfaceInfo
|
||||
{
|
||||
GInterfaceInitFunc interface_init;
|
||||
GInterfaceFinalizeFunc interface_finalize;
|
||||
gpointer interface_data;
|
||||
};
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<sect2 id="gtype-non-instantiable-classed-init">
|
||||
<title>Interface Initialization</title>
|
||||
|
||||
<para>
|
||||
When an instantiable classed type which registered an interface implementation
|
||||
is created for the first time, its class structure is initialized following the process
|
||||
described in <xref linkend="gtype-instantiable-classed"></xref>. Once the class structure is
|
||||
initialized,the function <function>type_class_init_Wm</function> (implemented in <filename>
|
||||
gtype.c</filename>) initializes the interface implementations associated with
|
||||
that type by calling <function>type_iface_vtable_init_Wm</function> for each
|
||||
interface.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
First a memory buffer is allocated to hold the interface structure. The parent's
|
||||
interface structure is then copied over to the new interface structure (the parent
|
||||
interface is already initialized at that point). If there is no parent interface,
|
||||
the interface structure is initialized with zeros. The g_type and the g_instance_type
|
||||
fields are then initialized: g_type is set to the type of the most-derived interface
|
||||
and g_instance_type is set to the type of the most derived type which implements
|
||||
this interface.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Finally, the interface' most-derived <function>base_init</function> function and then
|
||||
the implementation's <function>interface_init</function>
|
||||
function are invoked. It is important to understand that if there are multiple
|
||||
implementations of an interface the <function>base_init</function> and
|
||||
<function>interface_init</function> functions will be
|
||||
invoked once for each implementation initialized.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
It is thus common for base_init functions to hold a local static boolean variable
|
||||
which makes sure that the interface type is initialized only once even if there are
|
||||
multiple implementations of the interface:
|
||||
<programlisting>
|
||||
static void
|
||||
maman_ibaz_base_init (gpointer g_class)
|
||||
{
|
||||
static gboolean initialized = FALSE;
|
||||
|
||||
if (!initialized) {
|
||||
/* create interface signals here. */
|
||||
initialized = TRUE;
|
||||
}
|
||||
}
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you have found the stuff about interface hairy, you are right: it is hairy but
|
||||
there is not much I can do about it. What I can do is summarize what you need to know
|
||||
about interfaces:
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The above process can be summarized as follows:
|
||||
<table>
|
||||
<title><function>Interface Initialization</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_type_create_instance</function> for type
|
||||
implementing interface</entry>
|
||||
<entry>interface' base_init function</entry>
|
||||
<entry>On interface' vtable</entry>
|
||||
<entry>Register interface' signals here (use a local static
|
||||
boolean variable as described above to make sure not to register them
|
||||
twice.).</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>First call to <function>g_type_create_instance</function> for type
|
||||
implementing interface</entry>
|
||||
<entry>interface' interface_init function</entry>
|
||||
<entry>On interface' vtable</entry>
|
||||
<entry>
|
||||
Initialize interface' implementation. That is, initialize the interface
|
||||
method pointers in the interface structure to the function's implementation.
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
It is highly unlikely (ie: I do not know of <emphasis>anyone</emphasis> who actually
|
||||
used it) you will ever need other more fancy things such as the ones described in the
|
||||
following section (<xref linkend="gtype-non-instantiable-classed-dest"></xref>).
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="gtype-non-instantiable-classed-dest">
|
||||
<title>Interface Destruction</title>
|
||||
|
||||
<para>
|
||||
When the last instance of an instantiable type which registered an interface implementation
|
||||
is destroyed, the interface's implementations associated to the type are destroyed by
|
||||
<function>type_iface_vtable_finalize_Wm</function> (in <filename>gtype.c</filename>).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<function>type_iface_vtable_finalize_Wm</function> invokes first the implementation's
|
||||
<function>interface_finalize</function> function and then the interface's most-derived
|
||||
<function>base_finalize</function> function.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Again, it is important to understand, as in
|
||||
<xref linkend="gtype-non-instantiable-classed-init"></xref>,
|
||||
that both <function>interface_finalize</function> and <function>base_finalize</function>
|
||||
are invoked exactly once for the destruction of each implementation of an interface. Thus,
|
||||
if you were to use one of these functions, you would need to use a static integer variable
|
||||
which would hold the number of instances of implementations of an interface such that
|
||||
the interface's class is destroyed only once (when the integer variable reaches zero).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The above process can be summarized as follows:
|
||||
<table>
|
||||
<title><function>Interface Finalization</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>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>Last call to <function>g_type_free_instance</function> for type
|
||||
implementing interface</entry>
|
||||
<entry>interface' interface_finalize function</entry>
|
||||
<entry>On interface' vtable</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>Last call to <function>g_type_free_instance</function>for type
|
||||
implementing interface</entry>
|
||||
<entry>interface' base_finalize function</entry>
|
||||
<entry>On interface' vtable</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Now that you have read this section, you can forget about it. Please, forget it
|
||||
<emphasis>as soon as possible</emphasis>.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
1452
docs/reference/gobject/tut_howto.xml
Normal file
1452
docs/reference/gobject/tut_howto.xml
Normal file
File diff suppressed because it is too large
Load Diff
160
docs/reference/gobject/tut_intro.xml
Normal file
160
docs/reference/gobject/tut_intro.xml
Normal file
@ -0,0 +1,160 @@
|
||||
<chapter>
|
||||
<title>Introduction</title>
|
||||
|
||||
<para>
|
||||
GObject, and its lower-level type system, GType, are used by GTK+ and most Gnome libraries to
|
||||
provide:
|
||||
<itemizedlist>
|
||||
<listitem><para>object-oriented C-based APIs and</para></listitem>
|
||||
<listitem><para>automatic transparent API bindings to other compiled
|
||||
or interpreted languages.</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>A lot of programmers are used to work with compiled-only or dynamically interpreted-only
|
||||
languages and do not understand the challenges associated with cross-language interoperability.
|
||||
This introduction tries to provide an insight into these challenges and describes briefly
|
||||
the solution choosen by GLib.
|
||||
</para>
|
||||
|
||||
<sect1>
|
||||
<title>Data types and programming</title>
|
||||
|
||||
<para>
|
||||
One could say (I have seen such definitions used in some textbooks on programming language theory)
|
||||
that a programming language is merely a way to create data types and manipulate them. Most languages
|
||||
provide a number of language-native types and a few primitives to create more complex types based
|
||||
on these primitive types.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In C, the language provides types such as <emphasis>char</emphasis>, <emphasis>long</emphasis>,
|
||||
<emphasis>pointer</emphasis>. During compilation of C code, the compiler maps these
|
||||
language types to the compiler's target architecture machine types. If you are using a C interpreter
|
||||
(I have never seen one myself but it is possible :), the interpreter (the program which interprets
|
||||
the source code and executes it) maps the language types to the machine types of the target machine at
|
||||
runtime, during the program execution (or just before execution if it uses a Just In Time compiler engine).
|
||||
</para>
|
||||
|
||||
<para>Perl and Python which are interpreted languages do not really provide type definitions similar
|
||||
to those used by C. Perl and Python programmers manipulate variables and the type of the variables
|
||||
is decided only upon the first assignment or upon the first use which forces a type on the variable.
|
||||
The interpreter also often provides a lot of automatic conversions from one type to the other. For example,
|
||||
in Perl, a variable which holds an integer can be automatically converted to a string given the
|
||||
required context:
|
||||
<programlisting>
|
||||
my $tmp = 10;
|
||||
print "this is an integer converted to a string:" . $tmp . "\n";
|
||||
</programlisting>
|
||||
Of course, it is also often possible to explicitely specify conversions when the default conversions provided
|
||||
by the language are not intuitive.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Exporting a C API</title>
|
||||
|
||||
<para>C APIs are defined by a set of functions and global variables which are usually exported from a
|
||||
binary. C functions have an arbitrary number of arguments and one return value. Each function is thus
|
||||
uniquely identified by the function name and the set of C types which describe the function arguments
|
||||
and return value. The global variables exported by the API are similarly identified by their name and
|
||||
their type.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
A C API is thus merely defined by a set of names to which a set of types are associated. If you know the
|
||||
function calling convention and the mapping of the C types to the machine types used by the platform you
|
||||
are on, you can resolve the name of each function to find where the code associated to this function
|
||||
is located in memory, and then construct a valid argument list for the function. Finally, all you have to
|
||||
do is triger a call to the target C function with the argument list.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
For the sake of discussion, here is a sample C function and the associated 32 bit x86
|
||||
assembly code generated by gcc on my linux box:
|
||||
<programlisting>
|
||||
static void function_foo (int foo)
|
||||
{}
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
|
||||
function_foo (10);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
push $0xa
|
||||
call 0x80482f4 <function_foo>
|
||||
</programlisting>
|
||||
The assembly code shown above is pretty straightforward: the first instruction pushes
|
||||
the hexadecimal value 0xa (decimal value 10) as a 32 bit integer on the stack and calls
|
||||
<function>function_foo</function>. As you can see, C function calls are implemented by
|
||||
gcc by native function calls (this is probably the fastest implementation possible).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Now, let's say we want to call the C function <function>function_foo</function> from
|
||||
a python program. To do this, the python interpreter needs to:
|
||||
<itemizedlist>
|
||||
<listitem><para>Find where the function is located. This means probably find the binary generated by the C compiler
|
||||
which exports this functions.</para></listitem>
|
||||
<listitem><para>Load the code of the function in executable memory.</para></listitem>
|
||||
<listitem><para>Convert the python parameters to C-compatible parameters before calling
|
||||
the function.</para></listitem>
|
||||
<listitem><para>Call the function with the right calling convention</para></listitem>
|
||||
<listitem><para>Convert the return values of the C function to python-compatible
|
||||
variables to return them to the python code.</para></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>The process described above is pretty complex and there are a lot of ways to make it entirely automatic
|
||||
and transparent to the C and the Python programmers:
|
||||
<itemizedlist>
|
||||
<listitem><para>The first solution is to write by hand a lot of glue code, once for each function exported or imported,
|
||||
which does the python to C parameter conversion and the C to python return value conversion. This glue code is then
|
||||
linked with the interpreter which allows python programs to call a python functions which delegates the work to the
|
||||
C function.</para></listitem>
|
||||
<listitem><para>Another nicer solution is to automatically generate the glue code, once for each function exported or
|
||||
imported, with a special compiler which
|
||||
reads the original function signature.</para></listitem>
|
||||
<listitem><para>The solution used by GLib is to use the GType library which holds at runtime a description of
|
||||
all the objects manipulated by the programmer. This so-called <emphasis>dynamic type</emphasis><footnote>
|
||||
<para>
|
||||
There are numerous different implementations of dynamic type systems: all C++
|
||||
compilers have one, Java and .NET have one too. A dynamic type system allows you
|
||||
to get information about every instantiated object at runtime. It can be implemented
|
||||
by a process-specific database: every new object created registers the characteristics
|
||||
of its associated type in the type system. It can also be implemented by introspection
|
||||
interfaces. The common point between all these different type systems and implementations
|
||||
is that they all allow you to query for object metadata at runtime.
|
||||
</para>
|
||||
</footnote>
|
||||
|
||||
library is then
|
||||
used by special generic glue code to automatically convert function parameters and function caling conventions
|
||||
between different runtime domains.</para></listitem>
|
||||
</itemizedlist>
|
||||
The greatest advantage of the solution implemented by GType is that the glue code sitting at the runtime domain
|
||||
boundaries is written once: the figure below states this more clearly.
|
||||
<figure>
|
||||
<mediaobject>
|
||||
<imageobject> <!-- this is for HTML output -->
|
||||
<imagedata fileref="glue.png" format="png" align="center"/>
|
||||
</imageobject>
|
||||
<imageobject> <!-- this is for PDF output -->
|
||||
<imagedata fileref="glue.jpg" format="jpg" align="center"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
</figure>
|
||||
|
||||
Currently, there exist at least Python and Perl glue code which makes it possible to use
|
||||
C objects written with GType directly in Python or Perl, without any further work.
|
||||
</para>
|
||||
|
||||
|
||||
</sect1>
|
||||
|
||||
|
||||
</chapter>
|
Loading…
Reference in New Issue
Block a user