mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-27 07:56:14 +01:00
parent
390d9a446d
commit
0d05b87b9f
1846
docs/reference/gobject/concepts.md
Normal file
1846
docs/reference/gobject/concepts.md
Normal file
File diff suppressed because it is too large
Load Diff
@ -15,58 +15,6 @@
|
|||||||
</releaseinfo>
|
</releaseinfo>
|
||||||
</bookinfo>
|
</bookinfo>
|
||||||
|
|
||||||
<preface>
|
|
||||||
<title>Introduction</title>
|
|
||||||
<para>
|
|
||||||
Most modern programming languages come with their own native object
|
|
||||||
systems and additional fundamental algorithmic language constructs.
|
|
||||||
Just as GLib serves as an implementation of such fundamental
|
|
||||||
types and algorithms (linked lists, hash tables and so forth), the
|
|
||||||
GLib Object System provides the required implementations of a
|
|
||||||
flexible, extensible, and intentionally easy to map (into other
|
|
||||||
languages) object-oriented framework for C.
|
|
||||||
The substantial elements that are provided can be summarized as:
|
|
||||||
<itemizedlist>
|
|
||||||
<listitem><para>
|
|
||||||
A generic type system to register arbitrary single-inherited
|
|
||||||
flat and deep derived types as well as interfaces for
|
|
||||||
structured types.
|
|
||||||
It takes care of creation, initialization and memory management
|
|
||||||
of the assorted object and class structures, maintains
|
|
||||||
parent/child relationships and deals with dynamic implementations
|
|
||||||
of such types. That is, their type specific implementations are
|
|
||||||
relocatable/unloadable during runtime.
|
|
||||||
</para></listitem>
|
|
||||||
<listitem><para>
|
|
||||||
A collection of fundamental type implementations, such as integers,
|
|
||||||
doubles, enums and structured types, to name a few.
|
|
||||||
</para></listitem>
|
|
||||||
<listitem><para>
|
|
||||||
A sample fundamental type implementation to base object hierarchies
|
|
||||||
upon - the GObject fundamental type.
|
|
||||||
</para></listitem>
|
|
||||||
<listitem><para>
|
|
||||||
A signal system that allows very flexible user customization of
|
|
||||||
virtual/overridable object methods and can serve as a powerful
|
|
||||||
notification mechanism.
|
|
||||||
</para></listitem>
|
|
||||||
<listitem><para>
|
|
||||||
An extensible parameter/value system, supporting all the provided
|
|
||||||
fundamental types that can be used to generically handle object
|
|
||||||
properties or otherwise parameterized types.
|
|
||||||
</para></listitem>
|
|
||||||
</itemizedlist>
|
|
||||||
</para>
|
|
||||||
</preface>
|
|
||||||
|
|
||||||
<part label="I">
|
|
||||||
<title>Concepts</title>
|
|
||||||
|
|
||||||
<xi:include href="tut_intro.xml" />
|
|
||||||
<xi:include href="tut_gtype.xml" />
|
|
||||||
<xi:include href="tut_gobject.xml" />
|
|
||||||
<xi:include href="tut_gsignal.xml" />
|
|
||||||
</part>
|
|
||||||
<reference label="II">
|
<reference label="II">
|
||||||
<title>API Reference</title>
|
<title>API Reference</title>
|
||||||
|
|
||||||
|
@ -42,12 +42,14 @@ show_class_hierarchy = true
|
|||||||
urlmap_file = "urlmap.js"
|
urlmap_file = "urlmap.js"
|
||||||
# The same order will be used when generating the index
|
# The same order will be used when generating the index
|
||||||
content_files = [
|
content_files = [
|
||||||
|
"concepts.md",
|
||||||
"floating-refs.md",
|
"floating-refs.md",
|
||||||
"boxed.md",
|
"boxed.md",
|
||||||
"enum-types.md",
|
"enum-types.md",
|
||||||
"gvalue.md",
|
"gvalue.md",
|
||||||
]
|
]
|
||||||
content_images = [
|
content_images = [
|
||||||
|
"images/glue.png",
|
||||||
]
|
]
|
||||||
|
|
||||||
# This is the anonymous union inside GValue; we don't need it
|
# This is the anonymous union inside GValue; we don't need it
|
||||||
|
@ -38,11 +38,7 @@ if get_option('gtk_doc')
|
|||||||
'glib-mkenums.xml',
|
'glib-mkenums.xml',
|
||||||
'glib-genmarshal.xml',
|
'glib-genmarshal.xml',
|
||||||
'gobject-query.xml',
|
'gobject-query.xml',
|
||||||
'tut_gobject.xml',
|
|
||||||
'tut_gsignal.xml',
|
|
||||||
'tut_gtype.xml',
|
|
||||||
'tut_howto.xml',
|
'tut_howto.xml',
|
||||||
'tut_intro.xml',
|
|
||||||
'tut_tools.xml'
|
'tut_tools.xml'
|
||||||
],
|
],
|
||||||
html_assets : [
|
html_assets : [
|
||||||
@ -72,6 +68,7 @@ endif
|
|||||||
# gi-docgen version
|
# gi-docgen version
|
||||||
expand_content_files = [
|
expand_content_files = [
|
||||||
'boxed.md',
|
'boxed.md',
|
||||||
|
'concepts.md',
|
||||||
'enum-types.md',
|
'enum-types.md',
|
||||||
'floating-refs.md',
|
'floating-refs.md',
|
||||||
'gvalue.md',
|
'gvalue.md',
|
||||||
|
@ -1,728 +0,0 @@
|
|||||||
<?xml version='1.0' encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
|
||||||
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
|
|
||||||
]>
|
|
||||||
<chapter id="chapter-gobject">
|
|
||||||
<title>The GObject base class</title>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The previous chapter discussed the details of GLib's Dynamic Type System.
|
|
||||||
The GObject library also contains an implementation for a base fundamental
|
|
||||||
type named <link linkend="GObject"><type>GObject</type></link>.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
<link linkend="GObject"><type>GObject</type></link> is a fundamental classed instantiatable 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 GNOME libraries which use the GLib type system (like GTK and GStreamer)
|
|
||||||
inherit from <link linkend="GObject"><type>GObject</type></link> which is why it is important to understand
|
|
||||||
the details of how it works.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<sect1 id="gobject-instantiation">
|
|
||||||
<title>Object instantiation</title>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The <function><link linkend="g-object-new">g_object_new</link></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 and
|
|
||||||
instance structures have 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 and clear memory through <function><link linkend="g-type-create-instance">g_type_create_instance</link></function>,
|
|
||||||
</para></listitem>
|
|
||||||
<listitem><para>
|
|
||||||
Initialize the object's instance with the construction properties.
|
|
||||||
</para></listitem>
|
|
||||||
</itemizedlist>
|
|
||||||
Although one can expect all class and instance members (except the fields
|
|
||||||
pointing to the parents) to be set to zero, some consider it good practice
|
|
||||||
to explicitly set them.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Once all construction operations have been completed and constructor
|
|
||||||
properties set, the constructed class method is called.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Objects which inherit from GObject are allowed to override this
|
|
||||||
constructed class method.
|
|
||||||
The example below shows how <type>ViewerFile</type> overrides the parent's construction process:
|
|
||||||
<informalexample><programlisting>
|
|
||||||
#define VIEWER_TYPE_FILE viewer_file_get_type ()
|
|
||||||
G_DECLARE_FINAL_TYPE (ViewerFile, viewer_file, VIEWER, FILE, GObject)
|
|
||||||
|
|
||||||
struct _ViewerFile
|
|
||||||
{
|
|
||||||
GObject parent_instance;
|
|
||||||
|
|
||||||
/* instance members */
|
|
||||||
gchar *filename;
|
|
||||||
guint zoom_level;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* will create viewer_file_get_type and set viewer_file_parent_class */
|
|
||||||
G_DEFINE_TYPE (ViewerFile, viewer_file, G_TYPE_OBJECT)
|
|
||||||
|
|
||||||
static void
|
|
||||||
viewer_file_constructed (GObject *obj)
|
|
||||||
{
|
|
||||||
/* update the object state depending on constructor properties */
|
|
||||||
|
|
||||||
/* Always chain up to the parent constructed function to complete object
|
|
||||||
* initialisation. */
|
|
||||||
G_OBJECT_CLASS (viewer_file_parent_class)->constructed (obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
viewer_file_finalize (GObject *obj)
|
|
||||||
{
|
|
||||||
ViewerFile *self = VIEWER_FILE (obj);
|
|
||||||
|
|
||||||
g_free (self->filename);
|
|
||||||
|
|
||||||
/* Always chain up to the parent finalize function to complete object
|
|
||||||
* destruction. */
|
|
||||||
G_OBJECT_CLASS (viewer_file_parent_class)->finalize (obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
viewer_file_class_init (ViewerFileClass *klass)
|
|
||||||
{
|
|
||||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
||||||
|
|
||||||
object_class->constructed = viewer_file_constructed;
|
|
||||||
object_class->finalize = viewer_file_finalize;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
viewer_file_init (ViewerFile *self)
|
|
||||||
{
|
|
||||||
/* initialize the object */
|
|
||||||
}
|
|
||||||
|
|
||||||
</programlisting></informalexample>
|
|
||||||
If the user instantiates an object <type>ViewerFile</type> with:
|
|
||||||
<informalexample><programlisting>
|
|
||||||
ViewerFile *file = g_object_new (VIEWER_TYPE_FILE, NULL);
|
|
||||||
</programlisting></informalexample>
|
|
||||||
If this is the first instantiation of such an object, the
|
|
||||||
<function>viewer_file_class_init</function> function will be invoked
|
|
||||||
after any <function>viewer_file_base_class_init</function> function.
|
|
||||||
This will make sure the class structure of this new object is
|
|
||||||
correctly initialized. Here, <function>viewer_file_class_init</function>
|
|
||||||
is expected to override the object's class methods and setup the
|
|
||||||
class' own methods. In the example above, the <literal>constructed</literal>
|
|
||||||
method is the only overridden method: it is set to
|
|
||||||
<function>viewer_file_constructed</function>.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Once <function><link linkend="g-object-new">g_object_new</link></function> has obtained a reference to an initialized
|
|
||||||
class structure, it invokes its constructor method to create an instance of the new
|
|
||||||
object, if the constructor has been overridden in <function>viewer_file_class_init</function>.
|
|
||||||
Overridden constructors must chain up to their parent’s constructor. In
|
|
||||||
order to find the parent class and chain up to the parent class
|
|
||||||
constructor, we can use the <literal>viewer_file_parent_class</literal>
|
|
||||||
pointer that has been set up for us by the
|
|
||||||
<link linkend="G-DEFINE-TYPE:CAPS"><literal>G_DEFINE_TYPE</literal></link>
|
|
||||||
macro.
|
|
||||||
</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><link linkend="g-type-create-instance">g_type_create_instance</link></function>
|
|
||||||
which means that the <function>instance_init</function> function is invoked at this point if one
|
|
||||||
was registered. After <function>instance_init</function> returns, the object is fully initialized and should be
|
|
||||||
ready to have its methods called by the user. When
|
|
||||||
<function><link linkend="g-type-create-instance">g_type_create_instance</link></function>
|
|
||||||
returns, <function>g_object_constructor</function> sets the construction properties
|
|
||||||
(i.e. the properties which were given to <function><link linkend="g-object-new">g_object_new</link></function>) and returns
|
|
||||||
to the user's constructor.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The process described above might seem a bit complicated, but it can be
|
|
||||||
summarized easily by the table below which lists the functions invoked
|
|
||||||
by <function><link linkend="g-object-new">g_object_new</link></function>
|
|
||||||
and their order of invocation:
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
<table id="gobject-construction-table">
|
|
||||||
<title><function><link linkend="g-object-new">g_object_new</link></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 morerows="3">First call to <function><link linkend="g-object-new">g_object_new</link></function> for target type</entry>
|
|
||||||
<entry>target type's <function>base_init</function> function</entry>
|
|
||||||
<entry>On the inheritance tree of classes from fundamental type to target type.
|
|
||||||
<function>base_init</function> is invoked once for each class structure.</entry>
|
|
||||||
<entry>Never used in practice. Unlikely you will need it.</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<!--entry>First call to <function><link linkend="g-object-new">g_object_new</link></function> for target type</entry-->
|
|
||||||
<entry>target type's <function>class_init</function> 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><link linkend="g-object-new">g_object_new</link></function> for target type</entry-->
|
|
||||||
<entry>interface's <function>base_init</function> function</entry>
|
|
||||||
<entry>On interface's vtable</entry>
|
|
||||||
<entry></entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<!--entry>First call to <function><link linkend="g-object-new">g_object_new</link></function> for target type</entry-->
|
|
||||||
<entry>interface's <function>interface_init</function> function</entry>
|
|
||||||
<entry>On interface's vtable</entry>
|
|
||||||
<entry></entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry morerows="2">Each call to <function><link linkend="g-object-new">g_object_new</link></function> for target type</entry>
|
|
||||||
<entry>target type's class <function>constructor</function> method: <function>GObjectClass->constructor</function></entry>
|
|
||||||
<entry>On object's instance</entry>
|
|
||||||
<entry>
|
|
||||||
If you need to handle construct properties in a custom way, or implement a singleton class, 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><link linkend="g-object-new">g_object_new</link></function> for target type</entry-->
|
|
||||||
<entry>type's <function>instance_init</function> function</entry>
|
|
||||||
<entry>On the inheritance tree of classes from fundamental type to target type.
|
|
||||||
the <function>instance_init</function> provided for each type is invoked once for each instance
|
|
||||||
structure.</entry>
|
|
||||||
<entry>
|
|
||||||
Provide an <function>instance_init</function> 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>
|
|
||||||
<row>
|
|
||||||
<!--entry>Each call to <function><link linkend="g-object-new">g_object_new</link></function> for target type</entry-->
|
|
||||||
<entry>target type's class <function>constructed</function> method: <function>GObjectClass->constructed</function></entry>
|
|
||||||
<entry>On object's instance</entry>
|
|
||||||
<entry>
|
|
||||||
If you need to perform object initialization steps after all construct properties have been set.
|
|
||||||
This is the final step in the object initialization process, and is only called if the <function>constructor</function>
|
|
||||||
method returned a new object instance (rather than, for example, an existing singleton).
|
|
||||||
</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 <function>instance_init</function>
|
|
||||||
function (since <function><link linkend="g-type-create-instance">g_type_create_instance</link></function> which calls <function>instance_init</function> 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 <function>instance_init</function> 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). The methods which are used to
|
|
||||||
manipulate this reference count are described below.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<sect2 id="gobject-memory-refcount">
|
|
||||||
<title>Reference count</title>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The functions <function><link linkend="g-object-ref">g_object_ref</link></function>/<function><link linkend="g-object-unref">g_object_unref</link></function> respectively
|
|
||||||
increase and decrease the reference count. These functions are
|
|
||||||
thread-safe.
|
|
||||||
<function><link linkend="g-clear-object">g_clear_object</link></function>
|
|
||||||
is a convenience wrapper around <function>g_object_unref</function>
|
|
||||||
which also clears the pointer passed to it.
|
|
||||||
</para>
|
|
||||||
<para>
|
|
||||||
The reference count is initialized to one by
|
|
||||||
<function><link linkend="g-object-new">g_object_new</link></function> which means that the caller
|
|
||||||
is currently the sole owner of the newly-created reference. (If the object is derived from <link linkend="GInitiallyUnowned"><type>GInitiallyUnowned</type></link>, this reference count is <link linkend="floating-ref">floating</link>.)
|
|
||||||
When the reference count reaches zero, that is,
|
|
||||||
when <function><link linkend="g-object-unref">g_object_unref</link></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><link linkend="g-type-free-instance">g_type_free_instance</link></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-instantiatable-classed"/> and
|
|
||||||
<xref linkend="gtype-non-instantiatable-non-classed"/>.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The table below summarizes the destruction process of a GObject:
|
|
||||||
<table id="gobject-destruction-table">
|
|
||||||
<title><function><link linkend="g-object-unref">g_object_unref</link></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 morerows="1">Last call to <function><link linkend="g-object-unref">g_object_unref</link></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><link linkend="g-object-unref">g_object_unref</link></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"/>.
|
|
||||||
</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<entry morerows="3">Last call to <function><link linkend="g-object-unref">g_object_unref</link></function> for the last
|
|
||||||
instance of target type
|
|
||||||
</entry>
|
|
||||||
<entry>interface's <function>interface_finalize</function> function</entry>
|
|
||||||
<entry>On interface's vtable</entry>
|
|
||||||
<entry>Never used in practice. Unlikely you will need it.</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<!--entry>Last call to <function><link linkend="g-object-unref">g_object_unref</link></function>for the last
|
|
||||||
instance of target type
|
|
||||||
</entry-->
|
|
||||||
<entry>interface's <function>base_finalize</function> function</entry>
|
|
||||||
<entry>On interface's vtable</entry>
|
|
||||||
<entry>Never used in practice. Unlikely you will need it.</entry>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<!--entry>Last call to <function><link linkend="g-object-unref">g_object_unref</link></function> for the last
|
|
||||||
instance of target type
|
|
||||||
</entry-->
|
|
||||||
<entry>target type's <function>class_finalize</function> 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><link linkend="g-object-unref">g_object_unref</link></function> for the last
|
|
||||||
instance of target type
|
|
||||||
</entry-->
|
|
||||||
<entry>type's <function>base_finalize</function> function</entry>
|
|
||||||
<entry>On the inheritance tree of classes from fundamental type to target type.
|
|
||||||
<function>base_init</function> 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><link linkend="g-object-weak-ref">g_object_weak_ref</link></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><link linkend="g-object-weak-unref">g_object_weak_unref</link></function> can be used to remove a monitoring
|
|
||||||
callback from the object.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Weak references are also used to implement <function><link linkend="g-object-add-weak-pointer">g_object_add_weak_pointer</link></function>
|
|
||||||
and <function><link linkend="g-object-remove-weak-pointer">g_object_remove_weak_pointer</link></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>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Similarly, <link linkend="GWeakRef"><type>GWeakRef</type></link> can be
|
|
||||||
used to implement weak references if thread safety is required.
|
|
||||||
</para>
|
|
||||||
</sect2>
|
|
||||||
|
|
||||||
<sect2 id="gobject-memory-cycles">
|
|
||||||
<title>Reference counts and cycles</title>
|
|
||||||
|
|
||||||
<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 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><link linkend="g-object-run-dispose">g_object_run_dispose</link></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>
|
|
||||||
This explains one of the rules about the dispose handler stated earlier:
|
|
||||||
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><link linkend="g-object-run-dispose">g_object_run_dispose</link></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
|
|
||||||
GObjects are being handled by language bindings — hence the rules for
|
|
||||||
object destruction should be closely followed.
|
|
||||||
</para>
|
|
||||||
</sect2>
|
|
||||||
</sect1>
|
|
||||||
|
|
||||||
<sect1 id="gobject-properties">
|
|
||||||
<title>Object properties</title>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
One of GObject's nice features is its generic get/set mechanism for object
|
|
||||||
properties. When an object
|
|
||||||
is instantiated, the object's <function>class_init</function> handler should be used to register
|
|
||||||
the object's properties with <function><link linkend="g-object-class-install-properties">g_object_class_install_properties</link></function>.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The best way to understand how object properties work is by looking at a real example
|
|
||||||
of how it is used:
|
|
||||||
<informalexample><programlisting>
|
|
||||||
/************************************************/
|
|
||||||
/* Implementation */
|
|
||||||
/************************************************/
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
PROP_FILENAME = 1,
|
|
||||||
PROP_ZOOM_LEVEL,
|
|
||||||
N_PROPERTIES
|
|
||||||
} ViewerFileProperty;
|
|
||||||
|
|
||||||
static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
|
|
||||||
|
|
||||||
static void
|
|
||||||
viewer_file_set_property (GObject *object,
|
|
||||||
guint property_id,
|
|
||||||
const GValue *value,
|
|
||||||
GParamSpec *pspec)
|
|
||||||
{
|
|
||||||
ViewerFile *self = VIEWER_FILE (object);
|
|
||||||
|
|
||||||
switch ((ViewerFileProperty) property_id)
|
|
||||||
{
|
|
||||||
case PROP_FILENAME:
|
|
||||||
g_free (self->filename);
|
|
||||||
self->filename = g_value_dup_string (value);
|
|
||||||
g_print ("filename: %s\n", self->filename);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PROP_ZOOM_LEVEL:
|
|
||||||
self->zoom_level = g_value_get_uint (value);
|
|
||||||
g_print ("zoom level: %u\n", self->zoom_level);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
/* We don't have any other property... */
|
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
viewer_file_get_property (GObject *object,
|
|
||||||
guint property_id,
|
|
||||||
GValue *value,
|
|
||||||
GParamSpec *pspec)
|
|
||||||
{
|
|
||||||
ViewerFile *self = VIEWER_FILE (object);
|
|
||||||
|
|
||||||
switch ((ViewerFileProperty) property_id)
|
|
||||||
{
|
|
||||||
case PROP_FILENAME:
|
|
||||||
g_value_set_string (value, self->filename);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PROP_ZOOM_LEVEL:
|
|
||||||
g_value_set_uint (value, self->zoom_level);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
/* We don't have any other property... */
|
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
viewer_file_class_init (ViewerFileClass *klass)
|
|
||||||
{
|
|
||||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
||||||
|
|
||||||
object_class->set_property = viewer_file_set_property;
|
|
||||||
object_class->get_property = viewer_file_get_property;
|
|
||||||
|
|
||||||
obj_properties[PROP_FILENAME] =
|
|
||||||
g_param_spec_string ("filename",
|
|
||||||
"Filename",
|
|
||||||
"Name of the file to load and display from.",
|
|
||||||
NULL /* default value */,
|
|
||||||
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
|
||||||
|
|
||||||
obj_properties[PROP_ZOOM_LEVEL] =
|
|
||||||
g_param_spec_uint ("zoom-level",
|
|
||||||
"Zoom level",
|
|
||||||
"Zoom level to view the file at.",
|
|
||||||
0 /* minimum value */,
|
|
||||||
10 /* maximum value */,
|
|
||||||
2 /* default value */,
|
|
||||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
|
||||||
|
|
||||||
g_object_class_install_properties (object_class,
|
|
||||||
N_PROPERTIES,
|
|
||||||
obj_properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
/************************************************/
|
|
||||||
/* Use */
|
|
||||||
/************************************************/
|
|
||||||
|
|
||||||
ViewerFile *file;
|
|
||||||
GValue val = G_VALUE_INIT;
|
|
||||||
|
|
||||||
file = g_object_new (VIEWER_TYPE_FILE, NULL);
|
|
||||||
|
|
||||||
g_value_init (&val, G_TYPE_UINT);
|
|
||||||
g_value_set_char (&val, 11);
|
|
||||||
|
|
||||||
g_object_set_property (G_OBJECT (file), "zoom-level", &val);
|
|
||||||
|
|
||||||
g_value_unset (&val);
|
|
||||||
</programlisting></informalexample>
|
|
||||||
The client code above looks simple but a lot of things happen under the hood:
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
<function><link linkend="g-object-set-property">g_object_set_property</link></function> first ensures a property
|
|
||||||
with this name was registered in <emphasis>file</emphasis>'s <function>class_init</function> handler. If so it walks the class hierarchy,
|
|
||||||
from bottom-most most-derived type, to top-most fundamental type to find the class
|
|
||||||
which registered that property. It then tries to convert the user-provided
|
|
||||||
<link linkend="GValue"><type>GValue</type></link>
|
|
||||||
into a <type>GValue</type> whose type is that of the associated property.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
If the user provides a <type>signed char</type> <type>GValue</type>, as is shown
|
|
||||||
here, and if the object's property was registered as an <type>unsigned int</type>,
|
|
||||||
<function><link linkend="g-value-transform">g_value_transform</link></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 carried out if needed.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
After transformation, the <link linkend="GValue"><type>GValue</type></link> is validated by
|
|
||||||
<function><link linkend="g-param-value-validate">g_param_value_validate</link></function> which makes sure the user's
|
|
||||||
data stored in the <link linkend="GValue"><type>GValue</type></link> matches the characteristics specified by
|
|
||||||
the property's <link linkend="GParamSpec"><type>GParamSpec</type></link>.
|
|
||||||
Here, the <link linkend="GParamSpec"><type>GParamSpec</type></link> we
|
|
||||||
provided in <function>class_init</function> has a validation function which makes sure that the GValue
|
|
||||||
contains a value which respects the minimum and maximum bounds of the
|
|
||||||
<link linkend="GParamSpec"><type>GParamSpec</type></link>. 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><link linkend="g-object-set-property">g_object_set_property</link></function> function will return with an error.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
If the user's GValue had been set to a valid value, <function><link linkend="g-object-set-property">g_object_set_property</link></function>
|
|
||||||
would have proceeded with calling the object's
|
|
||||||
<function>set_property</function> class method. Here, since our
|
|
||||||
implementation of <type>ViewerFile</type> did override this method, execution would jump to
|
|
||||||
<function>viewer_file_set_property</function> after having retrieved from the
|
|
||||||
<link linkend="GParamSpec"><type>GParamSpec</type></link> the <emphasis>param_id</emphasis>
|
|
||||||
<footnote>
|
|
||||||
<para>
|
|
||||||
It should be noted that the param_id used here need only to uniquely identify each
|
|
||||||
<link linkend="GParamSpec"><type>GParamSpec</type></link> within the <type>ViewerFileClass</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><link linkend="g-object-class-install-property">g_object_class_install_property</link></function>.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Once the property has been set by the object's
|
|
||||||
<function>set_property</function> class method, execution
|
|
||||||
returns to <function><link linkend="g-object-set-property">g_object_set_property</link></function> which 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><link linkend="g-object-freeze-notify">g_object_freeze_notify</link></function>.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
<function><link linkend="g-object-thaw-notify">g_object_thaw_notify</link></function> can be used to re-enable notification of
|
|
||||||
property modifications through the
|
|
||||||
<link linkend="GObject-notify"><type>“notify”</type></link> 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 thawed: no property change is lost for the "notify"
|
|
||||||
signal, although multiple notifications for a single property are
|
|
||||||
compressed. Signals can only be delayed by the notification freezing
|
|
||||||
mechanism.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
It sounds like a tedious task to set up GValues every time when one wants to modify a property.
|
|
||||||
In practice one will rarely do this. The functions <function><link linkend="g-object-set-property">g_object_set_property</link></function>
|
|
||||||
and <function><link linkend="g-object-get-property">g_object_get_property</link></function>
|
|
||||||
are meant to be used by language bindings. For application there is an easier way and
|
|
||||||
that is described next.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<sect2 id="gobject-multi-properties">
|
|
||||||
<title>Accessing multiple properties at once</title>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
It is interesting to note that the <function><link linkend="g-object-set">g_object_set</link></function> and
|
|
||||||
<function><link linkend="g-object-set-valist">g_object_set_valist</link></function> (variadic version) functions can be used to set
|
|
||||||
multiple properties at once. The client code shown above can then be re-written as:
|
|
||||||
<informalexample><programlisting>
|
|
||||||
ViewerFile *file;
|
|
||||||
file = /* */;
|
|
||||||
g_object_set (G_OBJECT (file),
|
|
||||||
"zoom-level", 6,
|
|
||||||
"filename", "~/some-file.txt",
|
|
||||||
NULL);
|
|
||||||
</programlisting></informalexample>
|
|
||||||
This saves us from managing the GValues that we were needing to handle when using
|
|
||||||
<function><link linkend="g-object-set-property">g_object_set_property</link></function>.
|
|
||||||
The code above will trigger one notify signal emission for each property modified.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Equivalent <function>_get</function> versions are also available:
|
|
||||||
<function><link linkend="g-object-get">g_object_get</link></function>
|
|
||||||
and <function><link linkend="g-object-get-valist">g_object_get_valist</link></function> (variadic version) can be used to get numerous
|
|
||||||
properties at once.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
These high level functions have one drawback — they don't provide a return value.
|
|
||||||
One should pay attention to the argument types and ranges when using them.
|
|
||||||
A known source of errors is to pass a different type from what the
|
|
||||||
property expects; for instance, passing an integer when the property
|
|
||||||
expects a floating point value and thus shifting all subsequent parameters
|
|
||||||
by some number of bytes. Also forgetting the terminating
|
|
||||||
<literal>NULL</literal> will lead to undefined behaviour.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
This explains how <function><link linkend="g-object-new">g_object_new</link></function>,
|
|
||||||
<function><link linkend="g-object-newv">g_object_newv</link></function> and <function><link linkend="g-object-new-valist">g_object_new_valist</link></function>
|
|
||||||
work: they parse the user-provided variable number of parameters and invoke
|
|
||||||
<function><link linkend="g-object-set">g_object_set</link></function> on the parameters only after the object has been successfully constructed.
|
|
||||||
The "notify" signal will be emitted for each property set.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</sect2>
|
|
||||||
|
|
||||||
<!-- @todo tell here about how to pass use handle properties in derived classes -->
|
|
||||||
|
|
||||||
</sect1>
|
|
||||||
|
|
||||||
</chapter>
|
|
@ -1,495 +0,0 @@
|
|||||||
<?xml version='1.0' encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
|
||||||
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
|
|
||||||
]>
|
|
||||||
<chapter id="chapter-signal">
|
|
||||||
<title>The GObject messaging system</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:
|
|
||||||
<informalexample><programlisting>
|
|
||||||
return_type function_callback (… , gpointer user_data);
|
|
||||||
</programlisting></informalexample>
|
|
||||||
</para></listitem>
|
|
||||||
<listitem><para>
|
|
||||||
the <parameter>user_data</parameter> 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 <link linkend="GClosure"><type>GClosure</type></link> structure represents the common functionality of all
|
|
||||||
closure implementations: there exists 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 callbacks receives a signal from
|
|
||||||
a GTK widget, 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 <link linkend="GCClosure"><type>GCClosure</type></link> type which
|
|
||||||
is a specific implementation of closures to be used with C/C++ callbacks.
|
|
||||||
</para>
|
|
||||||
<para>
|
|
||||||
A <link linkend="GClosure"><type>GClosure</type></link> provides simple services:
|
|
||||||
<itemizedlist>
|
|
||||||
<listitem><para>
|
|
||||||
Invocation (<function><link linkend="g-closure-invoke">g_closure_invoke</link></function>): this is what closures
|
|
||||||
were created for: they hide the details of callback invocation from the
|
|
||||||
callback invoker.</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><link linkend="g-closure-add-finalize-notifier">g_closure_add_finalize_notifier</link></function>
|
|
||||||
(finalization notification), <function><link linkend="g-closure-add-invalidate-notifier">g_closure_add_invalidate_notifier</link></function>
|
|
||||||
(invalidation notification) and
|
|
||||||
<function><link linkend="g-closure-add-marshal-guards">g_closure_add_marshal_guards</link></function> (invocation notification).
|
|
||||||
There exist symmetric deregistration functions for finalization and invalidation
|
|
||||||
events (<function><link linkend="g-closure-remove-finalize-notifier">g_closure_remove_finalize_notifier</link></function> and
|
|
||||||
<function><link linkend="g-closure-remove-invalidate-notifier">g_closure_remove_invalidate_notifier</link></function>) but not for the invocation
|
|
||||||
process.
|
|
||||||
<footnote><para>
|
|
||||||
Closures are reference counted 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 simple <link linkend="GCClosure"><type>GCClosure</type></link>s
|
|
||||||
which have a pretty minimal API or the even simpler <function><link linkend="g-signal-connect">g_signal_connect</link></function>
|
|
||||||
functions (which will be presented a bit later).
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
<function><link linkend="g-cclosure-new">g_cclosure_new</link></function> will create a new closure which can invoke the
|
|
||||||
user-provided callback_func with the user-provided
|
|
||||||
<parameter>user_data</parameter> as its last parameter. When the closure
|
|
||||||
is finalized (second stage of the destruction process), it will invoke
|
|
||||||
the <parameter>destroy_data</parameter> function if the user has
|
|
||||||
supplied one.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
<function><link linkend="g-cclosure-new-swap">g_cclosure_new_swap</link></function> will create a new closure which can invoke the
|
|
||||||
user-provided <parameter>callback_func</parameter> with the
|
|
||||||
user-provided <parameter>user_data</parameter> as its first parameter
|
|
||||||
(instead of being the
|
|
||||||
last parameter as with <function><link linkend="g-cclosure-new">g_cclosure_new</link></function>). When the closure
|
|
||||||
is finalized (second stage of the destruction process), it will invoke
|
|
||||||
the <parameter>destroy_data</parameter> 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>
|
|
||||||
A generic C closure marshaller is available as
|
|
||||||
<link linkend="g-cclosure-marshal-generic"><function>g_cclosure_marshal_generic</function></link>
|
|
||||||
which implements marshalling for all function types using libffi. Custom
|
|
||||||
marshallers for different types are not needed apart from performance
|
|
||||||
critical code where the libffi-based marshaller may be too slow.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
An example of a custom marshaller is given below, illustrating how
|
|
||||||
<type>GValue</type>s can be converted to a C function call. The
|
|
||||||
marshaller is for a C function which takes an integer as its first
|
|
||||||
parameter and returns void.
|
|
||||||
<informalexample><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></informalexample>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
There exist other kinds of marshallers, for example there is a generic
|
|
||||||
Python marshaller which is used by all Python closures (a Python closure
|
|
||||||
is used to invoke a callback written in Python). This Python marshaller
|
|
||||||
transforms the input GValue list representing the function parameters
|
|
||||||
into a Python tuple which is the equivalent structure in Python.
|
|
||||||
</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 windowing system and generates a GTK event in the form of a signal emission
|
|
||||||
on the widget 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. The closure will be called synchronously on 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:
|
|
||||||
<informalexample><programlisting>
|
|
||||||
return_type function_callback (gpointer instance, …, gpointer user_data);
|
|
||||||
</programlisting></informalexample>
|
|
||||||
</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><link linkend="g-signal-newv">g_signal_newv</link></function>,
|
|
||||||
<function><link linkend="g-signal-new-valist">g_signal_new_valist</link></function> or <function><link linkend="g-signal-new">g_signal_new</link></function> functions:
|
|
||||||
<informalexample><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></informalexample>
|
|
||||||
The number of parameters to these functions is a bit intimidating but they are relatively
|
|
||||||
simple:
|
|
||||||
<itemizedlist>
|
|
||||||
<listitem><para>
|
|
||||||
<parameter>signal_name</parameter>: is a string which can be used to uniquely identify a given signal.
|
|
||||||
</para></listitem>
|
|
||||||
<listitem><para>
|
|
||||||
<parameter>itype</parameter>: is the instance type on which this signal can be emitted.
|
|
||||||
</para></listitem>
|
|
||||||
<listitem><para>
|
|
||||||
<parameter>signal_flags</parameter>: partly defines the order in which closures which were connected to the
|
|
||||||
signal are invoked.
|
|
||||||
</para></listitem>
|
|
||||||
<listitem><para>
|
|
||||||
<parameter>class_closure</parameter>: 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>
|
|
||||||
<parameter>accumulator</parameter>: 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.
|
|
||||||
For example, an accumulator could ignore
|
|
||||||
<literal>NULL</literal> returns from closures; or it
|
|
||||||
could build a list of the values returned by the
|
|
||||||
closures.
|
|
||||||
</para></listitem>
|
|
||||||
<listitem><para>
|
|
||||||
<parameter>accu_data</parameter>: this pointer will be passed down to each invocation of the
|
|
||||||
accumulator during emission.
|
|
||||||
</para></listitem>
|
|
||||||
<listitem><para>
|
|
||||||
<parameter>c_marshaller</parameter>: this is the default C marshaller for any closure which is connected to
|
|
||||||
this signal.
|
|
||||||
</para></listitem>
|
|
||||||
<listitem><para>
|
|
||||||
<parameter>return_type</parameter>: this is the type of the return value of the signal.
|
|
||||||
</para></listitem>
|
|
||||||
<listitem><para>
|
|
||||||
<parameter>n_params</parameter>: this is the number of parameters this signal takes.
|
|
||||||
</para></listitem>
|
|
||||||
<listitem><para>
|
|
||||||
<parameter>param_types</parameter>: 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 <emphasis>any</emphasis> of the instances of the type which supports that signal.
|
|
||||||
</para></listitem>
|
|
||||||
<listitem><para>
|
|
||||||
You can use <function><link linkend="g-signal-override-class-closure">g_signal_override_class_closure</link></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><link linkend="g-signal-connect">g_signal_connect</link></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><link linkend="g-signal-add-emission-hook">g_signal_add_emission_hook</link></function>
|
|
||||||
and removed with <function><link linkend="g-signal-remove-emission-hook">g_signal_remove_emission_hook</link></function>.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</sect2>
|
|
||||||
|
|
||||||
<sect2 id="signal-emission">
|
|
||||||
<title>Signal emission</title>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Signal emission is done through the use of the <function><link linkend="g-signal-emit">g_signal_emit</link></function> family
|
|
||||||
of functions.
|
|
||||||
<informalexample><programlisting>
|
|
||||||
void g_signal_emitv (const GValue *instance_and_params,
|
|
||||||
guint signal_id,
|
|
||||||
GQuark detail,
|
|
||||||
GValue *return_value);
|
|
||||||
</programlisting></informalexample>
|
|
||||||
<itemizedlist>
|
|
||||||
<listitem><para>
|
|
||||||
The <parameter>instance_and_params</parameter> 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>
|
|
||||||
<parameter>signal_id</parameter> identifies the signal to invoke.
|
|
||||||
</para></listitem>
|
|
||||||
<listitem><para>
|
|
||||||
<parameter>detail</parameter> 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>
|
|
||||||
<parameter>return_value</parameter> 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.
|
|
||||||
If no closure is invoked during
|
|
||||||
emission, the <parameter>return_value</parameter> is nonetheless initialized to zero/null.
|
|
||||||
</para></listitem>
|
|
||||||
</itemizedlist>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Signal emission is done synchronously and can be decomposed in 5 steps:
|
|
||||||
<orderedlist>
|
|
||||||
<listitem><para>
|
|
||||||
<literal>RUN_FIRST</literal>: if the
|
|
||||||
<link linkend="G-SIGNAL-RUN-FIRST:CAPS"><literal>G_SIGNAL_RUN_FIRST</literal></link> flag was used
|
|
||||||
during signal registration and if there exists a class closure for this signal,
|
|
||||||
the class closure is invoked.
|
|
||||||
</para></listitem>
|
|
||||||
<listitem><para>
|
|
||||||
<literal>EMISSION_HOOK</literal>: if any emission hook was added to
|
|
||||||
the signal, they are invoked from first to last added. Accumulate return values.
|
|
||||||
</para></listitem>
|
|
||||||
<listitem><para>
|
|
||||||
<literal>HANDLER_RUN_FIRST</literal>: if any closure were connected
|
|
||||||
with the <function><link linkend="g-signal-connect">g_signal_connect</link></function> family of
|
|
||||||
functions, and if they are not blocked (with the <function><link linkend="g-signal-handler-block">g_signal_handler_block</link></function>
|
|
||||||
family of functions) they are run here, from first to last connected.
|
|
||||||
</para></listitem>
|
|
||||||
<listitem><para>
|
|
||||||
<literal>RUN_LAST</literal>: if the <literal>G_SIGNAL_RUN_LAST</literal>
|
|
||||||
flag was set during registration and if a class closure
|
|
||||||
was set, it is invoked here.
|
|
||||||
</para></listitem>
|
|
||||||
<listitem><para>
|
|
||||||
<literal>HANDLER_RUN_LAST</literal>: if any closure were connected
|
|
||||||
with the <function>g_signal_connect_after</function> family of
|
|
||||||
functions, if they were not invoked during <literal>HANDLER_RUN_FIRST</literal> and if they
|
|
||||||
are not blocked, they are run here, from first to last connected.
|
|
||||||
</para></listitem>
|
|
||||||
<listitem><para>
|
|
||||||
<literal>RUN_CLEANUP</literal>: if the <literal>G_SIGNAL_RUN_CLEANUP</literal> flag
|
|
||||||
was set during registration and if a class closure was set,
|
|
||||||
it is invoked here. Signal emission is completed here.
|
|
||||||
</para></listitem>
|
|
||||||
</orderedlist>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
If, at any point during emission (except in <literal>RUN_CLEANUP</literal> or
|
|
||||||
<literal>EMISSION_HOOK</literal> state), one of the closures stops the signal emission with
|
|
||||||
<function><link linkend="g-signal-stop-emission">g_signal_stop_emission</link></function>,
|
|
||||||
emission jumps to <literal>RUN_CLEANUP</literal> 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 <literal>RUN_FIRST</literal> state.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The accumulator function is invoked in all states, after invocation
|
|
||||||
of each closure (except in <literal>RUN_EMISSION_HOOK</literal> and
|
|
||||||
<literal>RUN_CLEANUP</literal>). 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 <literal>RUN_CLEANUP</literal> state.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
If no accumulator function was provided, the value returned by the last handler
|
|
||||||
run will be returned by <function><link linkend="g-signal-emit">g_signal_emit</link></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, in one form or another.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Of the three main connection functions,
|
|
||||||
only one has an explicit detail parameter as a <link linkend="GQuark"><type>GQuark</type></link>:
|
|
||||||
<link linkend="g-signal-connect-closure-by-id"><function>g_signal_connect_closure_by_id</function></link>.
|
|
||||||
<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><link linkend="g-quark-from-string">g_quark_from_string</link></function> and <function><link linkend="g-quark-to-string">g_quark_to_string</link></function>.
|
|
||||||
</para>
|
|
||||||
</footnote>
|
|
||||||
</para>
|
|
||||||
<para>
|
|
||||||
The two other functions,
|
|
||||||
<link linkend="g-signal-connect-closure"><function>g_signal_connect_closure</function></link> and
|
|
||||||
<link linkend="g-signal-connect-data"><function>g_signal_connect_data</function></link>
|
|
||||||
hide the detail parameter in the signal name identification.
|
|
||||||
Their <parameter>detailed_signal</parameter> parameter is a
|
|
||||||
string which identifies the name of the signal to connect to.
|
|
||||||
The format of this string should match
|
|
||||||
<emphasis>signal_name::detail_name</emphasis>. For example,
|
|
||||||
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> detail.
|
|
||||||
Internally, the detail string is transformed to a GQuark if it is present.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Of the four main signal emission functions, one hides it in its
|
|
||||||
signal name parameter:
|
|
||||||
<link linkend="g-signal-connect"><function>g_signal_connect</function></link>.
|
|
||||||
The other three have an explicit detail parameter as a
|
|
||||||
<link linkend="GQuark"><type>GQuark</type></link> again:
|
|
||||||
<link linkend="g-signal-emit"><function>g_signal_emit</function></link>,
|
|
||||||
<link linkend="g-signal-emitv"><function>g_signal_emitv</function></link> and
|
|
||||||
<link linkend="g-signal-emit-valist"><function>g_signal_emit_valist</function></link>.
|
|
||||||
</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.
|
|
||||||
If a closure's detail does not match the detail provided by the user, it
|
|
||||||
will not be invoked (even though it is connected to a signal which is
|
|
||||||
being emitted).
|
|
||||||
</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 in before the closure's marshalling code runs. For example, this is used extensively
|
|
||||||
by the <link linkend="GObject-notify"><structfield>notify</structfield></link> 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 for that signal.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</sect2>
|
|
||||||
|
|
||||||
</sect1>
|
|
||||||
</chapter>
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
@ -1,185 +0,0 @@
|
|||||||
<?xml version='1.0' encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
|
||||||
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
|
|
||||||
]>
|
|
||||||
<chapter id="chapter-intro">
|
|
||||||
<title>Background</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 working 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 briefly describes
|
|
||||||
the solution chosen by GLib.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The following chapters go into greater detail into how GType and GObject work and
|
|
||||||
how you can use them as a C programmer. It is useful to keep in mind that
|
|
||||||
allowing access to C objects from other interpreted languages was one of the major design
|
|
||||||
goals: this can often explain the sometimes rather convoluted APIs and features present
|
|
||||||
in this library.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<sect1>
|
|
||||||
<title>Data types and programming</title>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
One could say
|
|
||||||
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
|
|
||||||
(assuming one exists), 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 are interpreted languages which 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:
|
|
||||||
<informalexample><programlisting>
|
|
||||||
my $tmp = 10;
|
|
||||||
print "this is an integer converted to a string:" . $tmp . "\n";
|
|
||||||
</programlisting></informalexample>
|
|
||||||
Of course, it is also often possible to explicitly 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 trigger 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 a Linux computer:
|
|
||||||
<informalexample><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></informalexample>
|
|
||||||
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 as 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 probably means finding the binary generated by the C compiler
|
|
||||||
which exports this function.</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 C and 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 Python functions which delegate work to
|
|
||||||
C functions.</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 calling 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 generic glue code which makes it possible to use
|
|
||||||
C objects written with GType directly in Python or Perl, with a minimum amount of work: there
|
|
||||||
is no need to generate huge amounts of glue code either automatically or by hand.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Although that goal was arguably laudable, its pursuit has had a major influence on
|
|
||||||
the whole GType/GObject library. C programmers are likely to be puzzled at the complexity
|
|
||||||
of the features exposed in the following chapters if they forget that the GType/GObject library
|
|
||||||
was not only designed to offer OO-like features to C programmers but also transparent
|
|
||||||
cross-language interoperability.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</sect1>
|
|
||||||
|
|
||||||
</chapter>
|
|
Loading…
Reference in New Issue
Block a user