mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-26 15:36:14 +01:00
1741 lines
66 KiB
XML
1741 lines
66 KiB
XML
<chapter id="howto">
|
|
<title>How To ?</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.
|
|
</para>
|
|
|
|
<!--
|
|
Howto GObject
|
|
-->
|
|
|
|
<sect1 id="howto-gobject">
|
|
<title>How To define and implement a new GObject ?</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:
|
|
<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>
|
|
</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>
|
|
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:
|
|
<programlisting>
|
|
/*
|
|
* Copyright/Licensing information.
|
|
*/
|
|
|
|
#ifndef MAMAN_BAR_H
|
|
#define MAMAN_BAR_H
|
|
|
|
/*
|
|
* Potentially, include other headers on which this header depends.
|
|
*/
|
|
|
|
|
|
/*
|
|
* Type macros.
|
|
*/
|
|
|
|
typedef struct _MamanBar MamanBar;
|
|
typedef struct _MamanBarClass MamanBarClass;
|
|
|
|
struct _MamanBar {
|
|
GObject parent;
|
|
/* instance members */
|
|
};
|
|
|
|
struct _MamanBarClass {
|
|
GObjectClass parent;
|
|
/* class members */
|
|
};
|
|
|
|
/* used by MAMAN_BAR_TYPE */
|
|
GType maman_bar_get_type (void);
|
|
|
|
/*
|
|
* Method definitions.
|
|
*/
|
|
|
|
#endif
|
|
</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;
|
|
|
|
/* 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;
|
|
|
|
/* private */
|
|
MamanBarPrivate *priv;
|
|
};
|
|
</programlisting>
|
|
<note>Do not call this <code>private</code>, as that is a registered c++ keyword.</note>
|
|
The private structure is then defined in the .c file, instantiated in the object's
|
|
<function>init</function> function and destroyed in the object's <function>finalize</function> function.
|
|
<programlisting>
|
|
static void maman_bar_finalize(GObject *object) {
|
|
MamanBar *self = MAMAN_BAR (object);
|
|
...
|
|
g_free (self->priv);
|
|
}
|
|
|
|
static void maman_bar_init(GTypeInstance *instance, gpointer g_class) {
|
|
MamanBar *self = MAMAN_BAR (instance);
|
|
self->priv = g_new0(MamanBarPrivate,1);
|
|
...
|
|
}
|
|
</programlisting>
|
|
</para></listitem>
|
|
|
|
<listitem><para>
|
|
A similar alternative, available since Glib version 2.4, is to define a private structure in the .c file,
|
|
declare it as a private structure in <function>class_init</function> using
|
|
<function>g_type_class_add_private</function> and declare a macro to allow convenient access to this structure.
|
|
A private structure will then be attached to each newly created object by the GObject system.
|
|
You dont need to free or allocate the private structure, only the objects or pointers that it may contain.
|
|
<programlisting>
|
|
typedef struct _MamanBarPrivate MamanBarPrivate;
|
|
|
|
struct _MamanBarPrivate {
|
|
int private_field;
|
|
};
|
|
|
|
#define MAMAN_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MAMAN_BAR_TYPE, MamanBarPrivate))
|
|
|
|
static void
|
|
maman_bar_class_init (MamanBarClass *klass)
|
|
{
|
|
...
|
|
g_type_class_add_private (klass, sizeof (MamanBarPrivate));
|
|
...
|
|
}
|
|
|
|
static int
|
|
maman_bar_get_private_field (MamanBar *self)
|
|
{
|
|
MamanBarPrivate *priv = MAMAN_BAR_GET_PRIVATE (self);
|
|
|
|
return priv->private_field;
|
|
}
|
|
</programlisting>
|
|
</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 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>
|
|
|
|
<sect2 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 #include "maman-bar.h" or as complicated as tens of #include lines ending with
|
|
#include "maman-bar.h":
|
|
<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>
|
|
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;
|
|
}
|
|
</programlisting>
|
|
</para>
|
|
</sect2>
|
|
|
|
<sect2 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 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>
|
|
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>
|
|
As such, I would recommend writing the following code first:
|
|
<programlisting>
|
|
static void
|
|
maman_bar_init (GTypeInstance *instance,
|
|
gpointer g_class)
|
|
{
|
|
MamanBar *self = (MamanBar *)instance;
|
|
self->private = g_new0 (MamanBarPrivate, 1);
|
|
|
|
/* 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.
|
|
*/
|
|
}
|
|
</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>
|
|
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
|
|
<type>GParamSpec</type> 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;
|
|
|
|
gobject_class->set_property = bar_set_property;
|
|
gobject_class->get_property = bar_get_property;
|
|
|
|
maman_param_spec = 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_property (gobject_class,
|
|
PROP_MAMAN,
|
|
maman_param_spec);
|
|
}
|
|
</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>
|
|
|
|
<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>
|
|
|
|
<sect2 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>
|
|
The destruction process of your object must be split is two different phases: you must override
|
|
both the dispose and the finalize class methods.
|
|
<programlisting>
|
|
struct _MamanBarPrivate {
|
|
gboolean dispose_has_run;
|
|
};
|
|
|
|
static GObjectClass parent_class = NULL;
|
|
|
|
static void
|
|
bar_dispose (GObject *obj)
|
|
{
|
|
MamanBar *self = (MamanBar *)obj;
|
|
|
|
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;
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
/* Chain up to the parent class */
|
|
G_OBJECT_CLASS (parent_class)->dispose (obj);
|
|
}
|
|
|
|
static void
|
|
bar_finalize (GObject *obj)
|
|
{
|
|
MamanBar *self = (MamanBar *)obj;
|
|
|
|
/*
|
|
* Here, complete object destruction.
|
|
* You might not need to do much...
|
|
*/
|
|
g_free (self->private);
|
|
|
|
/* Chain up to the parent class */
|
|
G_OBJECT_CLASS (parent_class)->finalize (obj);
|
|
}
|
|
|
|
static void
|
|
bar_class_init (BarClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
gobject_class->dispose = bar_dispose;
|
|
gobject_class->finalize = bar_finalize;
|
|
}
|
|
|
|
static void
|
|
maman_bar_init (GTypeInstance *instance,
|
|
gpointer g_class)
|
|
{
|
|
MamanBar *self = (MamanBar *)instance;
|
|
self->private = g_new0 (MamanBarPrivate, 1);
|
|
self->private->dispose_has_run = FALSE;
|
|
|
|
parent_class = g_type_class_peek_parent (klass);
|
|
}
|
|
</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.
|
|
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;
|
|
}
|
|
</programlisting>
|
|
</para>
|
|
</sect2>
|
|
|
|
<sect2 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>http://www.cplusplus.com/doc/tutorial/</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>
|
|
|
|
<sect3>
|
|
<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 */)
|
|
{
|
|
/* do stuff here. */
|
|
}
|
|
</programlisting>
|
|
</para>
|
|
|
|
<para>There is really nothing scary about this.</para>
|
|
</sect3>
|
|
|
|
<sect3>
|
|
<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;
|
|
|
|
/* 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 */)
|
|
{
|
|
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:
|
|
<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 */)
|
|
{
|
|
MAMAN_BAR_GET_CLASS (self)->do_action_one (self, /* parameters */);
|
|
}
|
|
void maman_bar_do_action_two (MamanBar *self, /* parameters */)
|
|
{
|
|
MAMAN_BAR_GET_CLASS (self)->do_action_two (self, /* parameters */);
|
|
}
|
|
</programlisting>
|
|
</para>
|
|
</sect3>
|
|
|
|
<sect3>
|
|
<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>
|
|
</sect3>
|
|
</sect2>
|
|
|
|
<sect2 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 modifed, 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 Responsability pattern: each object of the inheritance
|
|
tree chains up to its parent (typically, at the begining 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 attemps to explain how to implement it.
|
|
</para>
|
|
|
|
<para>To explicitely chain up to the implementation of the virtual method in the parent class,
|
|
you first need a handle to the original parent class structure. This pointer can then be used to
|
|
access the original class function pointer and invoke it directly.
|
|
<footnote>
|
|
<para>The <emphasis>original</emphasis> adjective used in this sentence is not innocuous. To fully
|
|
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>g_type_class_peek_parent</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>
|
|
A lot of people who use this idiom in GTK+ store the parent class structure pointer in a global static
|
|
variable to avoid the costly call to <function>g_type_class_peek_parent</function> for each function call.
|
|
Typically, the class_init callback initializes the global static variable. <filename>gtk/gtkhscale.c</filename>
|
|
does this.
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
</sect1>
|
|
|
|
<!--
|
|
End Howto GObject
|
|
-->
|
|
|
|
|
|
<!--
|
|
Howto Interfaces
|
|
-->
|
|
|
|
<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>
|
|
|
|
<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_IBAZ_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), MAMAN_TYPE_IBAZ, MamanIbazClass))
|
|
#define MAMAN_IS_IBAZ(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_IBAZ))
|
|
#define MAMAN_IS_IBAZ_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), MAMAN_TYPE_IBAZ))
|
|
#define MAMAN_IBAZ_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), MAMAN_TYPE_IBAZ, MamanIbazClass))
|
|
|
|
|
|
typedef struct _MamanIbaz MamanIbaz; /* dummy object */
|
|
typedef struct _MamanIbazClass MamanIbazClass;
|
|
|
|
struct _MamanIbazClass {
|
|
GTypeInterface parent;
|
|
|
|
void (*do_action) (MamanIbaz *self);
|
|
};
|
|
|
|
GType maman_ibaz_get_type (void);
|
|
|
|
void maman_ibaz_do_action (MamanIbaz *self);
|
|
|
|
#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>
|
|
|
|
<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>
|
|
<programlisting>
|
|
static void
|
|
maman_ibaz_base_init (gpointer g_class)
|
|
{
|
|
static gboolean initialized = FALSE;
|
|
|
|
if (!initialized) {
|
|
/* create interface signals here. */
|
|
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;
|
|
}
|
|
|
|
void maman_ibaz_do_action (MamanIbaz *self)
|
|
{
|
|
MAMAN_IBAZ_GET_CLASS (self)->do_action (self);
|
|
}
|
|
</programlisting>
|
|
</para>
|
|
</sect2>
|
|
|
|
<sect2 id="howto-interface-implement">
|
|
<title>How To define and implement an implementation of 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>.
|
|
</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>:
|
|
<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_BAZ_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), MAMAN_TYPE_BAZ, MamanbazClass))
|
|
#define MAMAN_IS_BAZ(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_BAZ))
|
|
#define MAMAN_IS_BAZ_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), MAMAN_TYPE_BAZ))
|
|
#define MAMAN_BAZ_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), MAMAN_TYPE_BAZ, MamanbazClass))
|
|
|
|
|
|
typedef struct _MamanBaz MamanBaz;
|
|
typedef struct _MamanBazClass MamanBazClass;
|
|
|
|
struct _MamanBaz {
|
|
GObject parent;
|
|
int instance_member;
|
|
};
|
|
|
|
struct _MamanBazClass {
|
|
GObjectClass parent;
|
|
};
|
|
|
|
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 <function>maman_baz_get_type</function>:
|
|
<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_TYPE_IBAZ,
|
|
&ibaz_info);
|
|
}
|
|
return type;
|
|
}
|
|
</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_TYPE_IBAZ</function>.
|
|
</para>
|
|
|
|
<para>
|
|
<function>baz_interface_init</function>, the interface initialization function, is also pretty simple:
|
|
<programlisting>
|
|
static void baz_do_action (MamanBaz *self)
|
|
{
|
|
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)
|
|
{
|
|
MamanIbazClass *klass = (MamanIbazClass *)g_iface;
|
|
klass->do_action = (void (*) (MamanIbaz *self))baz_do_action;
|
|
}
|
|
static void
|
|
baz_instance_init (GTypeInstance *instance,
|
|
gpointer g_class)
|
|
{
|
|
MamanBaz *self = (MamanBaz *)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:
|
|
|
|
<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_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 ibar_do_another_action (MamanBar *self)
|
|
{
|
|
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)
|
|
{
|
|
MamanIbarClass *klass = (MamanIbarClass *)g_iface;
|
|
klass->do_another_action = (void (*) (MamanIbar *self))ibar_do_another_action;
|
|
}
|
|
|
|
|
|
static void ibaz_do_action (MamanBar *self)
|
|
{
|
|
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)
|
|
{
|
|
MamanIbazClass *klass = (MamanIbazClass *)g_iface;
|
|
klass->do_action = (void (*) (MamanIbaz *self))ibaz_do_action;
|
|
}
|
|
|
|
|
|
static void
|
|
bar_instance_init (GTypeInstance *instance,
|
|
gpointer g_class)
|
|
{
|
|
MamanBar *self = (MamanBar *)instance;
|
|
self->instance_member = 0x666;
|
|
}
|
|
|
|
|
|
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 */
|
|
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_TYPE_IBAZ,
|
|
&ibaz_info);
|
|
g_type_add_interface_static (type,
|
|
MAMAN_TYPE_IBAR,
|
|
&ibar_info);
|
|
}
|
|
return type;
|
|
}
|
|
</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>
|
|
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>.
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
<sect2 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>g_object_interface_install_property</function> is used to
|
|
declare the properties instead of <function>g_object_class_install_property</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 gobject_install_property 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_class)
|
|
{
|
|
static gboolean initialized = FALSE;
|
|
|
|
if (!initialized) {
|
|
/* create interface signals here. */
|
|
|
|
g_object_interface_install_property (g_class,
|
|
g_param_spec_string ("name",
|
|
"maman_ibaz_name",
|
|
"Name of the MamanIbaz",
|
|
"maman",
|
|
G_PARAM_READWRITE));
|
|
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 properities are utilized only
|
|
inside the get and set methods and since interfaces do not implement properties,
|
|
there is no need to assign integer IDs to interface properties.
|
|
</para>
|
|
|
|
<para>The story for the implementers of the interface is also quite trivial.
|
|
An implementer shall declare and define it's properties in the usual way as
|
|
explained in <xref linkend="gobject-properties"/>, except for one small
|
|
change: it shall declare the properties of the interface it implements using
|
|
<function>g_object_class_override_property</function> instead of
|
|
<function>g_object_class_install_property</function>. The following code snipet
|
|
shows the modifications needed in the <type>MamanBaz</type> declaration and
|
|
implementation above:
|
|
<programlisting>
|
|
|
|
struct _MamanBaz {
|
|
GObject parent;
|
|
gint instance_member;
|
|
gchar *name; /* placeholder for property */
|
|
};
|
|
|
|
enum
|
|
{
|
|
ARG_0,
|
|
ARG_NAME
|
|
};
|
|
|
|
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 */
|
|
baz_class_init, /* 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_TYPE_IBAZ,
|
|
&ibaz_info);
|
|
}
|
|
return type;
|
|
}
|
|
|
|
static void
|
|
maman_baz_class_init (MamanBazClass * klass)
|
|
{
|
|
GObjectClass *gobject_class;
|
|
|
|
gobject_class = (GObjectClass *) klass;
|
|
|
|
parent_class = g_type_class_ref (G_TYPE_OBJECT);
|
|
|
|
gobject_class->set_property = maman_baz_set_property;
|
|
gobject_class->get_property = maman_baz_get_property;
|
|
|
|
g_object_class_override_property (gobject_class, ARG_NAME, "name");
|
|
}
|
|
|
|
static void
|
|
maman_baz_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
MamanBaz *baz;
|
|
GObject *obj;
|
|
|
|
/* it's not null if we got it, but it might not be ours */
|
|
g_return_if_fail (G_IS_MAMAN_BAZ (object));
|
|
|
|
baz = MAMAN_BAZ (object);
|
|
|
|
switch (prop_id) {
|
|
case ARG_NAME:
|
|
baz->name = g_value_get_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;
|
|
|
|
/* it's not null if we got it, but it might not be ours */
|
|
g_return_if_fail (G_IS_TEXT_PLUGIN (object));
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
</programlisting>
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
|
|
</sect1>
|
|
|
|
<!--
|
|
End Howto Interfaces
|
|
-->
|
|
|
|
|
|
<!--
|
|
start Howto Signals
|
|
-->
|
|
|
|
|
|
<sect1 id="howto-signals">
|
|
<title>Howto 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 events which can be received by numerous clients.
|
|
</para>
|
|
|
|
<sect2 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>
|
|
<programlisting>
|
|
file = g_object_new (MAMAN_FILE_TYPE, NULL);
|
|
|
|
g_signal_connect (G_OBJECT (file), "write",
|
|
(GCallback)write_event,
|
|
NULL);
|
|
|
|
maman_file_write (file, buffer, 50);
|
|
</programlisting>
|
|
</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 */);
|
|
</programlisting>
|
|
and the signal is emited in <function>maman_file_write</function>:
|
|
<programlisting>
|
|
void maman_file_write (MamanFile *self, guint8 *buffer, guint32 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);
|
|
}
|
|
</programlisting>
|
|
As shown above, you can safely set the details parameter to zero if you do not know what it can be used for.
|
|
For a discussion of what you could used it for, see <xref linkend="signal-detail"/>
|
|
</para>
|
|
|
|
<para>
|
|
The signature of the signal handler in the above example is defined as
|
|
<function>g_cclosure_marshal_VOID__VOID</function>. Its name follows
|
|
a simple convention which encodes the function parameter and return value
|
|
types in the function name. Specifically, the value infront of the double
|
|
underscore is the type of the return value, while the value(s) after the
|
|
double underscore denote the parameter types.
|
|
The header <filename>gobject/gmarshal.h</filename> defines a set of commonly
|
|
needed closures that one can use.
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
|
|
<sect2>
|
|
<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 genmarshall tool. We thus create a file named <filename>marshall.list</filename> which contains
|
|
the following single line:
|
|
<programlisting>
|
|
VOID:POINTER,UINT
|
|
</programlisting>
|
|
and use the Makefile provided in <filename>sample/signal/Makefile</filename> to generate the file named
|
|
<filename>maman-file-complex-marshall.c</filename>. This C file is finally included in
|
|
<filename>maman-file-complex.c</filename>.
|
|
</para>
|
|
|
|
<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>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>
|
|
<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>
|
|
|
|
|
|
<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.
|
|
<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 initialied 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>g_signal_new</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>G_STRUCT_OFFSET</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>
|
|
|
|
<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>
|
|
|
|
|
|
</sect2>
|
|
|
|
</sect1>
|
|
|
|
<!--
|
|
<sect3>
|
|
<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>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:
|
|
<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>g_object_do_class_init</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>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>
|
|
-->
|
|
|
|
|
|
<!--
|
|
<sect1 id="howto-doc">
|
|
<title>How to generate API documentation for your type ?</title>
|
|
|
|
</sect1>
|
|
-->
|
|
|
|
</chapter>
|
|
|
|
|
|
|
|
|
|
|