-<chapter id="howto">
- <title>How To ?</title>
+<?xml version='1.0' encoding="ISO-8859-1"?>
+<!DOCTYPE part PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+]>
+<part label="IV">
+ <title>Tutorial</title>
+ <partintro>
+ <para>
+ This chapter tries to answer the real-life questions of users and presents
+ the most common scenario use cases I could come up with.
+ The use cases are presented from most likely to less likely.
+ </para>
+ </partintro>
+
+<chapter id="howto-gobject">
+ <title>How to define and implement a new GObject</title>
<para>
- This chapter tries to answer the real-life questions of users and presents
- the most common scenario use-cases I could come up with.
- The use-cases are presented from most likely to less likely.
+ Clearly, this is one of the most common questions people ask: they just
+ want to crank code and implement a subclass of a GObject. Sometimes because
+ they want to create their own class hierarchy, sometimes because they want
+ to subclass one of GTK+'s widget. This chapter will focus on the
+ implementation of a subtype of GObject.
</para>
-<!--
- Howto GObject
--->
-
- <sect1 id="howto-gobject">
- <title>How To define and implement a new GObject ?</title>
+ <sect1 id="howto-gobject-header">
+ <title>Boilerplate header code</title>
<para>
- Clearly, this is one of the most common question people ask: they just want to crank code and
- implement a subclass of a GObject. Sometimes because they want to create their own class hierarchy,
- sometimes because they want to subclass one of GTK+'s widget. This chapter will focus on the
- implementation of a subtype of GObject. The sample source code
- associated to this section can be found in the documentation's source tarball, in the
- <filename>sample/gobject</filename> directory:
+ The first step before writing the code for your GObject is to write the
+ type's header which contains the needed type, function and macro
+ definitions. Each of these elements is nothing but a convention which
+ is followed not only by GTK+'s code but also by most users of GObject.
+ If you feel the need not to obey the rules stated below, think about it
+ twice:
<itemizedlist>
- <listitem><para><filename>maman-bar.{h|c}</filename>: this is the source for a object which derives from
- <type>GObject</type> and which shows how to declare different types of methods on the object.
- </para></listitem>
- <listitem><para><filename>maman-subbar.{h|c}</filename>: this is the source for a object which derives from
- <type>MamanBar</type> and which shows how to override some of its parent's methods.
- </para></listitem>
- <listitem><para><filename>maman-foo.{h|c}</filename>: this is the source for an object which derives from
- <type>GObject</type> and which declares a signal.
- </para></listitem>
- <listitem><para><filename>test.c</filename>: this is the main source which instantiates an instance of
- type and exercises their API.
- </para></listitem>
+ <listitem><para>If your users are a bit accustomed to GTK+ code or any
+ GLib code, they will be a bit surprised and getting used to the
+ conventions you decided upon will take time (money) and will make them
+ grumpy (not a good thing)</para></listitem>
+ <listitem><para>You must assess the fact that these conventions might
+ have been designed by both smart and experienced people: maybe they
+ were at least partly right. Try to put your ego aside.</para></listitem>
</itemizedlist>
</para>
- <sect2 id="howto-gobject-header">
- <title>Boilerplate header code</title>
-
- <para>
- The first step before writing the code for your GObject is to write the type's header which contains
- the needed type, function and macro definitions. Each of these elements is nothing but a convention
- which is followed not only by GTK+'s code but also by most users of GObject. If you feel the need
- not to obey the rules stated below, think about it twice:
- <itemizedlist>
- <listitem><para>If your users are a bit accustomed to GTK+ code or any Glib code, they will
- be a bit surprised and getting used to the conventions you decided upon will take time (money) and
- will make them grumpy (not a good thing)
- </para></listitem>
- <listitem><para>
- You must assess the fact that these conventions might have been designed by both smart
- and experienced people: maybe they were at least partly right. Try to put your ego aside.
- </para></listitem>
- </itemizedlist>
- </para>
+ <para>
+ Pick a name convention for your headers and source code and stick to it:
+ <itemizedlist>
+ <listitem><para>use a dash to separate the prefix from the typename:
+ <filename>maman-bar.h</filename> and <filename>maman-bar.c</filename>
+ (this is the convention used by Nautilus and most GNOME libraries).</para></listitem>
+ <listitem><para>use an underscore to separate the prefix from the
+ typename: <filename>maman_bar.h</filename> and
+ <filename>maman_bar.c</filename>.</para></listitem>
+ <listitem><para>Do not separate the prefix from the typename:
+ <filename>mamanbar.h</filename> and <filename>mamanbar.c</filename>.
+ (this is the convention used by GTK+)</para></listitem>
+ </itemizedlist>
+ Some people like the first two solutions better: it makes reading file
+ names easier for those with poor eyesight.
+ </para>
- <para>
- Pick a name convention for your headers and source code and stick to it:
- <itemizedlist>
- <listitem><para>
- use a dash to separate the prefix from the typename: <filename>maman-bar.h</filename> and
- <filename>maman-bar.c</filename> (this is the convention used by Nautilus and most Gnome libraries).
- </para></listitem>
- <listitem><para>
- use an underscore to separate the prefix from the typename: <filename>maman_bar.h</filename> and
- <filename>maman_bar.c</filename>.
- </para></listitem>
- <listitem><para>
- Do not separate the prefix from the typename: <filename>mamanbar.h</filename> and
- <filename>mamanbar.c</filename>. (this is the convention used by GTK+)
- </para></listitem>
- </itemizedlist>
- I personally like the first solution better: it makes reading file names easier for those with poor
- eyesight like me.
- </para>
+ <para>
+ When you need some private (internal) declarations in several
+ (sub)classes, you can define them in a private header file which
+ is often named by appending the <emphasis>private</emphasis> keyword
+ to the public header name. For example, one could use
+ <filename>maman-bar-private.h</filename>,
+ <filename>maman_bar_private.h</filename> or
+ <filename>mamanbarprivate.h</filename>. Typically, such private header
+ files are not installed.
+ </para>
- <para>
- The basic conventions for any header which exposes a GType are described in
- <xref linkend="gtype-conventions"/>. Most GObject-based code also obeys onf of the following
- conventions: pick one and stick to it.
- <itemizedlist>
- <listitem><para>
- If you want to declare a type named bar with prefix maman, name the type instance
- <function>MamanBar</function> and its class <function>MamanBarClass</function>
- (name is case-sensitive). It is customary to declare them with code similar to the
- following:
+ <para>
+ The basic conventions for any header which exposes a GType are described
+ in <xref linkend="gtype-conventions"/>. Most GObject-based code also
+ obeys one of of the following conventions: pick one and stick to it.
+ <itemizedlist>
+ <listitem><para>
+ If you want to declare a type named bar with prefix maman, name the type instance
+ <function>MamanBar</function> and its class <function>MamanBarClass</function>
+ (name is case-sensitive). It is customary to declare them with code similar to the
+ following:
<programlisting>
/*
* Copyright/Licensing information.
*/
-#ifndef MAMAN_BAR_H
-#define MAMAN_BAR_H
+/* inclusion guard */
+#ifndef __MAMAN_BAR_H__
+#define __MAMAN_BAR_H__
+#include <glib-object.h>
/*
* Potentially, include other headers on which this header depends.
*/
-
/*
* Type macros.
*/
+#define MAMAN_TYPE_BAR (maman_bar_get_type ())
+#define MAMAN_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_TYPE_BAR, MamanBar))
+#define MAMAN_IS_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_BAR))
+#define MAMAN_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MAMAN_TYPE_BAR, MamanBarClass))
+#define MAMAN_IS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MAMAN_TYPE_BAR))
+#define MAMAN_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MAMAN_TYPE_BAR, MamanBarClass))
-typedef struct _MamanBar MamanBar;
-typedef struct _MamanBarClass MamanBarClass;
+typedef struct _MamanBar MamanBar;
+typedef struct _MamanBarClass MamanBarClass;
-struct _MamanBar {
- GObject parent;
- /* instance members */
+struct _MamanBar
+{
+ GObject parent_instance;
+
+ /* instance members */
};
-struct _MamanBarClass {
- GObjectClass parent;
- /* class members */
+struct _MamanBarClass
+{
+ GObjectClass parent_class;
+
+ /* class members */
};
-/* used by MAMAN_BAR_TYPE */
+/* used by MAMAN_TYPE_BAR */
GType maman_bar_get_type (void);
/*
* Method definitions.
*/
-#endif
+#endif /* __MAMAN_BAR_H__ */
</programlisting>
- </para></listitem>
- <listitem><para>
- Most GTK+ types declare their private fields in the public header with a /* private */ comment,
- relying on their user's intelligence not to try to play with these fields. Fields not marked private
- are considered public by default. The /* protected */ comment (same semantics as those of C++)
- is also used, mainly in the GType library, in code written by Tim Janik.
+ </para></listitem>
+ <listitem><para>
+ Most GTK+ types declare their private fields in the public header
+ with a /* private */ comment, relying on their user's intelligence
+ not to try to play with these fields. Fields not marked private
+ are considered public by default. The /* protected */ comment
+ (same semantics as those of C++) is also used, mainly in the GType
+ library, in code written by Tim Janik.
<programlisting>
-struct _MamanBar {
- GObject parent;
-
- /* private */
- int hsize;
+struct _MamanBar
+{
+ GObject parent_instance;
+
+ /*< private >*/
+ int hsize;
};
</programlisting>
- </para></listitem>
- <listitem><para>
- All of Nautilus code and a lot of Gnome libraries use private indirection members, as described
- by Herb Sutter in his Pimpl articles (see <ulink></ulink>: Herb summarizes the different
- issues better than I will):
+ </para></listitem>
+ <listitem><para>
+ All of Nautilus code and a lot of GNOME libraries use private
+ indirection members, as described by Herb Sutter in his Pimpl
+ articles(see <ulink url="http://www.gotw.ca/gotw/024.htm">Compilation Firewalls</ulink>
+ and <ulink url="http://www.gotw.ca/gotw/028.htm">The Fast Pimpl Idiom</ulink>:
+ he summarizes the different issues better than I will).
<programlisting>
typedef struct _MamanBarPrivate MamanBarPrivate;
-struct _MamanBar {
- GObject parent;
-
- /* private */
- MamanBarPrivate *priv;
+
+struct _MamanBar
+{
+ GObject parent_instance;
+
+ /*< private >*/
+ MamanBarPrivate *priv;
};
</programlisting>
- The private structure is then defined in the .c file, instantiated in the object's XXX
- function and destroyed in the object's XXX function.
- </para></listitem>
- </itemizedlist>
- </para>
+ <note><simpara>Do not call this <varname>private</varname>, as
+ that is a registered c++ keyword.</simpara></note>
+
+ The private structure is then defined in the .c file, using the
+ g_type_class_add_private() function to notify the presence of
+ a private memory area for each instance and it can either
+ be retrieved using <function>G_TYPE_INSTANCE_GET_PRIVATE()</function>
+ each time is needed, or assigned to the <literal>priv</literal>
+ member of the instance structure inside the object's
+ <function>init</function> function.
+<programlisting>
+#define MAMAN_BAR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MAMAN_TYPE_BAR, MamanBarPrivate))
- <para>
- Finally, there are different header include conventions. Again, pick one and stick to it. I personally
- use indifferently any of the two, depending on the codebase I work on: the rule is consistency.
- <itemizedlist>
- <listitem><para>
- Some people add at the top of their headers a number of #include directives to pull in
- all the headers needed to compile client code. This allows client code to simply
- #include "maman-bar.h".
- </para></listitem>
- <listitem><para>
- Other do not #include anything and expect the client to #include themselves the headers
- they need before including your header. This speeds up compilation because it minimizes the
- amount of pre-processor work. This can be used in conjunction with the re-declaration of certain
- unused types in the client code to minimize compile-time dependencies and thus speed up
- compilation.
- </para></listitem>
- </itemizedlist>
- </para>
-
- </sect2>
+struct _MamanBarPrivate
+{
+ int hsize;
+};
- <sect2 id="howto-gobject-code">
- <title>Boilerplate code</title>
+static void
+maman_bar_class_init (MamanBarClass *klass)
+{
+ g_type_class_add_private (klass, sizeof (MamanBarPrivate));
+}
- <para>
- In your code, the first step is to #include the needed headers: depending on your header include strategy, this
- can be as simple as #include "maman-bar.h" or as complicated as tens of #include lines ending with
- #include "maman-bar.h":
+static void
+maman_bar_init (MamanBar *self)
+{
+ MamanBarPrivate *priv;
+
+ self->priv = priv = MAMAN_BAR_GET_PRIVATE (self);
+
+ priv->hsize = 42;
+}
+</programlisting>
+ </para></listitem>
+
+ <listitem><para>
+ You don't need to free or allocate the private structure, only the
+ objects or pointers that it may contain. Another advantage of this
+ to the previous version is that is lessens memory fragmentation,
+ as the public and private parts of the instance memory are
+ allocated at once.
+ </para></listitem>
+ </itemizedlist>
+ </para>
+
+ <para>
+ Finally, there are different header include conventions. Again, pick one
+ and stick to it. I personally use indifferently any of the two, depending
+ on the codebase I work on: the rule, as always, is consistency.
+ <itemizedlist>
+ <listitem><para>
+ Some people add at the top of their headers a number of #include
+ directives to pull in all the headers needed to compile client
+ code. This allows client code to simply #include "maman-bar.h".
+ </para></listitem>
+ <listitem><para>
+ Other do not #include anything and expect the client to #include
+ themselves the headers they need before including your header. This
+ speeds up compilation because it minimizes the amount of
+ pre-processor work. This can be used in conjunction with the
+ re-declaration of certain unused types in the client code to
+ minimize compile-time dependencies and thus speed up compilation.
+ </para></listitem>
+ </itemizedlist>
+ </para>
+
+ </sect1>
+
+ <sect1 id="howto-gobject-code">
+ <title>Boilerplate code</title>
+
+ <para>
+ In your code, the first step is to #include the needed headers: depending
+ on your header include strategy, this can be as simple as
+ <literal>#include "maman-bar.h"</literal> or as complicated as tens
+ of #include lines ending with <literal>#include "maman-bar.h"</literal>:
<programlisting>
/*
* Copyright information
* definition for this private structure.
*/
struct _MamanBarPrivate {
- int member_1;
- /* stuff */
+ int member_1;
+ /* stuff */
};
/*
* forward definitions
*/
</programlisting>
- </para>
+ </para>
+
+ <para>
+ Call the <function>G_DEFINE_TYPE</function> macro using the name
+ of the type, the prefix of the functions and the parent GType to
+ reduce the amount of boilerplate needed. This macro will:
+
+ <itemizedlist>
+ <listitem><simpara>implement the <function>maman_bar_get_type</function>
+ function</simpara></listitem>
+ <listitem><simpara>define a parent class pointer accessible from
+ the whole .c file</simpara></listitem>
+ </itemizedlist>
- <para>
- Implement <function>maman_bar_get_type</function> and make sure the code compiles:
<programlisting>
-GType
-maman_bar_get_type (void)
-{
- static GType type = 0;
- if (type == 0) {
- static const GTypeInfo info = {
- sizeof (MamanBarClass),
- NULL, /* base_init */
- NULL, /* base_finalize */
- NULL, /* class_init */
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (MamanBar),
- 0, /* n_preallocs */
- NULL /* instance_init */
- };
- type = g_type_register_static (G_TYPE_OBJECT,
- "MamanBarType",
- &info, 0);
- }
- return type;
-}
+G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT);
</programlisting>
- </para>
- </sect2>
+ </para>
- <sect2 id="howto-gobject-construction">
- <title>Object Construction</title>
+ <para>
+ It is also possible to use the
+ <function>G_DEFINE_TYPE_WITH_CODE</function> macro to control the
+ get_type function implementation - for instance, to add a call to
+ <function>G_IMPLEMENT_INTERFACE</function> macro which will
+ call the <function>g_type_implement_interface</function> function.
+ </para>
+ </sect1>
- <para>
- People often get confused when trying to construct their GObjects because of the
- sheer number of different ways to hook into the objects's construction process: it is
- difficult to figure which is the <emphasis>correct</emphasis>, recommended way.
- </para>
+ <sect1 id="howto-gobject-construction">
+ <title>Object Construction</title>
- <para>
- <xref linkend="gobject-construction-table"/> shows what user-provided functions
- are invoked during object instanciation and in which order they are invoked.
- A user looking for the equivalent of the simple C++ constructor function should use
- the instance_init method. It will be invoked after all the parent's instance_init
- functions have been invoked. It cannot take arbitrary construction parameters
- (as in C++) but if your object needs arbitrary parameters to complete initialization,
- you can use construction properties.
- </para>
+ <para>
+ People often get confused when trying to construct their GObjects because of the
+ sheer number of different ways to hook into the objects's construction process: it is
+ difficult to figure which is the <emphasis>correct</emphasis>, recommended way.
+ </para>
- <para>
- Construction properties will be set only after all instance_init functions have run.
- No object reference will be returned to the client of <function>g_object_new></function>
- until all the construction properties have been set.
- </para>
+ <para>
+ <xref linkend="gobject-construction-table"/> shows what user-provided functions
+ are invoked during object instantiation and in which order they are invoked.
+ A user looking for the equivalent of the simple C++ constructor function should use
+ the instance_init method. It will be invoked after all the parent's instance_init
+ functions have been invoked. It cannot take arbitrary construction parameters
+ (as in C++) but if your object needs arbitrary parameters to complete initialization,
+ you can use construction properties.
+ </para>
- <para>
- As such, I would recommend writing the following code first:
+ <para>
+ Construction properties will be set only after all instance_init functions have run.
+ No object reference will be returned to the client of <function><link linkend="g-object-new">g_object_new</link></function>
+ until all the construction properties have been set.
+ </para>
+
+ <para>
+ As such, I would recommend writing the following code first:
<programlisting>
static void
-maman_bar_init (GTypeInstance *instance,
- gpointer g_class)
+maman_bar_init (MamanBar *self)
{
- MamanBar *self = (MamanBar *)instance;
- self->private = g_new0 (MamanBarPrivate, 1);
+ self->priv = MAMAN_BAR_GET_PRIVATE (self);
- /* initialize all public and private members to reasonable default values. */
- /* If you need specific consruction properties to complete initialization,
- * delay initialization completion until the property is set.
- */
+ /* initialize all public and private members to reasonable default values. */
+
+ /* If you need specific construction properties to complete initialization,
+ * delay initialization completion until the property is set.
+ */
}
</programlisting>
- And make sure that you set <function>maman_bar_init</function> as the type's instance_init function
- in <function>maman_bar_get_type</function>. Make sure the code builds and runs: create an instance
- of the object and make sure <function>maman_bar_init</function> is called (add a
- <function>g_print</function> call in it).
- </para>
+ </para>
- <para>
- Now, if you need special construction properties, install the properties in the class_init function,
- override the set and get methods and implement the get and set methods as described in
- <xref linkend="gobject-properties"/>. Make sure that these properties use a construct only
- pspec by setting the param spec's flag field to G_PARAM_CONSTRUCT_ONLY: this helps
- GType ensure that these properties are not set again later by malicious user code.
+ <para>
+ Now, if you need special construction properties, install the properties in the class_init function,
+ override the set and get methods and implement the get and set methods as described in
+ <xref linkend="gobject-properties"/>. Make sure that these properties use a construct only
+ <link linkend="GParamSpec"><type>GParamSpec</type></link> by setting the param spec's flag field to G_PARAM_CONSTRUCT_ONLY: this helps
+ GType ensure that these properties are not set again later by malicious user code.
<programlisting>
static void
bar_class_init (MamanBarClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- GParamSpec *maman_param_spec;
+ GParamSpec *pspec;
gobject_class->set_property = bar_set_property;
gobject_class->get_property = bar_get_property;
- maman_param_spec = g_param_spec_string ("maman",
+ pspec = g_param_spec_string ("maman",
"Maman construct prop",
"Set maman's name",
"no-name-set" /* default value */,
- G_PARAM_CONSTRUCT_ONLY |G_PARAM_READWRITE);
-
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
g_object_class_install_property (gobject_class,
- PROP_MAMAN,
- maman_param_spec);
+ PROP_MAMAN,
+ pspec);
}
</programlisting>
- If you need this, make sure you can build and run code similar to the code shown above. Make sure
- your construct properties can set correctly during construction, make sure you cannot set them
- afterwards and make sure that if your users do not call <function>g_object_new</function>
- with the required construction properties, these will be initialized with the default values.
- </para>
+ If you need this, make sure you can build and run code similar to the code shown above. Make sure
+ your construct properties can set correctly during construction, make sure you cannot set them
+ afterwards and make sure that if your users do not call <function><link linkend="g-object-new">g_object_new</link></function>
+ with the required construction properties, these will be initialized with the default values.
+ </para>
- <para>
- I consider good taste to halt program execution if a construction property is set its
- default value. This allows you to catch client code which does not give a reasonable
- value to the construction properties. Of course, you are free to disagree but you
- should have a good reason to do so.
- </para>
+ <para>
+ I consider good taste to halt program execution if a construction property is set its
+ default value. This allows you to catch client code which does not give a reasonable
+ value to the construction properties. Of course, you are free to disagree but you
+ should have a good reason to do so.
+ </para>
- <para>Some people sometimes need to construct their object but only after the construction properties
- have been set. This is possible through the use of the constructor class method as described in
- <xref linkend="gobject-instanciation"/>. However, I have yet to see <emphasis>any</emphasis> reasonable
- use of this feature. As such, to initialize your object instances, use by default the base_init function
- and construction properties.
- </para>
- </sect2>
+ <para>
+ Some people sometimes need to construct their object but only after
+ the construction properties have been set. This is possible through
+ the use of the constructor class method as described in
+ <xref linkend="gobject-instantiation"/> or, more simply, using
+ the constructed class method available since GLib 2.12.
+ </para>
+ </sect1>
- <sect2 id="howto-gobject-destruction">
- <title>Object Destruction</title>
+ <sect1 id="howto-gobject-destruction">
+ <title>Object Destruction</title>
- <para>
- Again, it is often difficult to figure out which mechanism to use to hook into the object's
- destruction process: when the last <function>g_object_unref</function> function call is made,
- a lot of things happen as described in <xref linkend="gobject-destruction-table"/>.
- </para>
+ <para>
+ Again, it is often difficult to figure out which mechanism to use to
+ hook into the object's destruction process: when the last
+ <function><link linkend="g-object-unref">g_object_unref</link></function>
+ function call is made, a lot of things happen as described in
+ <xref linkend="gobject-destruction-table"/>.
+ </para>
- <para>
- The destruction process of your object must be split is two different phases: you must override
- both the dispose and the finalize class methods.
+ <para>
+ The destruction process of your object might be split in two different
+ phases: dispose and the finalize.
<programlisting>
-struct _MamanBarPrivate {
- gboolean dispose_has_run;
+#define MAMAN_BAR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MAMAN_TYPE_BAR, MamanBarPrivate))
+
+struct _MamanBarPrivate
+{
+ GObject *an_object;
+
+ gchar *a_string;
};
+G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT);
+
static void
-bar_dispose (MamanBar *self)
+maman_bar_dispose (GObject *gobject)
{
- if (self->private->dispose_has_run) {
- /* If dispose did already run, return. */
- return;
- }
- /* Make sure dispose does not run twice. */
- object->private->dispose_has_run = TRUE;
+ MamanBar *self = MAMAN_BAR (gobject);
/*
* In dispose, you are supposed to free all types referenced from this
* the most simple solution is to unref all members on which you own a
* reference.
*/
+
+ /* dispose might be called multiple times, so we must guard against
+ * calling g_object_unref() on an invalid GObject.
+ */
+ if (self->priv->an_object)
+ {
+ g_object_unref (self->priv->an_object);
+
+ self->priv->an_object = NULL;
+ }
+
+ /* Chain up to the parent class */
+ G_OBJECT_CLASS (maman_bar_parent_class)->dispose (gobject);
}
static void
-bar_finalize (MamanBar *self)
+maman_bar_finalize (GObject *gobject)
{
- /*
- * Here, complete object destruction.
- * You might not need to do much...
- */
- g_free (self->private);
+ MamanBar *self = MAMAN_BAR (gobject);
+
+ g_free (self->priv->a_string);
+
+ /* Chain up to the parent class */
+ G_OBJECT_CLASS (maman_bar_parent_class)->finalize (gobject);
}
static void
-bar_class_init (BarClass *klass)
+maman_bar_class_init (MamanBarClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- gobject_class->dispose = bar_dispose;
- gobject_class->finalize = bar_finalize;
+ gobject_class->dispose = maman_bar_dispose;
+ gobject_class->finalize = maman_bar_finalize;
+
+ g_type_class_add_private (klass, sizeof (MamanBarPrivate));
}
static void
-maman_bar_init (GTypeInstance *instance,
- gpointer g_class)
+maman_bar_init (MamanBar *self);
{
- MamanBar *self = (MamanBar *)instance;
- self->private = g_new0 (MamanBarPrivate, 1);
- self->private->dispose_has_run = FALSE;
-}
-</programlisting>
- </para>
+ self->priv = MAMAN_BAR_GET_PRIVATE (self);
- <para>
- Add similar code to your GObject, make sure the code still builds and runs: dispose and finalize must be called
- during the last unref.
- It is possible that object methods might be invoked after dispose is run and before finalize runs. GObject
- does not consider this to be a program error: you must gracefully detect this and neither crash nor warn
- the user. To do this, you need something like the following code at the start of each object method, to make
- sure the object's data is still valid before manipulating it:
-<programlisting>
-if (self->private->dispose_has_run) {
- /* Dispose has run. Data is not valid anymore. */
- return;
+ self->priv->an_object = g_object_new (MAMAN_TYPE_BAZ, NULL);
+ self->priv->a_string = g_strdup ("Maman");
}
</programlisting>
- </para>
- </sect2>
+ </para>
- <sect2 id="howto-gobject-methods">
- <title>Object methods</title>
+ <para>
+ Add similar code to your GObject, make sure the code still builds
+ and runs: dispose and finalize must be called during the last unref.
+ </para>
- <para>
- Just as with C++, there are many different ways to define object
- methods and extend them: the following list and sections draw on C++ vocabulary.
- (Readers are expected to know basic C++ buzzwords. Those who have not had to
- write C++ code recently can refer to <ulink>XXXX</ulink> to refresh their
- memories.)
- <itemizedlist>
- <listitem><para>
- non-virtual public methods,
- </para></listitem>
- <listitem><para>
- virtual public methods and
- </para></listitem>
- <listitem><para>
- virtual private methods
- </para></listitem>
- </itemizedlist>
- </para>
+ <para>
+ It is possible that object methods might be invoked after dispose is
+ run and before finalize runs. GObject does not consider this to be a
+ program error: you must gracefully detect this and neither crash nor
+ warn the user.
+ </para>
+ </sect1>
+
+ <sect1 id="howto-gobject-methods">
+ <title>Object methods</title>
+
+ <para>
+ Just as with C++, there are many different ways to define object
+ methods and extend them: the following list and sections draw on
+ C++ vocabulary. (Readers are expected to know basic C++ buzzwords.
+ Those who have not had to write C++ code recently can refer to e.g.
+ <ulink url="http://www.cplusplus.com/doc/tutorial/"/> to refresh
+ their memories.)
+ <itemizedlist>
+ <listitem><para>
+ non-virtual public methods,
+ </para></listitem>
+ <listitem><para>
+ virtual public methods and
+ </para></listitem>
+ <listitem><para>
+ virtual private methods
+ </para></listitem>
+ </itemizedlist>
+ </para>
- <sect3>
- <title>non-virtual public methods</title>
+ <sect2>
+ <title>Non-virtual public methods</title>
- <para>
- These are the simplest: you want to provide a simple method which can act on your object. All you need
- to do is to provide a function prototype in the header and an implementation of that prototype
- in the source file.
+ <para>
+ These are the simplest: you want to provide a simple method which
+ can act on your object. All you need to do is to provide a function
+ prototype in the header and an implementation of that prototype
+ in the source file.
<programlisting>
/* declaration in the header. */
void maman_bar_do_action (MamanBar *self, /* parameters */);
+
/* implementation in the source file */
-void maman_bar_do_action (MamanBar *self, /* parameters */)
+void
+maman_bar_do_action (MamanBar *self, /* parameters */)
{
+ g_return_if_fail (MAMAN_IS_BAR (self));
+
/* do stuff here. */
}
</programlisting>
- </para>
+ </para>
- <para>There is really nothing scary about this.</para>
- </sect3>
+ <para>There is really nothing scary about this.</para>
+ </sect2>
- <sect3>
- <title>Virtual Public methods</title>
+ <sect2>
+ <title>Virtual public methods</title>
- <para>
- This is the preferred way to create polymorphic GObjects. All you need to do is to
- define the common method and its class function in the public header, implement the
- common method in the source file and re-implement the class function in each object
- which inherits from you.
+ <para>
+ This is the preferred way to create polymorphic GObjects. All you
+ need to do is to define the common method and its class function in
+ the public header, implement the common method in the source file
+ and re-implement the class function in each object which inherits
+ from you.
<programlisting>
/* declaration in maman-bar.h. */
-struct _MamanBarClass {
- GObjectClass parent;
+struct _MamanBarClass
+{
+ GObjectClass parent_class;
/* stuff */
void (*do_action) (MamanBar *self, /* parameters */);
};
+
void maman_bar_do_action (MamanBar *self, /* parameters */);
+
/* implementation in maman-bar.c */
-void maman_bar_do_action (MamanBar *self, /* parameters */)
+void
+maman_bar_do_action (MamanBar *self, /* parameters */)
{
- MAMAN_BAR_GET_CLASS (self)->do_action (self, /* parameters */);
+ g_return_if_fail (MAMAN_IS_BAR (self));
+
+ MAMAN_BAR_GET_CLASS (self)->do_action (self, /* parameters */);
}
</programlisting>
- The code above simply redirects the do_action call to the relevant class function. Some users,
- concerned about performance, do not provide the <function>maman_bar_do_action</function>
- wrapper function and require users to de-reference the class pointer themselves. This is not such
- a great idea in terms of encapsulation and makes it difficult to change the object's implementation
- afterwards, should this be needed.
- </para>
-
- <para>
- Other users, also concerned by performance issues, declare the <function>maman_bar_do_action</function>
- function inline in the header file. This, however, makes it difficult to change the
- object's implementation later (although easier than requiring users to directly de-reference the class
- function) and is often difficult to write in a portable way (the <emphasis>inline</emphasis> keyword
- is not part of the C standard).
- </para>
-
- <para>
- In doubt, unless a user shows you hard numbers about the performance cost of the function call,
- just <function>maman_bar_do_action</function> in the source file.
- </para>
-
- <para>
- Please, note that it is possible for you to provide a default implementation for this class method in
- the object's class_init function: initialize the klass->do_action field to a pointer to the actual
- implementation. You can also make this class method pure virtual by initializing the klass->do_action
- field to NULL:
+ The code above simply redirects the do_action call to the relevant
+ class function. Some users, concerned about performance, do not
+ provide the <function>maman_bar_do_action</function> wrapper function
+ and require users to dereference the class pointer themselves. This
+ is not such a great idea in terms of encapsulation and makes it
+ difficult to change the object's implementation afterwards, should
+ this be needed.
+ </para>
+
+ <para>
+ Other users, also concerned by performance issues, declare
+ the <function>maman_bar_do_action</function> function inline in the
+ header file. This, however, makes it difficult to change the
+ object's implementation later (although easier than requiring users
+ to directly dereference the class function) and is often difficult
+ to write in a portable way (the <emphasis>inline</emphasis> keyword
+ is part of the C99 standard but not every compiler supports it).
+ </para>
+
+ <para>
+ In doubt, unless a user shows you hard numbers about the performance
+ cost of the function call, just implement <function>maman_bar_do_action</function>
+ in the source file.
+ </para>
+
+ <para>
+ Please, note that it is possible for you to provide a default
+ implementation for this class method in the object's
+ <function>class_init</function> function: initialize the
+ klass->do_action field to a pointer to the actual implementation.
+ You can also make this class method pure virtual by initializing
+ the klass->do_action field to NULL:
<programlisting>
-static void
+static void
maman_bar_real_do_action_two (MamanBar *self, /* parameters */)
{
- /* Default implementation for the virtual method. */
+ /* Default implementation for the virtual method. */
}
static void
maman_bar_class_init (BarClass *klass)
{
- /* pure virtual method: mandates implementation in children. */
- klass->do_action_one = NULL;
- /* merely virtual method. */
- klass->do_action_two = maman_bar_real_do_action_two;
+ /* pure virtual method: mandates implementation in children. */
+ klass->do_action_one = NULL;
+
+ /* merely virtual method. */
+ klass->do_action_two = maman_bar_real_do_action_two;
}
-void maman_bar_do_action_one (MamanBar *self, /* parameters */)
+void
+maman_bar_do_action_one (MamanBar *self, /* parameters */)
{
- MAMAN_BAR_GET_CLASS (self)->do_action_one (self, /* parameters */);
+ g_return_if_fail (MAMAN_IS_BAR (self));
+
+ MAMAN_BAR_GET_CLASS (self)->do_action_one (self, /* parameters */);
}
-void maman_bar_do_action_two (MamanBar *self, /* parameters */)
+
+void
+maman_bar_do_action_two (MamanBar *self, /* parameters */)
{
- MAMAN_BAR_GET_CLASS (self)->do_action_two (self, /* parameters */);
+ g_return_if_fail (MAMAN_IS_BAR (self));
+
+ MAMAN_BAR_GET_CLASS (self)->do_action_two (self, /* parameters */);
}
</programlisting>
- </para>
- </sect3>
+ </para>
+ </sect2>
- <sect3>
- <title>Virtual Private Methods</title>
+ <sect2>
+ <title>Virtual private Methods</title>
- <para>
- These are very similar to Virtual Public methods. They just don't have a public function to call the
- function directly. The header file contains only a declaration of the class function:
+ <para>
+ These are very similar to Virtual Public methods. They just don't
+ have a public function to call the function directly. The header
+ file contains only a declaration of the class function:
<programlisting>
/* declaration in maman-bar.h. */
-struct _MamanBarClass {
+struct _MamanBarClass
+{
GObjectClass parent;
/* stuff */
- void (*helper_do_specific_action) (MamanBar *self, /* parameters */);
+ void (* helper_do_specific_action) (MamanBar *self, /* parameters */);
};
+
void maman_bar_do_any_action (MamanBar *self, /* parameters */);
</programlisting>
- These class functions are often used to delegate part of the job to child classes:
+ These class functions are often used to delegate part of the job
+ to child classes:
<programlisting>
/* this accessor function is static: it is not exported outside of this file. */
static void
maman_bar_do_specific_action (MamanBar *self, /* parameters */)
{
- MAMAN_BAR_GET_CLASS (self)->do_specific_action (self, /* parameters */);
+ MAMAN_BAR_GET_CLASS (self)->do_specific_action (self, /* parameters */);
}
-void maman_bar_do_any_action (MamanBar *self, /* parameters */)
+void
+maman_bar_do_any_action (MamanBar *self, /* parameters */)
{
/* random code here */
/*
- * Try to execute the requested action. Maybe the requested action cannot be implemented
- * here. So, we delegate its implementation to the child class:
+ * Try to execute the requested action. Maybe the requested action
+ * cannot be implemented here. So, we delegate its implementation
+ * to the child class:
*/
- maman_bar_do_specific_action (self, /* parameters */);
+ maman_bar_do_specific_action (self, /* parameters */);
/* other random code here */
}
</programlisting>
- </para>
+ </para>
- <para>
- Again, it is possible to provide a default implementation for this private virtual class function:
+ <para>
+ Again, it is possible to provide a default implementation for this
+ private virtual class function:
<programlisting>
static void
maman_bar_class_init (MamanBarClass *klass)
{
- /* pure virtual method: mandates implementation in children. */
- klass->do_specific_action_one = NULL;
- /* merely virtual method. */
- klass->do_specific_action_two = maman_bar_real_do_specific_action_two;
+ /* pure virtual method: mandates implementation in children. */
+ klass->do_specific_action_one = NULL;
+
+ /* merely virtual method. */
+ klass->do_specific_action_two = maman_bar_real_do_specific_action_two;
}
</programlisting>
- </para>
+ </para>
- <para>
- Children can then implement the subclass with code such as:
+ <para>
+ Children can then implement the subclass with code such as:
<programlisting>
static void
maman_bar_subtype_class_init (MamanBarSubTypeClass *klass)
{
- MamanBarClass *bar_class = MAMAN_BAR_CLASS (klass);
- /* implement pure virtual class function. */
- bar_class->do_specific_action_one = maman_bar_subtype_do_specific_action_one;
+ MamanBarClass *bar_class = MAMAN_BAR_CLASS (klass);
+
+ /* implement pure virtual class function. */
+ bar_class->do_specific_action_one = maman_bar_subtype_do_specific_action_one;
}
</programlisting>
- </para>
+ </para>
+ </sect2>
+ </sect1>
+
+ <sect1 id="howto-gobject-chainup">
+ <title>Chaining up</title>
+
+ <para>Chaining up is often loosely defined by the following set of
+ conditions:
+ <itemizedlist>
+ <listitem><para>Parent class A defines a public virtual method named <function>foo</function> and
+ provides a default implementation.</para></listitem>
+ <listitem><para>Child class B re-implements method <function>foo</function>.</para></listitem>
+ <listitem><para>In the method B::foo, the child class B calls its parent class method A::foo.</para></listitem>
+ </itemizedlist>
+ There are many uses to this idiom:
+ <itemizedlist>
+ <listitem><para>You need to change the behaviour of a class without modifying its code. You create
+ a subclass to inherit its implementation, re-implement a public virtual method to modify the behaviour
+ slightly and chain up to ensure that the previous behaviour is not really modified, just extended.
+ </para></listitem>
+ <listitem><para>You are lazy, you have access to the source code of the parent class but you don't want
+ to modify it to add method calls to new specialized method calls: it is faster to hack the child class
+ to chain up than to modify the parent to call down.</para></listitem>
+ <listitem><para>You need to implement the Chain Of Responsibility pattern: each object of the inheritance
+ tree chains up to its parent (typically, at the beginning or the end of the method) to ensure that
+ they each handler is run in turn.</para></listitem>
+ </itemizedlist>
+ I am personally not really convinced any of the last two uses are really a good idea but since this
+ programming idiom is often used, this section attempts to explain how to implement it.
+ </para>
- <para>
- Finally, it is interesting to note that, just like in C++, it is possible
- to make each object class method chain to its parent class method:
+ <para>
+ To explicitly 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
+ 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
+ the resulting class structure. Since the class_init callback is responsible for overwriting the class structure
+ with the user re-implementations of the class methods, we cannot merely use the modified copy of the parent class
+ structure stored in our derived instance. We want to get a copy of the class structure of an instance of the parent
+ class.
+ </para>
+ </footnote>
+ </para>
+
+ <para>The function <function><link linkend="g-type-class-peek-parent">g_type_class_peek_parent</link></function> is used to access the original parent
+ class structure. Its input is a pointer to the class of the derived object and it returns a pointer
+ to the original parent class structure. The code below shows how you could use it:
<programlisting>
-static void
-maman_bar_real_do_action_two (MamanBar *self, /* parameters */)
+static void
+b_method_to_call (B *obj, int a)
{
- MamanBarClass *bar_class = g_type_class_peek_parent (klass);
- /* chain up */
- bar_class->do_action (self, /* parameters */);
+ BClass *klass;
+ AClass *parent_class;
- /* do local stuff here. */
-}
+ klass = B_GET_CLASS (obj);
+ parent_class = g_type_class_peek_parent (klass);
-static void
-maman_bar_subtype_class_init (MamanBarSubTypeClass *klass)
-{
- MamanBarClass *bar_class = MAMAN_BAR_CLASS (klass);
- /* implement pure virtual class function. */
- bar_class->do_specific_action_one = maman_bar_subtype_do_specific_action_one;
+ /* do stuff before chain up */
+
+ parent_class->method_to_call (obj, a);
+
+ /* do stuff after chain up */
}
</programlisting>
- </para>
- </sect3>
- </sect2>
-
+ </para>
</sect1>
-<!--
- End Howto GObject
--->
-
+</chapter>
+<!-- End Howto GObject -->
-<!--
- Howto Interfaces
--->
+<chapter id="howto-interface">
+ <title>How to define and implement interfaces</title>
- <sect1 id="howto-interface">
- <title>How To define and implement Interfaces ?</title>
-
- <sect2 id="howto-interface-define">
- <title>How To define Interfaces ?</title>
-
- <para>
- The bulk of interface definition has already been shown in <xref linkend="gtype-non-instantiable-classed"/>
- but I feel it is needed to show exactly how to create an interface. The sample source code
- associated to this section can be found in the documentation's source tarball, in the
- <filename>sample/interface/maman-ibaz.{h|c}</filename> file.
- </para>
+ <sect1 id="howto-interface-define">
+ <title>How to define interfaces</title>
+
+ <para>
+ The bulk of interface definition has already been shown in <xref linkend="gtype-non-instantiable-classed"/>
+ but I feel it is needed to show exactly how to create an interface.
+ </para>
- <para>
- As above, the first step is to get the header right:
+ <para>
+ As above, the first step is to get the header right:
<programlisting>
-#ifndef MAMAN_IBAZ_H
-#define MAMAN_IBAZ_H
+#ifndef __MAMAN_IBAZ_H__
+#define __MAMAN_IBAZ_H__
#include <glib-object.h>
-#define MAMAN_IBAZ_TYPE (maman_ibaz_get_type ())
-#define MAMAN_IBAZ(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_IBAZ_TYPE, MamanIbaz))
-#define MAMAN_IBAZ_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), MAMAN_IBAZ_TYPE, MamanIbazClass))
-#define MAMAN_IS_IBAZ(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_IBAZ_TYPE))
-#define MAMAN_IS_IBAZ_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), MAMAN_IBAZ_TYPE))
-#define MAMAN_IBAZ_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), MAMAN_IBAZ_TYPE, MamanIbazClass))
+#define MAMAN_TYPE_IBAZ (maman_ibaz_get_type ())
+#define MAMAN_IBAZ(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_TYPE_IBAZ, MamanIbaz))
+#define MAMAN_IS_IBAZ(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_IBAZ))
+#define MAMAN_IBAZ_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), MAMAN_TYPE_IBAZ, MamanIbazInterface))
-typedef struct _MamanIbaz MamanIbaz; /* dummy object */
-typedef struct _MamanIbazClass MamanIbazClass;
+typedef struct _MamanIbaz MamanIbaz; /* dummy object */
+typedef struct _MamanIbazInterface MamanIbazInterface;
-struct _MamanIbazClass {
- GTypeInterface parent;
+struct _MamanIbazInterface
+{
+ GTypeInterface parent_iface;
- void (*do_action) (MamanIbaz *self);
+ void (*do_action) (MamanIbaz *self);
};
GType maman_ibaz_get_type (void);
void maman_ibaz_do_action (MamanIbaz *self);
-#endif //MAMAN_IBAZ_H
+#endif /* __MAMAN_IBAZ_H__ */
</programlisting>
- This code is almost exactly similar to the code for a normal <type>GType</type>
- which derives from a <type>GObject</type> except for a few details:
- <itemizedlist>
- <listitem><para>
- The <function>_GET_CLASS</function> macro is not implemented with
- <function>G_TYPE_INSTANCE_GET_CLASS</function> but with <function>G_TYPE_INSTANCE_GET_INTERFACE</function>.
- </para></listitem>
- <listitem><para>
- The instance type, <type>MamanIbaz</type> is not fully defined: it is used merely as an abstract
- type which represents an instance of whatever object which implements the interface.
- </para></listitem>
- </itemizedlist>
- </para>
+ This code is the same as the code for a normal <link linkend="GType"><type>GType</type></link>
+ which derives from a <link linkend="GObject"><type>GObject</type></link> except for a few details:
+ <itemizedlist>
+ <listitem><para>
+ The <function>_GET_CLASS</function> macro is called <function>_GET_INTERFACE</function>
+ and not implemented with <function><link linkend="G-TYPE-INSTANCE-GET-CLASS:CAPS">G_TYPE_INSTANCE_GET_CLASS</link></function>
+ but with <function><link linkend="G-TYPE-INSTANCE-GET-INTERFACE:CAPS">G_TYPE_INSTANCE_GET_INTERFACE</link></function>.
+ </para></listitem>
+ <listitem><para>
+ The instance type, <type>MamanIbaz</type> is not fully defined: it is
+ used merely as an abstract type which represents an instance of
+ whatever object which implements the interface.
+ </para></listitem>
+ <listitem><para>
+ The parent of the <type>MamanIbazInterface</type> is not
+ <type>GObjectClass</type> but <type>GTypeInterface</type>.
+ </para></listitem>
+ </itemizedlist>
+ </para>
- <para>
- The implementation of the <type>MamanIbaz</type> type itself is trivial:
- <itemizedlist>
- <listitem><para><function>maman_ibaz_get_type</function> registers the
- type in the type system.
- </para></listitem>
- <listitem><para><function>maman_ibaz_base_init</function> is expected
- to register the interface's signals if there are any (we will see a bit
- (later how to use them). Make sure to use a static local boolean variable
- to make sure not to run the initialization code twice (as described in
- <xref linkend="gtype-non-instantiable-classed-init"/>,
- <function>base_init</function> is run once for each interface implementation
- instanciation)</para></listitem>
- <listitem><para><function>maman_ibaz_do_action</function> de-references the class
- structure to access its associated class function and calls it.
- </para></listitem>
- </itemizedlist>
+ <para>
+ The implementation of the <type>MamanIbaz</type> type itself is trivial:
+ <itemizedlist>
+ <listitem><para><function>maman_ibaz_get_type</function> registers the
+ type in the type system.
+ </para></listitem>
+ <listitem><para><function>maman_ibaz_base_init</function> is expected
+ to register the interface's signals if there are any (we will see a bit
+ (later how to use them). Make sure to use a static local boolean variable
+ to make sure not to run the initialization code twice (as described in
+ <xref linkend="gtype-non-instantiable-classed-init"/>,
+ <function>base_init</function> is run once for each interface implementation
+ instantiation)</para></listitem>
+ <listitem><para><function>maman_ibaz_do_action</function> dereferences
+ the class structure to access its associated class function and calls it.
+ </para></listitem>
+ </itemizedlist>
<programlisting>
static void
maman_ibaz_base_init (gpointer g_class)
{
- static gboolean initialized = FALSE;
+ static gboolean is_initialized = FALSE;
- if (!initialized) {
- /* create interface signals here. */
- initialized = TRUE;
- }
+ if (!is_initialized)
+ {
+ /* add properties and signals to the interface here */
+
+ is_initialized = TRUE;
+ }
}
GType
maman_ibaz_get_type (void)
{
- static GType type = 0;
- if (type == 0) {
- static const GTypeInfo info = {
- sizeof (MamanIbazClass),
- maman_ibaz_base_init, /* base_init */
- NULL, /* base_finalize */
- NULL, /* class_init */
- NULL, /* class_finalize */
- NULL, /* class_data */
- 0,
- 0, /* n_preallocs */
- NULL /* instance_init */
- };
- type = g_type_register_static (G_TYPE_INTERFACE, "MamanIbaz", &info, 0);
- }
- return type;
+ static GType iface_type = 0;
+ if (iface_type == 0)
+ {
+ const GTypeInfo info = {
+ sizeof (MamanIbazInterface),
+ maman_ibaz_base_init, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ iface_type = g_type_register_static (G_TYPE_INTERFACE, "MamanIbaz",
+ &info, 0);
+ }
+
+ return iface_type;
}
-void maman_ibaz_do_action (MamanIbaz *self)
+void
+maman_ibaz_do_action (MamanIbaz *self)
{
- MAMAN_IBAZ_GET_CLASS (self)->do_action (self);
+ g_return_if_fail (MAMAN_IS_IBAZ (self));
+
+ MAMAN_IBAZ_GET_INTERFACE (self)->do_action (self);
}
</programlisting>
</para>
- </sect2>
-
- <sect2 id="howto-interface-implement">
- <title>How To define and implement an implementation of an Interface ?</title>
-
+ </sect1>
+
+ <sect1 id="howto-interface-implement">
+ <title>How To define implement an Interface?</title>
+
<para>
- Once the interface is defined, implementing it is rather trivial. Source code showing how to do this
- for the <type>IBaz</type> interface defined in the previous section is located in
- <filename>sample/interface/maman-baz.{h|c}</filename>.
+ Once the interface is defined, implementing it is rather trivial.
</para>
-
+
<para>
- The first step is to define a normal GType. Here, we have decided to use a GType which derives from
- GObject. Its name is <type>MamanBaz</type>:
+ The first step is to define a normal GObject class, like:
<programlisting>
-#ifndef MAMAN_BAZ_H
-#define MAMAN_BAZ_H
+#ifndef __MAMAN_BAZ_H__
+#define __MAMAN_BAZ_H__
-#include <glib-object.h>
+#include <glib-object.h>
-#define MAMAN_BAZ_TYPE (maman_baz_get_type ())
-#define MAMAN_BAZ(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_BAZ_TYPE, Mamanbaz))
-#define MAMAN_BAZ_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), MAMAN_BAZ_TYPE, MamanbazClass))
-#define MAMAN_IS_BAZ(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_BAZ_TYPE))
-#define MAMAN_IS_BAZ_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), MAMAN_BAZ_TYPE))
-#define MAMAN_BAZ_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), MAMAN_BAZ_TYPE, MamanbazClass))
+#define MAMAN_TYPE_BAZ (maman_baz_get_type ())
+#define MAMAN_BAZ(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_TYPE_BAZ, Mamanbaz))
+#define MAMAN_IS_BAZ(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_BAZ))
+#define MAMAN_BAZ_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MAMAN_TYPE_BAZ, MamanbazClass))
+#define MAMAN_IS_BAZ_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MAMAN_TYPE_BAZ))
+#define MAMAN_BAZ_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MAMAN_TYPE_BAZ, MamanbazClass))
-typedef struct _MamanBaz MamanBaz;
-typedef struct _MamanBazClass MamanBazClass;
+typedef struct _MamanBaz MamanBaz;
+typedef struct _MamanBazClass MamanBazClass;
-struct _MamanBaz {
- GObject parent;
- int instance_member;
+struct _MamanBaz
+{
+ GObject parent_instance;
+
+ int instance_member;
};
-struct _MamanBazClass {
- GObjectClass parent;
+struct _MamanBazClass
+{
+ GObjectClass parent_class;
};
GType maman_baz_get_type (void);
-
-#endif //MAMAN_BAZ_H
+#endif /* __MAMAN_BAZ_H__ */
</programlisting>
- There is clearly nothing specifically weird or scary about this header: it does not define any weird API
- or derives from a weird type.
+ There is clearly nothing specifically weird or scary about this header:
+ it does not define any weird API or derives from a weird type.
</para>
-
+
<para>
- The second step is to implement <function>maman_baz_get_type</function>:
+ The second step is to implement <type>MamanBaz</type> by defining
+ its GType. Instead of using <function>G_DEFINE_TYPE</function> we
+ use <function>G_DEFINE_TYPE_WITH_CODE</function> and the
+ <function>G_IMPLEMENT_INTERFACE</function> macros.
<programlisting>
-GType
-maman_baz_get_type (void)
-{
- static GType type = 0;
- if (type == 0) {
- static const GTypeInfo info = {
- sizeof (MamanBazClass),
- NULL, /* base_init */
- NULL, /* base_finalize */
- NULL, /* class_init */
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (MamanBaz),
- 0, /* n_preallocs */
- baz_instance_init /* instance_init */
- };
- static const GInterfaceInfo ibaz_info = {
- (GInterfaceInitFunc) baz_interface_init, /* interface_init */
- NULL, /* interface_finalize */
- NULL /* interface_data */
- };
- type = g_type_register_static (G_TYPE_OBJECT,
- "MamanBazType",
- &info, 0);
- g_type_add_interface_static (type,
- MAMAN_IBAZ_TYPE,
- &ibaz_info);
- }
- return type;
-}
+static void maman_ibaz_interface_init (MamanIbazInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (MamanBar, maman_bar, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (MAMAN_TYPE_IBAZ,
+ maman_ibaz_interface_init));
</programlisting>
- This function is very much like all the similar functions we looked at previously. The only interface-specific
- code present here is the call to <function>g_type_add_interface_static</function> which is used to inform
- the type system that this just-registered <type>GType</type> also implements the interface
- <function>MAMAN_IBAZ_TYPE</function>.
+ This definition is very much like all the similar functions we looked
+ at previously. The only interface-specific code present here is the call
+ to <function>G_IMPLEMENT_INTERFACE</function>.
</para>
+ <note><para>Classes can implement multiple interfaces by using multiple
+ calls to <function>G_IMPLEMENT_INTERFACE</function> inside the call
+ to <function>G_DEFINE_TYPE_WITH_CODE</function>.</para></note>
+
<para>
- <function>baz_interface_init</function>, the interface initialization function, is also pretty simple:
+ <function>maman_baz_interface_init</function>, the interface
+ initialization function: inside it every virtual method of the interface
+ must be assigned to its implementation:
<programlisting>
-static void baz_do_action (MamanBaz *self)
+static void
+maman_baz_do_action (MamanBaz *self)
{
- g_print ("Baz implementation of IBaz interface Action: 0x%x.\n", self->instance_member);
+ g_print ("Baz implementation of IBaz interface Action: 0x%x.\n",
+ self->instance_member);
}
+
static void
-baz_interface_init (gpointer g_iface,
- gpointer iface_data)
+maman_ibaz_interface_init (MamanIbazInterface *iface)
{
- MamanIbazClass *klass = (MamanIbazClass *)g_iface;
- klass->do_action = (void (*) (MamanIbaz *self))baz_do_action;
+ iface->do_action = baz_do_action;
}
+
static void
-baz_instance_init (GTypeInstance *instance,
- gpointer g_class)
+maman_baz_init (MamanBaz *self)
{
- MamanBaz *self = (MamanBaz *)instance;
- self->instance_member = 0xdeadbeaf;
+ MamanBaz *self = MAMAN_BAZ (instance);
+ self->instance_member = 0xdeadbeaf;
}
</programlisting>
- <function>baz_interface_init</function> merely initializes the interface methods to the implementations
- defined by <type>MamanBaz</type>: <function>maman_baz_do_action</function> does nothing very useful
- but it could :)
</para>
-
-</sect2>
-
-<sect2>
- <title>Interface definition prerequisites</title>
-
-
-
- <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
- interface I2. The example below shows the GObject equivalent:
-
+
+ </sect1>
+
+ <sect1>
+ <title>Interface definition prerequisites</title>
+
+ <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 interface I2. The example below shows
+ the GObject equivalent:
<programlisting>
- type = g_type_register_static (G_TYPE_INTERFACE, "MamanIbar", &info, 0);
- /* Make the MamanIbar interface require MamanIbaz interface. */
- g_type_interface_add_prerequisite (type, MAMAN_IBAZ_TYPE);
+ /* inside the GType function of the MamanIbar interface */
+ type = g_type_register_static (G_TYPE_INTERFACE, "MamanIbar", &info, 0);
+
+ /* Make the MamanIbar interface require MamanIbaz interface. */
+ g_type_interface_add_prerequisite (type, MAMAN_TYPE_IBAZ);
</programlisting>
- The code shown above adds the MamanIbaz interface to the list of prerequisites of MamanIbar while the
- code below shows how an implementation can implement both interfaces and register their implementations:
+ The code shown above adds the MamanIbaz interface to the list of
+ prerequisites of MamanIbar while the code below shows how an
+ implementation can implement both interfaces and register their
+ implementations:
<programlisting>
-static void ibar_do_another_action (MamanBar *self)
+static void
+maman_ibar_do_another_action (MamanIbar *ibar)
{
- g_print ("Bar implementation of IBar interface Another Action: 0x%x.\n", self->instance_member);
+ MamanBar *self = MAMAN_BAR (ibar);
+
+ g_print ("Bar implementation of IBar interface Another Action: 0x%x.\n",
+ self->instance_member);
}
static void
-ibar_interface_init (gpointer g_iface,
- gpointer iface_data)
+maman_ibar_interface_init (MamanIbarInterface *iface)
{
- MamanIbarClass *klass = (MamanIbarClass *)g_iface;
- klass->do_another_action = (void (*) (MamanIbar *self))ibar_do_another_action;
+ iface->do_another_action = maman_ibar_do_another_action;
}
-
-static void ibaz_do_action (MamanBar *self)
+static void
+maman_ibaz_do_action (MamanIbaz *ibaz)
{
- g_print ("Bar implementation of IBaz interface Action: 0x%x.\n", self->instance_member);
+ MamanBar *self = MAMAN_BAR (ibaz);
+
+ g_print ("Bar implementation of IBaz interface Action: 0x%x.\n",
+ self->instance_member);
}
static void
-ibaz_interface_init (gpointer g_iface,
- gpointer iface_data)
+maman_ibaz_interface_init (MamanIbazInterface *iface)
{
- MamanIbazClass *klass = (MamanIbazClass *)g_iface;
- klass->do_action = (void (*) (MamanIbaz *self))ibaz_do_action;
+ iface->do_action = maman_ibaz_do_action;
}
-
static void
-bar_instance_init (GTypeInstance *instance,
- gpointer g_class)
+maman_bar_class_init (MamanBarClass *klass)
{
- MamanBar *self = (MamanBar *)instance;
- self->instance_member = 0x666;
+
}
+static void
+maman_bar_init (MamanBar *self)
+{
+ self->instance_member = 0x666;
+}
-GType
-maman_bar_get_type (void)
+G_DEFINE_TYPE_WITH_CODE (MamanBar, maman_bar, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (MAMAN_TYPE_IBAZ,
+ maman_ibaz_interface_init)
+ G_IMPLEMENT_INTERFACE (MAMAN_TYPE_IBAR,
+ maman_ibar_interface_init));
+</programlisting>
+ 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>,
+ which is called by <function>G_IMPLEMENT_INTERFACE</function>, must be
+ invoked first on the interfaces which have no prerequisites and then on
+ the others.
+ </para>
+ </sect1>
+
+ <sect1 id="howto-interface-properties">
+ <title>Interface Properties</title>
+
+ <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
+ <type>maman_ibaz</type> interface example code above, we only need to
+ add one
+ <footnote>
+ <para>
+ That really is one line extended to six for the sake of clarity
+ </para>
+ </footnote>
+ line in the <function>maman_ibaz_base_init</function>
+ <footnote>
+ <para>
+ The <function><link linkend="g-object-interface-install-property">g_object_interface_install_property</link></function>
+ can also be called from <function>class_init</function> but it must
+ not be called after that point.
+ </para>
+ </footnote>
+ as shown below:
+<programlisting>
+static void
+maman_ibaz_base_init (gpointer g_iface)
{
- static GType type = 0;
- if (type == 0) {
- static const GTypeInfo info = {
- sizeof (MamanBarClass),
- NULL, /* base_init */
- NULL, /* base_finalize */
- NULL, /* class_init */
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (MamanBar),
- 0, /* n_preallocs */
- bar_instance_init /* instance_init */
- };
- static const GInterfaceInfo ibar_info = {
- (GInterfaceInitFunc) ibar_interface_init, /* interface_init */
- NULL, /* interface_finalize */
- NULL /* interface_data */
- };
- static const GInterfaceInfo ibaz_info = {
- (GInterfaceInitFunc) ibaz_interface_init, /* interface_init */
- NULL, /* interface_finalize */
- NULL /* interface_data */
- };
- type = g_type_register_static (G_TYPE_OBJECT,
- "MamanBarType",
- &info, 0);
- g_type_add_interface_static (type,
- MAMAN_IBAZ_TYPE,
- &ibaz_info);
- g_type_add_interface_static (type,
- MAMAN_IBAR_TYPE,
- &ibar_info);
- }
- return type;
+ static gboolean is_initialized = FALSE;
+
+ if (!is_initialized)
+ {
+ g_object_interface_install_property (g_iface,
+ g_param_spec_string ("name",
+ "Name",
+ "Name of the MamanIbaz",
+ "maman",
+ G_PARAM_READWRITE));
+ is_initialized = TRUE;
+ }
}
</programlisting>
- It is very important to notice that the order in which interface implementations are added to the main object
- is not random: <function>g_type_interface_static</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
- implement the MamanIbar interface is located in <filename>sample/interface/maman-ibar.{h|c}</filename>
- and <filename>sample/interface/maman-bar.{h|c}</filename>.
+ One point worth noting is that the declared property wasn't assigned an
+ integer ID. The reason being that integer IDs of properties are used
+ 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>
+ An implementation shall declare and define it's properties in the usual
+ way as explained in <xref linkend="gobject-properties"/>, except for one
+ small change: it must declare the properties of the interface it
+ implements using <function><link linkend="g-object-class-override-property">g_object_class_override_property</link></function>
+ instead of <function><link linkend="g-object-class-install-property">g_object_class_install_property</link></function>.
+ The following code snippet shows the modifications needed in the
+ <type>MamanBaz</type> declaration and implementation above:
+<programlisting>
-</sect2>
+struct _MamanBaz
+{
+ GObject parent_instance;
- </sect1>
+ gint instance_member;
+ gchar *name;
+};
-<!--
- End Howto Interfaces
--->
+enum
+{
+ PROP_0,
+ PROP_NAME
+};
-<!--
- start Howto Signals
--->
+static void
+maman_baz_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MamanBaz *baz = MAMAN_BAZ (object);
+ GObject *obj;
+
+ switch (prop_id)
+ {
+ case ARG_NAME:
+ g_free (baz->name);
+ baz->name = g_value_dup_string (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+static void
+maman_baz_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MamanBaz *baz = MAMAN_BAZ (object);
- <sect1 id="howto-signals">
- <title>Howto create and use signals</title>
+ switch (prop_id)
+ {
+ case ARG_NAME:
+ g_value_set_string (value, baz->name);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
- <para>
- The signal system which was built in GType is pretty complex and flexible: it is possible for its users
- to connect at runtime any number of callbacks (implemented in any language for which a binding exists)
- <footnote>
- <para>A python callback can be connected to any signal on any C-based GObject.
- </para>
- </footnote>
+static void
+maman_baz_class_init (MamanBazClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->set_property = maman_baz_set_property;
+ gobject_class->get_property = maman_baz_get_property;
+
+ g_object_class_override_property (gobject_class, PROP_NAME, "name");
+}
+
+</programlisting>
+ </para>
+
+ </sect1>
+</chapter>
+<!-- End Howto Interfaces -->
- to any signal and to stop the emission of any signal at any
- state of the signal emission process. This flexibility makes it possible to use GSignal for much more than
- just emit events which can be received by numerous clients.
+<chapter id="howto-signals">
+ <title>How to create and use signals</title>
+
+ <para>
+ The signal system which was built in GType is pretty complex and
+ flexible: it is possible for its users to connect at runtime any
+ number of callbacks (implemented in any language for which a binding
+ exists)
+ <footnote>
+ <para>A Python callback can be connected to any signal on any
+ C-based GObject.
</para>
+ </footnote>
+ to any signal and to stop the emission of any signal at any
+ state of the signal emission process. This flexibility makes it
+ possible to use GSignal for much more than just emit signals which
+ can be received by numerous clients.
+ </para>
-<sect2>
-<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 has changed something via our MamanFile instance.
+ The code below shows how the user can connect a callback to the
+ "changed" signal.
<programlisting>
- file = g_object_new (MAMAN_FILE_TYPE, NULL);
+file = g_object_new (MAMAN_FILE_TYPE, NULL);
- g_signal_connect (G_OBJECT (file), "write",
- (GCallback)write_event,
- NULL);
+g_signal_connect (file, "changed", G_CALLBACK (changed_event), NULL);
- maman_file_write (file, buffer, 50);
+maman_file_write (file, buffer, strlen (buffer));
</programlisting>
-</para>
-
-<para>
-The <type>MamanFile</type> signal is registered in the class_init function:
+ </para>
+
+ <para>
+ The <type>MamanFile</type> signal is registered in the class_init
+ function:
<programlisting>
- klass->write_signal_id =
- g_signal_newv ("write",
- G_TYPE_FROM_CLASS (g_class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
- NULL /* class closure */,
- NULL /* accumulator */,
- NULL /* accu_data */,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE /* return_type */,
- 0 /* n_params */,
- NULL /* param_types */);
+file_signals[CHANGED] =
+ g_signal_newv ("changed",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
+ NULL /* closure */,
+ NULL /* accumulator */,
+ NULL /* accumulator data */,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE /* return_type */,
+ 0 /* n_params */,
+ NULL /* param_types */);
</programlisting>
-and the signal is emited in <function>maman_file_write</function>:
+ and the signal is emitted in <function>maman_file_write</function>:
<programlisting>
-void maman_file_write (MamanFile *self, guint8 *buffer, guint32 size)
+void
+maman_file_write (MamanFile *self,
+ const guchar *buffer,
+ gssize size)
{
- /* First write data. */
- /* Then, notify user of data written. */
- g_signal_emit (self, MAMAN_FILE_GET_CLASS (self)->write_signal_id,
- 0 /* details */,
- NULL);
+ /* First write data. */
+
+ /* Then, notify user of data written. */
+ g_signal_emit (self, file_signals[CHANGED], 0 /* details */);
}
</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>
-</para>
-
-</sect2>
+ 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 in front of the
+ double underscore is the type of the return value, while the value(s)
+ after the double underscore denote the parameter types.
+ </para>
-<sect2>
-<title>How to provide more flexibility to users ?</title>
+ <para>
+ The header <filename>gobject/gmarshal.h</filename> defines a set of
+ commonly needed closures that one can use. If you want to have complex
+ marshallers for your signals you should probably use glib-genmarshal
+ to autogenerate them from a file containing their return and
+ parameter types.
+ </para>
+ </sect1>
-<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>
+<!--
+ this is utterly wrong and should be completely removed - or rewritten
+ with a better example than writing a buffer using synchronous signals.
-<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>
+ <sect1>
+ <title>How to provide more flexibility to users?</title>
-<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 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>
+ 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 glib-genmarshal 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>
-
-<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>):
+ 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>):
<programlisting>
- GClosure *default_closure;
- GType param_types[2];
-
- default_closure = g_cclosure_new (G_CALLBACK (default_write_signal_handler),
- (gpointer)0xdeadbeaf /* user_data */,
- NULL /* destroy_data */);
-
- param_types[0] = G_TYPE_POINTER;
- param_types[1] = G_TYPE_UINT;
- klass->write_signal_id =
- g_signal_newv ("write",
- G_TYPE_FROM_CLASS (g_class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
- default_closure /* class closure */,
- NULL /* accumulator */,
- NULL /* accu_data */,
- maman_file_complex_VOID__POINTER_UINT,
- G_TYPE_NONE /* return_type */,
- 2 /* n_params */,
- param_types /* param_types */);
+GClosure *default_closure;
+GType param_types[2];
+
+default_closure = g_cclosure_new (G_CALLBACK (default_write_signal_handler),
+ (gpointer)0xdeadbeaf /* user_data */,
+ NULL /* destroy_data */);
+
+param_types[0] = G_TYPE_POINTER;
+param_types[1] = G_TYPE_UINT;
+klass->write_signal_id =
+ g_signal_newv ("write",
+ G_TYPE_FROM_CLASS (g_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
+ default_closure /* class closure */,
+ NULL /* accumulator */,
+ NULL /* accu_data */,
+ maman_file_complex_VOID__POINTER_UINT,
+ G_TYPE_NONE /* return_type */,
+ 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)
{
- g_assert (user_data == (gpointer)0xdeadbeaf);
- /* Here, we trigger the real file write. */
- g_print ("default signal handler: 0x%x %u\n", buffer, size);
+ g_assert (user_data == (gpointer)0xdeadbeaf);
+ /* Here, we trigger the real file write. */
+ 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)
{
- /* trigger event */
- g_signal_emit (self,
- MAMAN_FILE_COMPLEX_GET_CLASS (self)->write_signal_id,
- 0, /* details */
- buffer, size);
+ /* trigger event */
+ g_signal_emit (self,
+ MAMAN_FILE_COMPLEX_GET_CLASS (self)->write_signal_id,
+ 0, /* details */
+ buffer, size);
}
</programlisting>
-</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>g_signal_connect</function>
-and before all handlers connected with <function>g_signal_connect_after</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>
+
+ <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>
<programlisting>
static void complex_write_event_before (GObject *file, guint8 *buffer, guint size, gpointer user_data)
{
- g_assert (user_data == NULL);
- g_print ("Complex Write event before: 0x%x, %u\n", buffer, size);
+ g_assert (user_data == NULL);
+ g_print ("Complex Write event before: 0x%x, %u\n", buffer, size);
}
static void complex_write_event_after (GObject *file, guint8 *buffer, guint size, gpointer user_data)
{
- g_assert (user_data == NULL);
- g_print ("Complex Write event after: 0x%x, %u\n", buffer, size);
+ g_assert (user_data == NULL);
+ g_print ("Complex Write event after: 0x%x, %u\n", buffer, size);
}
static void test_file_complex (void)
{
- guint8 buffer[100];
- GObject *file;
+ guint8 buffer[100];
+ GObject *file;
- file = g_object_new (MAMAN_FILE_COMPLEX_TYPE, NULL);
+ file = g_object_new (MAMAN_FILE_COMPLEX_TYPE, NULL);
- g_signal_connect (G_OBJECT (file), "write",
- (GCallback)complex_write_event_before,
- NULL);
+ g_signal_connect (G_OBJECT (file), "write",
+ (GCallback)complex_write_event_before,
+ NULL);
- g_signal_connect_after (G_OBJECT (file), "write",
- (GCallback)complex_write_event_after,
- NULL);
+ g_signal_connect_after (G_OBJECT (file), "write",
+ (GCallback)complex_write_event_after,
+ NULL);
- maman_file_complex_write (MAMAN_FILE_COMPLEX (file), buffer, 50);
+ maman_file_complex_write (MAMAN_FILE_COMPLEX (file), buffer, 50);
- g_object_unref (G_OBJECT (file));
+ 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>
-
-
- <sect3>
- <title>How most people do the same thing with less code</title>
-
- <para>For many historic reasons related to how the ancestor of GObject used to work in GTK+ 1.x versions,
- there is a much <emphasis>simpler</emphasis>
- <footnote>
- <para>I personally think that this method is horribly mind-twisting: it adds a new indirection
- which unecessarily complicates the overall code path. However, because this method is widely used
- by all of GTK+ and GObject code, readers need to understand it. The reason why this is done that way
- in most of GTK+ is related to the fact that the ancestor of GObject did not provide any other way to
- create a signal with a default handler than this one. Some people have tried to justify that it is done
- that way because it is better, faster (I am extremly doubtfull about the faster bit. As a matter of fact,
- the better bit also mystifies me ;-). I have the feeling no one really knows and everyone does it
- because they copy/pasted code from code which did the same. It is probably better to leave this
- specific trivia to hacker legends domain...
- </para>
- </footnote>
- way to create a signal with a default handler than to create
- a closure by hand and to use the <function>g_signal_newv</function>.
- </para>
-
- <para>For example, <function>g_signal_new</function> can be used to create a signal which uses a default
- handler which is stored in the class structure of the object. More specifically, the class structure
- contains a function pointer which is accessed during signal emission to invoke the default handler and
- the user is expected to provide to <function>g_signal_new</function> the offset from the start of the
- class structure to the function pointer.
- <footnote>
- <para>I would like to point out here that the reason why the default handler of a signal is named everywhere
- a class_closure is probably related to the fact that it used to be really a function pointer stored in
- the class structure.
- </para>
- </footnote>
- </para>
-
- <para>The following code shows the declaration of the <type>MamanFileSimple</type> class structure which contains
- the <function>write</function> function pointer.
+ </para>
+
+-->
+
+<!--
+ this is also utterly wrong on so many levels that I don't even want
+ to enumerate them. it's also full of completely irrelevant footnotes
+ about personal preferences demonstrating a severe lack of whatsoever
+ clue. the whole idea of storing the signal ids inside the Class
+ structure is so fundamentally flawed that I'll require a frontal
+ lobotomy just to forget I've ever seen it.
+
+ <sect2>
+ <title>How most people do the same thing with less code</title>
+
+ <para>For many historic reasons related to how the ancestor of GObject used to work in GTK+ 1.x versions,
+ there is a much <emphasis>simpler</emphasis>
+ <footnote>
+ <para>I personally think that this method is horribly mind-twisting: it adds a new indirection
+ which unnecessarily complicates the overall code path. However, because this method is widely used
+ by all of GTK+ and GObject code, readers need to understand it. The reason why this is done that way
+ in most of GTK+ is related to the fact that the ancestor of GObject did not provide any other way to
+ create a signal with a default handler than this one. Some people have tried to justify that it is done
+ that way because it is better, faster (I am extremely doubtful about the faster bit. As a matter of fact,
+ the better bit also mystifies me ;-). I have the feeling no one really knows and everyone does it
+ because they copy/pasted code from code which did the same. It is probably better to leave this
+ specific trivia to hacker legends domain...
+ </para>
+ </footnote>
+ way to create a signal with a default handler than to create
+ a closure by hand and to use the <function><link linkend="g-signal-newv">g_signal_newv</link></function>.
+ </para>
+
+ <para>For example, <function><link linkend="g-signal-new">g_signal_new</link></function> can be used to create a signal which uses a default
+ handler which is stored in the class structure of the object. More specifically, the class structure
+ contains a function pointer which is accessed during signal emission to invoke the default handler and
+ the user is expected to provide to <function><link linkend="g-signal-new">g_signal_new</link></function> the offset from the start of the
+ class structure to the function pointer.
+ <footnote>
+ <para>I would like to point out here that the reason why the default handler of a signal is named everywhere
+ a class_closure is probably related to the fact that it used to be really a function pointer stored in
+ the class structure.
+ </para>
+ </footnote>
+ </para>
+
+ <para>The following code shows the declaration of the <type>MamanFileSimple</type> class structure which contains
+ the <function>write</function> function pointer.
<programlisting>
struct _MamanFileSimpleClass {
- GObjectClass parent;
+ GObjectClass parent;
- guint write_signal_id;
+ guint write_signal_id;
- /* signal default handlers */
- void (*write) (MamanFileSimple *self, guint8 *buffer, guint size);
+ /* signal default handlers */
+ void (*write) (MamanFileSimple *self, guint8 *buffer, guint size);
};
</programlisting>
- The <function>write</function> function pointer is initialied in the class_init function of the object
- to <function>default_write_signal_handler</function>:
+ The <function>write</function> function pointer is initialized in the class_init function of the object
+ to <function>default_write_signal_handler</function>:
<programlisting>
static void
maman_file_simple_class_init (gpointer g_class,
gpointer g_class_data)
{
- GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
- MamanFileSimpleClass *klass = MAMAN_FILE_SIMPLE_CLASS (g_class);
+ GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
+ MamanFileSimpleClass *klass = MAMAN_FILE_SIMPLE_CLASS (g_class);
- klass->write = default_write_signal_handler;
+ klass->write = default_write_signal_handler;
</programlisting>
- Finally, the signal is created with <function>g_signal_new</function> in the same class_init function:
+ Finally, the signal is created with <function><link linkend="g-signal-new">g_signal_new</link></function> in the same class_init function:
<programlisting>
- klass->write_signal_id =
- g_signal_new ("write",
- G_TYPE_FROM_CLASS (g_class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
- G_STRUCT_OFFSET (MamanFileSimpleClass, write),
- NULL /* accumulator */,
- NULL /* accu_data */,
- maman_file_complex_VOID__POINTER_UINT,
- G_TYPE_NONE /* return_type */,
- 2 /* n_params */,
- G_TYPE_POINTER,
- G_TYPE_UINT);
+klass->write_signal_id =
+ g_signal_new ("write",
+ G_TYPE_FROM_CLASS (g_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
+ G_STRUCT_OFFSET (MamanFileSimpleClass, write),
+ NULL /* accumulator */,
+ NULL /* accu_data */,
+ maman_file_complex_VOID__POINTER_UINT,
+ G_TYPE_NONE /* return_type */,
+ 2 /* n_params */,
+ G_TYPE_POINTER,
+ G_TYPE_UINT);
</programlisting>
- Of note, here, is the 4th argument to the function: it is an integer calculated by the <function>G_STRUCT_OFFSET</function>
+ Of note, here, is the 4th argument to the function: it is an integer calculated by the <function><link linkend="G-STRUCT-OFFSET">G_STRUCT_OFFSET</link></function>
macro which indicates the offset of the member <emphasis>write</emphasis> from the start of the
<type>MamanFileSimpleClass</type> class structure.
- <footnote>
- <para>GSignal uses this offset to create a special wrapper closure
- which first retrieves the target function pointer before calling it.
- </para>
- </footnote>
- </para>
-
- <para>
- While the complete code for this type of default handler looks less clutered as shown in
- <filename>sample/signal/maman-file-simple.{h|c}</filename>, it contains numerous subtleties.
- The main subtle point which everyone must be aware of is that the signature of the default
- handler created that way does not have a user_data argument:
- <function>default_write_signal_handler</function> is different in
- <filename>sample/signal/maman-file-complex.c</filename> and in
- <filename>sample/signal/maman-file-simple.c</filename>.
- </para>
-
- <para>If you have doubts about which method to use, I would advise you to use the second one which
- involves <function>g_signal_new</function> rather than <function>g_signal_newv</function>:
- it is better to write code which looks like the vast majority of other GTK+/Gobject code than to
- do it your own way. However, now, you know why.
- </para>
-
- </sect3>
-
-
-</sect2>
-
-
-
-<sect2>
- <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
- the signal emission process thanks to <function>g_signal_connect</function>,
- <function>g_signal_connect_after</function> and G_SIGNAL_RUN_LAST, it is time to look into how your
- users can and will screw you. This is also interesting to know how you too, can screw other people.
- This will make you feel good and eleet.
- </para>
-
- <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
- pointer in the class structure (which is the prefered way to create a default signal handler,
- as discussed in the previous section).</para></listitem>
- </itemizedlist>
- </para>
-
- <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,
- you can call <function>g_signal_stop_by_name</function>. Its use is very simple which is why I won't detail
- it further.
- </para>
+ <footnote>
+ <para>GSignal uses this offset to create a special wrapper closure
+ which first retrieves the target function pointer before calling it.
+ </para>
+ </footnote>
+ </para>
+
+ <para>
+ While the complete code for this type of default handler looks less cluttered as shown in
+ <filename>sample/signal/maman-file-simple.{h|c}</filename>, it contains numerous subtleties.
+ The main subtle point which everyone must be aware of is that the signature of the default
+ handler created that way does not have a user_data argument:
+ <function>default_write_signal_handler</function> is different in
+ <filename>sample/signal/maman-file-complex.c</filename> and in
+ <filename>sample/signal/maman-file-simple.c</filename>.
+ </para>
+
+ <para>If you have doubts about which method to use, I would advise you to use the second one which
+ involves <function><link linkend="g-signal-new">g_signal_new</link></function> rather than <function><link linkend="g-signal-newv">g_signal_newv</link></function>:
+ it is better to write code which looks like the vast majority of other GTK+/GObject code than to
+ do it your own way. However, now, you know why.
+ </para>
+
+ </sect2>
- <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>
+ </sect1>
+-->
- <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>
+<!--
+ yet another pointless section. if we are scared of possible abuses
+ from the users then we should not be mentioning it inside a tutorial
+ for beginners. but, obviously, there's nothing to be afraid of - it's
+ just that this section must be completely reworded.
+
+ <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
+ the signal emission process thanks to <function><link linkend="g-signal-connect">g_signal_connect</link></function>,
+ <function><link linkend="g-signal-connect-after">g_signal_connect_after</link></function> and G_SIGNAL_RUN_LAST, it is time to look into how your
+ users can and will screw you. This is also interesting to know how you too, can screw other people.
+ This will make you feel good and eleet.
+ </para>
+
+ <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
+ pointer in the class structure (which is the preferred way to create a default signal handler,
+ as discussed in the previous section).</para></listitem>
+ </itemizedlist>
+ </para>
+
+ <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,
+ 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
+ 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>
+ </sect1>
-</sect2>
+-->
-</sect1>
+</chapter>
<!--
- <sect3>
- <title>Warning on signal creation and default closure</title>
+ <sect2>
+ <title>Warning on signal creation and default closure</title>
- <para>
- Most of the existing code I have seen up to now (in both GTK+, Gnome libraries and
- many GTK+ and Gnome applications) using signals uses a small
- variation of the default handler pattern I have shown in the previous section.
- </para>
+ <para>
+ Most of the existing code I have seen up to now (in both GTK+, GNOME libraries and
+ many GTK+ and GNOME applications) using signals uses a small
+ variation of the default handler pattern I have shown in the previous section.
+ </para>
- <para>
- Usually, the <function>g_signal_new</function> function is preferred over
- <function>g_signal_newv</function>. When <function>g_signal_new</function>
- is used, the default closure is exported as a class function. For example,
- <filename>gobject.h</filename> contains the declaration of <type>GObjectClass</type>
- whose notify class function is the default handler for the <emphasis>notify</emphasis>
- signal:
+ <para>
+ Usually, the <function><link linkend="g-signal-new">g_signal_new</link></function> function is preferred over
+ <function><link linkend="g-signal-newv">g_signal_newv</link></function>. When <function><link linkend="g-signal-new">g_signal_new</link></function>
+ is used, the default closure is exported as a class function. For example,
+ <filename>gobject.h</filename> contains the declaration of <link linkend="GObjectClass"><type>GObjectClass</type></link>
+ whose notify class function is the default handler for the <emphasis>notify</emphasis>
+ signal:
<programlisting>
struct _GObjectClass
{
/* class methods and other stuff. */
/* signals */
- void (*notify) (GObject *object,
- GParamSpec *pspec);
+ void (*notify) (GObject *object,
+ GParamSpec *pspec);
};
</programlisting>
- </para>
+ </para>
- <para>
- <filename>gobject.c</filename>'s <function>g_object_do_class_init</function> function
- registers the <emphasis>notify</emphasis> signal and initializes this class function
- to NULL:
+ <para>
+ <filename>gobject.c</filename>'s <function><link linkend="g-object-do-class-init">g_object_do_class_init</link></function> function
+ registers the <emphasis>notify</emphasis> signal and initializes this class function
+ to NULL:
<programlisting>
static void
g_object_do_class_init (GObjectClass *class)
gobject_signals[NOTIFY] =
g_signal_new ("notify",
- G_TYPE_FROM_CLASS (class),
- G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED | G_SIGNAL_NO_HOOKS,
- G_STRUCT_OFFSET (GObjectClass, notify),
- NULL, NULL,
- g_cclosure_marshal_VOID__PARAM,
- G_TYPE_NONE,
- 1, G_TYPE_PARAM);
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED | G_SIGNAL_NO_HOOKS,
+ G_STRUCT_OFFSET (GObjectClass, notify),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__PARAM,
+ G_TYPE_NONE,
+ 1, G_TYPE_PARAM);
}
</programlisting>
- <function>g_signal_new</function> creates a <type>GClosure</type> which de-references the
- type's class structure to access the class function pointer and invoke it if it not NULL. The
- class function is ignored it is set to NULL.
- </para>
-
- <para>
- To understand the reason for such a complex scheme to access the signal's default handler,
- you must remember the whole reason for the use of these signals. The goal here is to delegate
- a part of the process to the user without requiring the user to subclass the object to override
- one of the class functions. The alternative to subclassing, that is, the use of signals
- to delegate processing to the user, is, however, a bit less optimal in terms of speed: rather
- than just de-referencing a function pointer in a class structure, you must start the whole
- process of signal emission which is a bit heavyweight.
- </para>
-
- <para>
- This is why some people decided to use class functions for some signal's default handlers:
- rather than having users connect a handler to the signal and stop the signal emission
- from within that handler, you just need to override the default class function which is
- supposedly more efficient.
- </para>
-
- </sect3>
--->
-
+ <function><link linkend="g-signal-new">g_signal_new</link></function> creates a <link linkend="GClosure"><type>GClosure</type></link> which dereferences the
+ type's class structure to access the class function pointer and invoke it if it not NULL. The
+ class function is ignored it is set to NULL.
+ </para>
+
+ <para>
+ To understand the reason for such a complex scheme to access the signal's default handler,
+ you must remember the whole reason for the use of these signals. The goal here is to delegate
+ a part of the process to the user without requiring the user to subclass the object to override
+ one of the class functions. The alternative to subclassing, that is, the use of signals
+ to delegate processing to the user, is, however, a bit less optimal in terms of speed: rather
+ than just dereferencing a function pointer in a class structure, you must start the whole
+ process of signal emission which is a bit heavyweight.
+ </para>
+
+ <para>
+ This is why some people decided to use class functions for some signal's default handlers:
+ rather than having users connect a handler to the signal and stop the signal emission
+ from within that handler, you just need to override the default class function which is
+ supposedly more efficient.
+ </para>
-<!--
- <sect1 id="howto-doc">
- <title>How to generate API documentation for your type ?</title>
-
- </sect1>
+ </sect2>
-->
-
- </chapter>
-
-
-
-
-
+</part>