mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-27 07:56:14 +01:00
1732 lines
64 KiB
XML
1732 lines
64 KiB
XML
<?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>
|
|
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>
|
|
|
|
<sect1 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>
|
|
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 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.
|
|
*/
|
|
|
|
/* 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;
|
|
|
|
struct _MamanBar
|
|
{
|
|
GObject parent_instance;
|
|
|
|
/* instance members */
|
|
};
|
|
|
|
struct _MamanBarClass
|
|
{
|
|
GObjectClass parent_class;
|
|
|
|
/* class members */
|
|
};
|
|
|
|
/* used by MAMAN_TYPE_BAR */
|
|
GType maman_bar_get_type (void);
|
|
|
|
/*
|
|
* Method definitions.
|
|
*/
|
|
|
|
#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.
|
|
<programlisting>
|
|
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 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_instance;
|
|
|
|
/*< private >*/
|
|
MamanBarPrivate *priv;
|
|
};
|
|
</programlisting>
|
|
<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))
|
|
|
|
struct _MamanBarPrivate
|
|
{
|
|
int hsize;
|
|
};
|
|
|
|
static void
|
|
maman_bar_class_init (MamanBarClass *klass)
|
|
{
|
|
g_type_class_add_private (klass, sizeof (MamanBarPrivate));
|
|
}
|
|
|
|
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
|
|
*/
|
|
|
|
#include "maman-bar.h"
|
|
|
|
/* If you use Pimpls, include the private structure
|
|
* definition here. Some people create a maman-bar-private.h header
|
|
* which is included by the maman-bar.c file and which contains the
|
|
* definition for this private structure.
|
|
*/
|
|
struct _MamanBarPrivate {
|
|
int member_1;
|
|
/* stuff */
|
|
};
|
|
|
|
/*
|
|
* forward definitions
|
|
*/
|
|
</programlisting>
|
|
</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>
|
|
|
|
<programlisting>
|
|
G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT);
|
|
</programlisting>
|
|
</para>
|
|
|
|
<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>
|
|
|
|
<sect1 id="howto-gobject-construction">
|
|
<title>Object Construction</title>
|
|
|
|
<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>
|
|
<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>
|
|
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 (MamanBar *self)
|
|
{
|
|
self->priv = MAMAN_BAR_GET_PRIVATE (self);
|
|
|
|
/* 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>
|
|
</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
|
|
<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.
|
|
<informalexample><programlisting>
|
|
enum {
|
|
PROP_0,
|
|
|
|
PROP_MAMAN,
|
|
|
|
N_PROPERTIES
|
|
};
|
|
|
|
static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
|
|
|
|
static void
|
|
bar_class_init (MamanBarClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
gobject_class->set_property = bar_set_property;
|
|
gobject_class->get_property = bar_get_property;
|
|
|
|
obj_properties[PROP_MAMAN] =
|
|
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_object_class_install_properties (gobject_class,
|
|
N_PROPERTIES,
|
|
obj_properties);
|
|
}
|
|
</programlisting></informalexample>
|
|
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>
|
|
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>
|
|
|
|
<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><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 might be split in two different
|
|
phases: dispose and the finalize.
|
|
<programlisting>
|
|
#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
|
|
maman_bar_dispose (GObject *gobject)
|
|
{
|
|
MamanBar *self = MAMAN_BAR (gobject);
|
|
|
|
/*
|
|
* In dispose, you are supposed to free all types referenced from this
|
|
* object which might themselves hold a reference to self. Generally,
|
|
* 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
|
|
maman_bar_finalize (GObject *gobject)
|
|
{
|
|
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
|
|
maman_bar_class_init (MamanBarClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
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 (MamanBar *self);
|
|
{
|
|
self->priv = MAMAN_BAR_GET_PRIVATE (self);
|
|
|
|
self->priv->an_object = g_object_new (MAMAN_TYPE_BAZ, NULL);
|
|
self->priv->a_string = g_strdup ("Maman");
|
|
}
|
|
</programlisting>
|
|
</para>
|
|
|
|
<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>
|
|
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>
|
|
|
|
<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.
|
|
<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 */)
|
|
{
|
|
g_return_if_fail (MAMAN_IS_BAR (self));
|
|
|
|
/* do stuff here. */
|
|
}
|
|
</programlisting>
|
|
</para>
|
|
|
|
<para>There is really nothing scary about this.</para>
|
|
</sect2>
|
|
|
|
<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.
|
|
<programlisting>
|
|
/* declaration in maman-bar.h. */
|
|
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 */)
|
|
{
|
|
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 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
|
|
maman_bar_real_do_action_two (MamanBar *self, /* parameters */)
|
|
{
|
|
/* 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;
|
|
}
|
|
|
|
void
|
|
maman_bar_do_action_one (MamanBar *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 */)
|
|
{
|
|
g_return_if_fail (MAMAN_IS_BAR (self));
|
|
|
|
MAMAN_BAR_GET_CLASS (self)->do_action_two (self, /* parameters */);
|
|
}
|
|
</programlisting>
|
|
</para>
|
|
</sect2>
|
|
|
|
<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:
|
|
<programlisting>
|
|
/* declaration in maman-bar.h. */
|
|
struct _MamanBarClass
|
|
{
|
|
GObjectClass parent;
|
|
|
|
/* stuff */
|
|
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:
|
|
<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 */);
|
|
}
|
|
|
|
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:
|
|
*/
|
|
maman_bar_do_specific_action (self, /* parameters */);
|
|
|
|
/* other random code here */
|
|
}
|
|
</programlisting>
|
|
</para>
|
|
|
|
<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;
|
|
}
|
|
</programlisting>
|
|
</para>
|
|
|
|
<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;
|
|
}
|
|
</programlisting>
|
|
</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>
|
|
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
|
|
b_method_to_call (B *obj, int a)
|
|
{
|
|
BClass *klass;
|
|
AClass *parent_class;
|
|
|
|
klass = B_GET_CLASS (obj);
|
|
parent_class = g_type_class_peek_parent (klass);
|
|
|
|
/* do stuff before chain up */
|
|
|
|
parent_class->method_to_call (obj, a);
|
|
|
|
/* do stuff after chain up */
|
|
}
|
|
</programlisting>
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
</chapter>
|
|
<!-- End Howto GObject -->
|
|
|
|
<chapter id="howto-interface">
|
|
<title>How to define and implement interfaces</title>
|
|
|
|
<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:
|
|
<programlisting>
|
|
#ifndef __MAMAN_IBAZ_H__
|
|
#define __MAMAN_IBAZ_H__
|
|
|
|
#include <glib-object.h>
|
|
|
|
#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 _MamanIbazInterface MamanIbazInterface;
|
|
|
|
struct _MamanIbazInterface
|
|
{
|
|
GTypeInterface parent_iface;
|
|
|
|
void (*do_action) (MamanIbaz *self);
|
|
};
|
|
|
|
GType maman_ibaz_get_type (void);
|
|
|
|
void maman_ibaz_do_action (MamanIbaz *self);
|
|
|
|
#endif /* __MAMAN_IBAZ_H__ */
|
|
</programlisting>
|
|
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
|
|
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 is_initialized = FALSE;
|
|
|
|
if (!is_initialized)
|
|
{
|
|
/* add properties and signals to the interface here */
|
|
|
|
is_initialized = TRUE;
|
|
}
|
|
}
|
|
|
|
GType
|
|
maman_ibaz_get_type (void)
|
|
{
|
|
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)
|
|
{
|
|
g_return_if_fail (MAMAN_IS_IBAZ (self));
|
|
|
|
MAMAN_IBAZ_GET_INTERFACE (self)->do_action (self);
|
|
}
|
|
</programlisting>
|
|
</para>
|
|
</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.
|
|
</para>
|
|
|
|
<para>
|
|
The first step is to define a normal GObject class, like:
|
|
<programlisting>
|
|
#ifndef __MAMAN_BAZ_H__
|
|
#define __MAMAN_BAZ_H__
|
|
|
|
#include <glib-object.h>
|
|
|
|
#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;
|
|
|
|
struct _MamanBaz
|
|
{
|
|
GObject parent_instance;
|
|
|
|
int instance_member;
|
|
};
|
|
|
|
struct _MamanBazClass
|
|
{
|
|
GObjectClass parent_class;
|
|
};
|
|
|
|
GType maman_baz_get_type (void);
|
|
|
|
#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.
|
|
</para>
|
|
|
|
<para>
|
|
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>
|
|
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 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>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
|
|
maman_baz_do_action (MamanBaz *self)
|
|
{
|
|
g_print ("Baz implementation of IBaz interface Action: 0x%x.\n",
|
|
self->instance_member);
|
|
}
|
|
|
|
static void
|
|
maman_ibaz_interface_init (MamanIbazInterface *iface)
|
|
{
|
|
iface->do_action = baz_do_action;
|
|
}
|
|
|
|
static void
|
|
maman_baz_init (MamanBaz *self)
|
|
{
|
|
MamanBaz *self = MAMAN_BAZ (instance);
|
|
self->instance_member = 0xdeadbeaf;
|
|
}
|
|
</programlisting>
|
|
</para>
|
|
|
|
</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>
|
|
/* 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:
|
|
<programlisting>
|
|
static void
|
|
maman_ibar_do_another_action (MamanIbar *ibar)
|
|
{
|
|
MamanBar *self = MAMAN_BAR (ibar);
|
|
|
|
g_print ("Bar implementation of IBar interface Another Action: 0x%x.\n",
|
|
self->instance_member);
|
|
}
|
|
|
|
static void
|
|
maman_ibar_interface_init (MamanIbarInterface *iface)
|
|
{
|
|
iface->do_another_action = maman_ibar_do_another_action;
|
|
}
|
|
|
|
static void
|
|
maman_ibaz_do_action (MamanIbaz *ibaz)
|
|
{
|
|
MamanBar *self = MAMAN_BAR (ibaz);
|
|
|
|
g_print ("Bar implementation of IBaz interface Action: 0x%x.\n",
|
|
self->instance_member);
|
|
}
|
|
|
|
static void
|
|
maman_ibaz_interface_init (MamanIbazInterface *iface)
|
|
{
|
|
iface->do_action = maman_ibaz_do_action;
|
|
}
|
|
|
|
static void
|
|
maman_bar_class_init (MamanBarClass *klass)
|
|
{
|
|
|
|
}
|
|
|
|
static void
|
|
maman_bar_init (MamanBar *self)
|
|
{
|
|
self->instance_member = 0x666;
|
|
}
|
|
|
|
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 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>
|
|
</para>
|
|
|
|
<para>
|
|
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>
|
|
|
|
struct _MamanBaz
|
|
{
|
|
GObject parent_instance;
|
|
|
|
gint instance_member;
|
|
gchar *name;
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
|
|
PROP_NAME
|
|
};
|
|
|
|
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);
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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 -->
|
|
|
|
<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>
|
|
|
|
<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 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);
|
|
|
|
g_signal_connect (file, "changed", G_CALLBACK (changed_event), NULL);
|
|
|
|
maman_file_write (file, buffer, strlen (buffer));
|
|
</programlisting>
|
|
</para>
|
|
|
|
<para>
|
|
The <type>MamanFile</type> signal is registered in the class_init
|
|
function:
|
|
<programlisting>
|
|
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 emitted in <function>maman_file_write</function>:
|
|
<programlisting>
|
|
void
|
|
maman_file_write (MamanFile *self,
|
|
const guchar *buffer,
|
|
gssize size)
|
|
{
|
|
/* 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>
|
|
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>
|
|
|
|
<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>
|
|
|
|
<!--
|
|
this is utterly wrong and should be completely removed - or rewritten
|
|
with a better example than writing a buffer using synchronous signals.
|
|
|
|
<sect1>
|
|
<title>How to provide more flexibility to users?</title>
|
|
|
|
<para>
|
|
The previous implementation does the job but the signal facility of
|
|
GObject can be used to provide even more flexibility to this file
|
|
change notification mechanism. One of the key ideas is to make the
|
|
process of writing data to the file part of the signal emission
|
|
process to allow users to be notified either before or after the
|
|
data is written to the file.
|
|
</para>
|
|
|
|
<para>
|
|
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>):
|
|
<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 */);
|
|
</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>
|
|
|
|
<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);
|
|
}
|
|
</programlisting>
|
|
</para>
|
|
|
|
<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);
|
|
}
|
|
</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><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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
static void test_file_complex (void)
|
|
{
|
|
guint8 buffer[100];
|
|
GObject *file;
|
|
|
|
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_after (G_OBJECT (file), "write",
|
|
(GCallback)complex_write_event_after,
|
|
NULL);
|
|
|
|
maman_file_complex_write (MAMAN_FILE_COMPLEX (file), buffer, 50);
|
|
|
|
g_object_unref (G_OBJECT (file));
|
|
}
|
|
</programlisting>
|
|
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>
|
|
|
|
-->
|
|
|
|
<!--
|
|
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;
|
|
|
|
guint write_signal_id;
|
|
|
|
/* signal default handlers */
|
|
void (*write) (MamanFileSimple *self, guint8 *buffer, guint size);
|
|
};
|
|
</programlisting>
|
|
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);
|
|
|
|
klass->write = default_write_signal_handler;
|
|
</programlisting>
|
|
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);
|
|
</programlisting>
|
|
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 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>
|
|
|
|
</sect1>
|
|
-->
|
|
|
|
<!--
|
|
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>
|
|
|
|
-->
|
|
|
|
</chapter>
|
|
|
|
<!--
|
|
<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>
|
|
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
|
|
{
|
|
GTypeClass g_type_class;
|
|
|
|
/* class methods and other stuff. */
|
|
|
|
/* signals */
|
|
void (*notify) (GObject *object,
|
|
GParamSpec *pspec);
|
|
};
|
|
</programlisting>
|
|
</para>
|
|
|
|
<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)
|
|
{
|
|
|
|
/* Stuff */
|
|
|
|
class->notify = NULL;
|
|
|
|
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);
|
|
}
|
|
</programlisting>
|
|
<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>
|
|
|
|
</sect2>
|
|
-->
|
|
</part>
|