mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-06-11 15:13:46 +02:00
Format XML to be more editable. Describe Interfaces better. Add a footnote
* gobject/tut_gobject.xml: * gobject/tut_gsignal.xml: * gobject/tut_gtype.xml: * gobject/tut_howto.xml: * gobject/tut_intro.xml: * gobject/tut_tools.xml: Format XML to be more editable. Describe Interfaces better. Add a footnote at first occurance of 'maman_'. svn path=/trunk/; revision=5334
This commit is contained in:
parent
bdca945da3
commit
8ec7d6ca3f
@ -1,3 +1,14 @@
|
||||
2007-02-11 Stefan Kost <ensonic@users.sf.net>
|
||||
|
||||
* gobject/tut_gobject.xml:
|
||||
* gobject/tut_gsignal.xml:
|
||||
* gobject/tut_gtype.xml:
|
||||
* gobject/tut_howto.xml:
|
||||
* gobject/tut_intro.xml:
|
||||
* gobject/tut_tools.xml:
|
||||
Format XML to be more editable. Describe Interfaces better. Add a
|
||||
footnote at first occurance of 'maman_'.
|
||||
|
||||
2007-02-08 Stefan Kost <ensonic@users.sf.net>
|
||||
|
||||
* gobject/tut_gobject.xml:
|
||||
|
@ -1,6 +1,5 @@
|
||||
<?xml version='1.0' encoding="ISO-8859-1"?>
|
||||
|
||||
<chapter id="chapter-gobject">
|
||||
<chapter id="chapter-gobject">
|
||||
<title>The GObject base class</title>
|
||||
|
||||
<para>
|
||||
@ -541,7 +540,6 @@ maman_bar_instance_init (GTypeInstance *instance,
|
||||
MamanBar *self = (MamanBar *)instance;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
maman_bar_set_property (GObject *object,
|
||||
guint property_id,
|
||||
@ -721,7 +719,6 @@ g_object_set_property (G_OBJECT (bar), "papa-number", &val);
|
||||
<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> (vararg version) functions can be used to set
|
||||
@ -765,10 +762,9 @@ g_object_set (G_OBJECT (foo),
|
||||
|
||||
<!-- @todo tell here about how to pass use handle properties in derived classe -->
|
||||
|
||||
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
</chapter>
|
||||
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding="ISO-8859-1"?>
|
||||
<chapter id="chapter-signal">
|
||||
<chapter id="chapter-signal">
|
||||
<title>The GObject messaging system</title>
|
||||
|
||||
<sect1 id="closure">
|
||||
@ -48,8 +48,8 @@ return_type function_callback (... , gpointer user_data);
|
||||
<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 invocator.
|
||||
</para></listitem>
|
||||
callback invocator.</para>
|
||||
</listitem>
|
||||
<listitem><para>
|
||||
Notification: the closure notifies listeners of certain events such as
|
||||
closure invocation, closure invalidation and closure finalization. Listeners
|
||||
@ -64,8 +64,8 @@ return_type function_callback (... , gpointer user_data);
|
||||
<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>
|
||||
</para></footnote></para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
@ -77,7 +77,7 @@ return_type function_callback (... , gpointer user_data);
|
||||
to connect a callback to a given event, you will either use the simple <type><link linkend="GCClosure">GCClosure</link></type>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 :).
|
||||
<programlisting>
|
||||
<programlisting>
|
||||
GClosure* g_cclosure_new (GCallback callback_func,
|
||||
gpointer user_data,
|
||||
GClosureNotify destroy_data);
|
||||
@ -86,7 +86,7 @@ GClosure* g_cclosure_new_swap (GCallback callback_func,
|
||||
GClosureNotify destroy_data);
|
||||
GClosure* g_signal_type_cclosure_new (GType itype,
|
||||
guint struct_offset);
|
||||
</programlisting>
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@ -125,7 +125,7 @@ GClosure* g_signal_type_cclosure_new (GType itype,
|
||||
<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>
|
||||
<programlisting>
|
||||
g_cclosure_marshal_VOID__INT (GClosure *closure,
|
||||
GValue *return_value,
|
||||
guint n_param_values,
|
||||
@ -151,7 +151,7 @@ g_cclosure_marshal_VOID__INT (GClosure *closure,
|
||||
g_marshal_value_peek_int (param_values + 1),
|
||||
data2);
|
||||
}
|
||||
</programlisting>
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@ -299,9 +299,6 @@ guint g_signal_newv (const gchar *signal_name,
|
||||
and removed with <function><link linkend="g-signal-remove-emission-hook">g_signal_remove_emission_hook</link></function>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="signal-emission">
|
||||
@ -501,7 +498,8 @@ void g_signal_emit_by_name (gpointer instance,
|
||||
(even though they are connected to a signal which is being emitted).
|
||||
</para>
|
||||
|
||||
<para>This completely optional filtering mechanism is mainly used as an optimization for signals
|
||||
<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,
|
||||
@ -510,11 +508,13 @@ void g_signal_emit_by_name (gpointer instance,
|
||||
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
|
||||
<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>
|
||||
</chapter>
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding="ISO-8859-1"?>
|
||||
<chapter>
|
||||
<chapter id="chapter-gtype">
|
||||
<title>The Glib Dynamic Type System</title>
|
||||
|
||||
<para>
|
||||
@ -236,6 +236,13 @@ struct _GTypeValueTable
|
||||
</para></listitem>
|
||||
<listitem><para>Use prefixing to avoid namespace conflicts with other projects.
|
||||
If your library (or application) is named <emphasis>Maman</emphasis>,
|
||||
<footnote>
|
||||
<para>
|
||||
<emphasis>Maman</emphasis> is the french word for <emphasis>mum</emphasis>
|
||||
or <emphasis>mother</emphasis> - nothing more and nothing less.
|
||||
</para>
|
||||
</footnote>
|
||||
|
||||
prefix all your function names with <emphasis>maman_</emphasis>.
|
||||
For example: <function>maman_object_method</function>.
|
||||
</para></listitem>
|
||||
@ -496,6 +503,9 @@ B *b;
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<sect2 id="gtype-instantiable-classed-init-done">
|
||||
<title>Initialization and Destruction</title>
|
||||
|
||||
<para>
|
||||
Instanciation of these types can be done with
|
||||
<function><link linkend="g-type-create-instance">g_type_create_instance</link></function>:
|
||||
@ -522,12 +532,12 @@ void g_type_free_instance (GTypeInstance *instance);
|
||||
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>
|
||||
<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>
|
||||
@ -543,6 +553,7 @@ The class initialization process is entirely implemented in
|
||||
last living instance of the object, the class is destroyed.
|
||||
</para>
|
||||
|
||||
|
||||
<para>
|
||||
Class destruction
|
||||
<footnote>
|
||||
@ -642,6 +653,8 @@ The class initialization process is entirely implemented in
|
||||
</table>
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="gtype-non-instantiable-classed">
|
||||
@ -650,6 +663,9 @@ The class initialization process is entirely implemented in
|
||||
<para>
|
||||
GType's Interfaces are very similar to Java's interfaces. They allow
|
||||
to describe a common API that several classes will adhere to.
|
||||
Imagine the play, pause and stop buttons on hifi equipment - those can
|
||||
be seen as a playback interface. Once you know what the do, you can
|
||||
control your cd-player, mp3-player or anything that uses these symbols.
|
||||
To declare an interfacce you have to register a non-instantiable
|
||||
classed type which derives from
|
||||
<type><link linkend="GTypeInterface">GTypeInterface</link></type>. The following piece of code declares such an interface.
|
||||
|
@ -1,3 +1,4 @@
|
||||
<?xml version='1.0' encoding="ISO-8859-1"?>
|
||||
<partintro>
|
||||
<para>
|
||||
This chapter tries to answer the real-life questions of users and presents
|
||||
@ -6,11 +7,7 @@
|
||||
</para>
|
||||
</partintro>
|
||||
|
||||
<!--
|
||||
Howto GObject
|
||||
-->
|
||||
|
||||
<chapter id="howto-gobject">
|
||||
<chapter id="howto-gobject">
|
||||
<title>How To define and implement a new GObject?</title>
|
||||
|
||||
<para>
|
||||
@ -701,11 +698,13 @@ maman_bar_subtype_class_init (MamanBarSubTypeClass *klass)
|
||||
programming idiom is often used, this section attemps to explain how to implement it.
|
||||
</para>
|
||||
|
||||
<para>To explicitely chain up to the implementation of the virtual method in the parent class,
|
||||
<para>
|
||||
To explicitely chain up to the implementation of the virtual method in the parent class,
|
||||
you first need a handle to the original parent class structure. This pointer can then be used to
|
||||
access the original class function pointer and invoke it directly.
|
||||
<footnote>
|
||||
<para>The <emphasis>original</emphasis> adjective used in this sentence is not innocuous. To fully
|
||||
<para>
|
||||
The <emphasis>original</emphasis> adjective used in this sentence is not innocuous. To fully
|
||||
understand its meaning, you need to recall how class structures are initialized: for each object type,
|
||||
the class structure associated to this object is created by first copying the class structure of its
|
||||
parent type (a simple <function>memcpy</function>) and then by invoking the class_init callback on
|
||||
@ -742,18 +741,11 @@ b_method_to_call (B *obj, int a)
|
||||
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
|
||||
<!--
|
||||
End Howto GObject
|
||||
-->
|
||||
</chapter>
|
||||
<!-- End Howto GObject -->
|
||||
|
||||
|
||||
<!--
|
||||
Howto Interfaces
|
||||
-->
|
||||
|
||||
<chapter id="howto-interface">
|
||||
<chapter id="howto-interface">
|
||||
<title>How To define and implement Interfaces?</title>
|
||||
|
||||
<sect1 id="howto-interface-define">
|
||||
@ -982,18 +974,20 @@ baz_instance_init (GTypeInstance *instance,
|
||||
but it could :)
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<sect1>
|
||||
<title>Interface definition prerequisites</title>
|
||||
|
||||
<para>To specify that an interface requires the presence of other interfaces when implemented,
|
||||
<para>
|
||||
To specify that an interface requires the presence of other interfaces when implemented,
|
||||
GObject introduces the concept of <emphasis>prerequisites</emphasis>: it is possible to associate
|
||||
a list of prerequisite interfaces to an interface. For example, if object A wishes to implement interface
|
||||
I1, and if interface I1 has a prerequisite on interface I2, A has to implement both I1 and I2.
|
||||
</para>
|
||||
|
||||
<para>The mechanism described above is, in practice, very similar to Java's interface I1 extends
|
||||
<para>
|
||||
The mechanism described above is, in practice, very similar to Java's interface I1 extends
|
||||
interface I2. The example below shows the GObject equivalent:
|
||||
|
||||
<programlisting>
|
||||
@ -1031,7 +1025,6 @@ ibaz_interface_init (gpointer g_iface,
|
||||
iface->do_action = (void (*) (MamanIbaz *self))ibaz_do_action;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
bar_instance_init (GTypeInstance *instance,
|
||||
gpointer g_class)
|
||||
@ -1040,7 +1033,6 @@ bar_instance_init (GTypeInstance *instance,
|
||||
self->instance_member = 0x666;
|
||||
}
|
||||
|
||||
|
||||
GType
|
||||
maman_bar_get_type (void)
|
||||
{
|
||||
@ -1083,7 +1075,7 @@ maman_bar_get_type (void)
|
||||
It is very important to notice that the order in which interface implementations are added to the main object
|
||||
is not random: <function><link linkend="g-type-add-interface-static">g_type_add_interface_static</link></function> must be invoked first on the interfaces which have
|
||||
no prerequisites and then on the others.
|
||||
</para>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Complete source code showing how to define the MamanIbar interface which requires MamanIbaz and how to
|
||||
@ -1091,19 +1083,21 @@ maman_bar_get_type (void)
|
||||
and <filename>sample/interface/maman-bar.{h|c}</filename>.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="howto-interface-properties">
|
||||
<sect1 id="howto-interface-properties">
|
||||
<title>Interface Properties</title>
|
||||
|
||||
<para>Starting from version 2.4 of glib, GObject interfaces can also have properties.
|
||||
<para>
|
||||
Starting from version 2.4 of glib, GObject interfaces can also have properties.
|
||||
Declaration of the interface properties is similar to declaring the properties of
|
||||
ordinary GObject types as explained in <xref linkend="gobject-properties"/>,
|
||||
except that <function><link linkend="g-object-interface-install-property">g_object_interface_install_property</link></function> is used to
|
||||
declare the properties instead of <function><link linkend="g-object-class-install-property">g_object_class_install_property</link></function>.
|
||||
</para>
|
||||
|
||||
<para>To include a property named 'name' of type <type>string</type> in the
|
||||
<para>
|
||||
To include a property named 'name' of type <type>string</type> in the
|
||||
<type>maman_ibaz</type> interface example code above, we only need to add one
|
||||
<footnote>
|
||||
<para>
|
||||
@ -1139,13 +1133,15 @@ maman_ibaz_base_init (gpointer g_iface)
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>One point worth noting is that the declared property wasn't assigned an
|
||||
<para>
|
||||
One point worth noting is that the declared property wasn't assigned an
|
||||
integer ID. The reason being that integer IDs of properities are utilized only
|
||||
inside the get and set methods and since interfaces do not implement properties,
|
||||
there is no need to assign integer IDs to interface properties.
|
||||
</para>
|
||||
|
||||
<para>The story for the implementers of the interface is also quite trivial.
|
||||
<para>
|
||||
The story for the implementers of the interface is also quite trivial.
|
||||
An implementer shall declare and define it's properties in the usual way as
|
||||
explained in <xref linkend="gobject-properties"/>, except for one small
|
||||
change: it shall declare the properties of the interface it implements using
|
||||
@ -1259,22 +1255,11 @@ maman_baz_get_property (GObject * object, guint prop_id,
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
|
||||
</sect1>
|
||||
</chapter>
|
||||
<!-- End Howto Interfaces -->
|
||||
|
||||
<!--
|
||||
End Howto Interfaces
|
||||
-->
|
||||
|
||||
|
||||
<!--
|
||||
start Howto Signals
|
||||
-->
|
||||
|
||||
|
||||
<chapter id="howto-signals">
|
||||
<chapter id="howto-signals">
|
||||
<title>Howto create and use signals</title>
|
||||
|
||||
|
||||
@ -1291,14 +1276,15 @@ maman_baz_get_property (GObject * object, guint prop_id,
|
||||
just emit events which can be received by numerous clients.
|
||||
</para>
|
||||
|
||||
<sect1 id="howto-simple-signals">
|
||||
<title>Simple use of signals</title>
|
||||
<sect1 id="howto-simple-signals">
|
||||
<title>Simple use of signals</title>
|
||||
|
||||
<para>The most basic use of signals is to implement simple event notification: for example, if we have a
|
||||
MamanFile object, and if this object has a write method, we might wish to be notified whenever someone
|
||||
uses this method. The code below shows how the user can connect a callback to the write signal. Full code
|
||||
for this simple example is located in <filename>sample/signal/maman-file.{h|c}</filename> and
|
||||
in <filename>sample/signal/test.c</filename>
|
||||
<para>
|
||||
The most basic use of signals is to implement simple event notification: for example, if we have a
|
||||
MamanFile object, and if this object has a write method, we might wish to be notified whenever someone
|
||||
uses this method. The code below shows how the user can connect a callback to the write signal. Full code
|
||||
for this simple example is located in <filename>sample/signal/maman-file.{h|c}</filename> and
|
||||
in <filename>sample/signal/test.c</filename>
|
||||
<programlisting>
|
||||
file = g_object_new (MAMAN_FILE_TYPE, NULL);
|
||||
|
||||
@ -1308,10 +1294,10 @@ g_signal_connect (G_OBJECT (file), "write",
|
||||
|
||||
maman_file_write (file, buffer, 50);
|
||||
</programlisting>
|
||||
</para>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The <type>MamanFile</type> signal is registered in the class_init function:
|
||||
<para>
|
||||
The <type>MamanFile</type> signal is registered in the class_init function:
|
||||
<programlisting>
|
||||
klass->write_signal_id =
|
||||
g_signal_newv ("write",
|
||||
@ -1325,7 +1311,7 @@ klass->write_signal_id =
|
||||
0 /* n_params */,
|
||||
NULL /* param_types */);
|
||||
</programlisting>
|
||||
and the signal is emited in <function>maman_file_write</function>:
|
||||
and the signal is emited in <function>maman_file_write</function>:
|
||||
<programlisting>
|
||||
void maman_file_write (MamanFile *self, guint8 *buffer, guint32 size)
|
||||
{
|
||||
@ -1336,53 +1322,55 @@ void maman_file_write (MamanFile *self, guint8 *buffer, guint32 size)
|
||||
NULL);
|
||||
}
|
||||
</programlisting>
|
||||
As shown above, you can safely set the details parameter to zero if you do not know what it can be used for.
|
||||
For a discussion of what you could used it for, see <xref linkend="signal-detail"/>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The signature of the signal handler in the above example is defined as
|
||||
<function>g_cclosure_marshal_VOID__VOID</function>. Its name follows
|
||||
a simple convention which encodes the function parameter and return value
|
||||
types in the function name. Specifically, the value infront of the double
|
||||
underscore is the type of the return value, while the value(s) after the
|
||||
double underscore denote the parameter types.
|
||||
The header <filename>gobject/gmarshal.h</filename> defines a set of commonly
|
||||
needed closures that one can use.
|
||||
As shown above, you can safely set the details parameter to zero if you do not know what it can be used for.
|
||||
For a discussion of what you could used it for, see <xref linkend="signal-detail"/>
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
<para>
|
||||
The signature of the signal handler in the above example is defined as
|
||||
<function>g_cclosure_marshal_VOID__VOID</function>. Its name follows
|
||||
a simple convention which encodes the function parameter and return value
|
||||
types in the function name. Specifically, the value infront of the double
|
||||
underscore is the type of the return value, while the value(s) after the
|
||||
double underscore denote the parameter types.
|
||||
The header <filename>gobject/gmarshal.h</filename> defines a set of commonly
|
||||
needed closures that one can use.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>How to provide more flexibility to users?</title>
|
||||
|
||||
<sect1>
|
||||
<title>How to provide more flexibility to users?</title>
|
||||
<para>
|
||||
The previous implementation does the job but the signal facility of GObject can be used to provide
|
||||
even more flexibility to this file change notification mechanism. One of the key ideas is to make the process
|
||||
of writing data to the file part of the signal emission process to allow users to be notified either
|
||||
before or after the data is written to the file.
|
||||
</para>
|
||||
|
||||
<para>The previous implementation does the job but the signal facility of GObject can be used to provide
|
||||
even more flexibility to this file change notification mechanism. One of the key ideas is to make the process
|
||||
of writing data to the file part of the signal emission process to allow users to be notified either
|
||||
before or after the data is written to the file.
|
||||
</para>
|
||||
<para>
|
||||
To integrate the process of writing the data to the file into the signal emission mechanism, we can
|
||||
register a default class closure for this signal which will be invoked during the signal emission, just like
|
||||
any other user-connected signal handler.
|
||||
</para>
|
||||
|
||||
<para>To integrate the process of writing the data to the file into the signal emission mechanism, we can
|
||||
register a default class closure for this signal which will be invoked during the signal emission, just like
|
||||
any other user-connected signal handler.
|
||||
</para>
|
||||
|
||||
<para>The first step to implement this idea is to change the signature of the signal: we need to pass
|
||||
around the buffer to write and its size. To do this, we use our own marshaller which will be generated
|
||||
through glib's genmarshall tool. We thus create a file named <filename>marshall.list</filename> which contains
|
||||
the following single line:
|
||||
<para>
|
||||
The first step to implement this idea is to change the signature of the signal: we need to pass
|
||||
around the buffer to write and its size. To do this, we use our own marshaller which will be generated
|
||||
through glib's genmarshall tool. We thus create a file named <filename>marshall.list</filename> which contains
|
||||
the following single line:
|
||||
<programlisting>
|
||||
VOID:POINTER,UINT
|
||||
</programlisting>
|
||||
and use the Makefile provided in <filename>sample/signal/Makefile</filename> to generate the file named
|
||||
<filename>maman-file-complex-marshall.c</filename>. This C file is finally included in
|
||||
<filename>maman-file-complex.c</filename>.
|
||||
</para>
|
||||
and use the Makefile provided in <filename>sample/signal/Makefile</filename> to generate the file named
|
||||
<filename>maman-file-complex-marshall.c</filename>. This C file is finally included in
|
||||
<filename>maman-file-complex.c</filename>.
|
||||
</para>
|
||||
|
||||
<para>Once the marshaller is present, we register the signal and its marshaller in the class_init function
|
||||
of the object <type>MamanFileComplex</type> (full source for this object is included in
|
||||
<filename>sample/signal/maman-file-complex.{h|c}</filename>):
|
||||
<para>
|
||||
Once the marshaller is present, we register the signal and its marshaller in the class_init function
|
||||
of the object <type>MamanFileComplex</type> (full source for this object is included in
|
||||
<filename>sample/signal/maman-file-complex.{h|c}</filename>):
|
||||
<programlisting>
|
||||
GClosure *default_closure;
|
||||
GType param_types[2];
|
||||
@ -1405,13 +1393,13 @@ klass->write_signal_id =
|
||||
2 /* n_params */,
|
||||
param_types /* param_types */);
|
||||
</programlisting>
|
||||
The code shown above first creates the closure which contains the code to complete the file write. This
|
||||
closure is registered as the default class_closure of the newly created signal.
|
||||
</para>
|
||||
The code shown above first creates the closure which contains the code to complete the file write. This
|
||||
closure is registered as the default class_closure of the newly created signal.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Of course, you need to implement completely the code for the default closure since I just provided
|
||||
a skeleton:
|
||||
<para>
|
||||
Of course, you need to implement completely the code for the default closure since I just provided
|
||||
a skeleton:
|
||||
<programlisting>
|
||||
static void
|
||||
default_write_signal_handler (GObject *obj, guint8 *buffer, guint size, gpointer user_data)
|
||||
@ -1421,10 +1409,11 @@ default_write_signal_handler (GObject *obj, guint8 *buffer, guint size, gpointer
|
||||
g_print ("default signal handler: 0x%x %u\n", buffer, size);
|
||||
}
|
||||
</programlisting>
|
||||
</para>
|
||||
</para>
|
||||
|
||||
<para>Finally, the client code must invoke the <function>maman_file_complex_write</function> function which
|
||||
triggers the signal emission:
|
||||
<para>
|
||||
Finally, the client code must invoke the <function>maman_file_complex_write</function> function which
|
||||
triggers the signal emission:
|
||||
<programlisting>
|
||||
void maman_file_complex_write (MamanFileComplex *self, guint8 *buffer, guint size)
|
||||
{
|
||||
@ -1435,20 +1424,21 @@ void maman_file_complex_write (MamanFileComplex *self, guint8 *buffer, guint siz
|
||||
buffer, size);
|
||||
}
|
||||
</programlisting>
|
||||
</para>
|
||||
</para>
|
||||
|
||||
<para>The client code (as shown in <filename>sample/signal/test.c</filename> and below) can now connect signal handlers before
|
||||
and after the file write is completed: since the default signal handler which does the write itself runs during the
|
||||
RUN_LAST phase of the signal emission, it will run after all handlers connected with <function><link linkend="g-signal-connect">g_signal_connect</link></function>
|
||||
and before all handlers connected with <function><link linkend="g-signal-connect-after">g_signal_connect_after</link></function>. If you intent to write a GObject
|
||||
which emits signals, I would thus urge you to create all your signals with the G_SIGNAL_RUN_LAST such that your users
|
||||
have a maximum of flexibility as to when to get the event. Here, we combined it with G_SIGNAL_NO_RECURSE and
|
||||
G_SIGNAL_NO_HOOKS to ensure our users will not try to do really weird things with our GObject. I strongly advise you
|
||||
to do the same unless you really know why (in which case you really know the inner workings of GSignal by heart and
|
||||
you are not reading this).
|
||||
</para>
|
||||
<para>
|
||||
The client code (as shown in <filename>sample/signal/test.c</filename> and below) can now connect signal handlers before
|
||||
and after the file write is completed: since the default signal handler which does the write itself runs during the
|
||||
RUN_LAST phase of the signal emission, it will run after all handlers connected with <function><link linkend="g-signal-connect">g_signal_connect</link></function>
|
||||
and before all handlers connected with <function><link linkend="g-signal-connect-after">g_signal_connect_after</link></function>. If you intent to write a GObject
|
||||
which emits signals, I would thus urge you to create all your signals with the G_SIGNAL_RUN_LAST such that your users
|
||||
have a maximum of flexibility as to when to get the event. Here, we combined it with G_SIGNAL_NO_RECURSE and
|
||||
G_SIGNAL_NO_HOOKS to ensure our users will not try to do really weird things with our GObject. I strongly advise you
|
||||
to do the same unless you really know why (in which case you really know the inner workings of GSignal by heart and
|
||||
you are not reading this).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<para>
|
||||
<programlisting>
|
||||
static void complex_write_event_before (GObject *file, guint8 *buffer, guint size, gpointer user_data)
|
||||
{
|
||||
@ -1482,13 +1472,13 @@ static void test_file_complex (void)
|
||||
g_object_unref (G_OBJECT (file));
|
||||
}
|
||||
</programlisting>
|
||||
The code above generates the following output on my machine:
|
||||
The code above generates the following output on my machine:
|
||||
<programlisting>
|
||||
Complex Write event before: 0xbfffe280, 50
|
||||
default signal handler: 0xbfffe280 50
|
||||
Complex Write event after: 0xbfffe280, 50
|
||||
</programlisting>
|
||||
</para>
|
||||
</para>
|
||||
|
||||
|
||||
<sect2>
|
||||
@ -1592,12 +1582,10 @@ klass->write_signal_id =
|
||||
|
||||
</sect2>
|
||||
|
||||
|
||||
</sect1>
|
||||
</sect1>
|
||||
|
||||
|
||||
|
||||
<sect1>
|
||||
<sect1>
|
||||
<title>How users can abuse signals (and why some think it is good)</title>
|
||||
|
||||
<para>Now that you know how to create signals to which the users can connect easily and at any point in
|
||||
@ -1607,7 +1595,8 @@ klass->write_signal_id =
|
||||
This will make you feel good and eleet.
|
||||
</para>
|
||||
|
||||
<para>The users can:
|
||||
<para>
|
||||
The users can:
|
||||
<itemizedlist>
|
||||
<listitem><para>stop the emission of the signal at anytime</para></listitem>
|
||||
<listitem><para>override the default handler of the signal if it is stored as a function
|
||||
@ -1616,30 +1605,34 @@ klass->write_signal_id =
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<para>In both cases, the original programmer should be as careful as possible to write code which is
|
||||
<para>
|
||||
In both cases, the original programmer should be as careful as possible to write code which is
|
||||
resistant to the fact that the default handler of the signal might not able to run. This is obviously
|
||||
not the case in the example used in the previous sections since the write to the file depends on whether
|
||||
or not the default handler runs (however, this might be your goal: to allow the user to prevent the file
|
||||
write if he wishes to).
|
||||
</para>
|
||||
|
||||
<para>If all you want to do is to stop the signal emission from one of the callbacks you connected yourself,
|
||||
<para>
|
||||
If all you want to do is to stop the signal emission from one of the callbacks you connected yourself,
|
||||
you can call <function><link linkend="g-signal-stop-by-name">g_signal_stop_by_name</link></function>. Its use is very simple which is why I won't detail
|
||||
it further.
|
||||
</para>
|
||||
|
||||
<para>If the signal's default handler is just a class function pointer, it is also possible to override
|
||||
<para>
|
||||
If the signal's default handler is just a class function pointer, it is also possible to override
|
||||
it yourself from the class_init function of a type which derives from the parent. That way, when the signal
|
||||
is emitted, the parent class will use the function provided by the child as a signal default handler.
|
||||
Of course, it is also possible (and recommended) to chain up from the child to the parent's default signal
|
||||
handler to ensure the integrity of the parent object.
|
||||
</para>
|
||||
|
||||
<para>Overriding a class method and chaining up was demonstrated in <xref linkend="howto-gobject-methods"/>
|
||||
which is why I won't bother to show exactly how to do it here again.</para>
|
||||
<para>
|
||||
Overriding a class method and chaining up was demonstrated in <xref linkend="howto-gobject-methods"/>
|
||||
which is why I won't bother to show exactly how to do it here again.
|
||||
</para>
|
||||
|
||||
|
||||
</sect1>
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
|
||||
@ -1723,10 +1716,3 @@ g_object_do_class_init (GObjectClass *class)
|
||||
</sect2>
|
||||
-->
|
||||
|
||||
|
||||
<!--
|
||||
<capter1 id="howto-doc">
|
||||
<title>How to generate API documentation for your type?</title>
|
||||
|
||||
</chapter>
|
||||
-->
|
||||
|
@ -1,85 +1,90 @@
|
||||
<chapter>
|
||||
<title>Background</title>
|
||||
<?xml version='1.0' encoding="ISO-8859-1"?>
|
||||
<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>
|
||||
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. describes briefly
|
||||
the solution choosen by GLib.
|
||||
</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. describes briefly
|
||||
the solution choosen 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>
|
||||
<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>
|
||||
<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>
|
||||
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>
|
||||
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:
|
||||
<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>
|
||||
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>
|
||||
|
||||
<sect1>
|
||||
<title>Exporting a C API</title>
|
||||
<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>
|
||||
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>
|
||||
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:
|
||||
<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)
|
||||
{}
|
||||
@ -95,40 +100,42 @@ int main (int argc, char *argv[])
|
||||
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>
|
||||
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>
|
||||
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>
|
||||
<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
|
||||
@ -136,16 +143,14 @@ all the objects manipulated by the programmer. This so-called <emphasis>dynamic
|
||||
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>
|
||||
</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"/>
|
||||
@ -154,20 +159,21 @@ boundaries is written once: the figure below states this more clearly.
|
||||
<imagedata fileref="glue.jpg" format="JPG" align="center"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
</figure>
|
||||
</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>
|
||||
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>
|
||||
<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>
|
||||
</sect1>
|
||||
|
||||
</chapter>
|
||||
|
@ -1,3 +1,4 @@
|
||||
<?xml version='1.0' encoding="ISO-8859-1"?>
|
||||
<partintro>
|
||||
<para>
|
||||
Several useful developer tools have been build around GObject technology.
|
||||
|
Loading…
x
Reference in New Issue
Block a user