docs: Update GObject how-to for G_DECLARE_*_TYPE macros

Restructure the section of the how-to which covers the header and source
code boilerplate for declaring and defining GObjects to use the new
G_DECLARE_*_TYPE macros. Present both final and derivable types.

Trim various supporting paragraphs.

Rename ‘class functions’ to ‘virtual functions’ to use consistent,
modern terminology.

https://bugzilla.gnome.org/show_bug.cgi?id=744060
This commit is contained in:
Philip Withnall 2015-02-20 12:42:52 +00:00
parent b6fc1df022
commit b6b0f5f305

View File

@ -7,8 +7,8 @@
<partintro> <partintro>
<para> <para>
This chapter tries to answer the real-life questions of users and presents 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 most common use cases in order from most likely to least
The use cases are presented from most likely to less likely. likely.
</para> </para>
</partintro> </partintro>
@ -30,24 +30,12 @@
The first step before writing the code for your GObject is to write the 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 type's header which contains the needed type, function and macro
definitions. Each of these elements is nothing but a convention which 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. is followed by almost all users of GObject, and has been refined over
If you feel the need not to obey the rules stated below, think about it multiple years of experience developing GObject-based code. If you are
twice: writing a library, it is particularly important for you to adhere closely
<itemizedlist> to these conventions; users of your library will assume that you have.
<listitem><para>If your users are a bit accustomed to GTK+ code or any Even if not writing a library, it will help other people who want to work
GLib code, they will be a bit surprised and getting used to the on your project.
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>
It is, nevertheless, important to note that these rules generally apply
to code that is meant to be called by third parties; it is perfectly
possible to write a valid, self-contained GObject types without most of
the boilerplate used in this tutorial; most of the boilerplate is also
not strictly required if you plan to use the GObject types only through
language bindings based on introspection.
</para> </para>
<para> <para>
@ -67,27 +55,28 @@
names easier for those with poor eyesight. names easier for those with poor eyesight.
</para> </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> <para>
The basic conventions for any header which exposes a GType are described The basic conventions for any header which exposes a GType are described
in <xref linkend="gtype-conventions"/>. in <xref linkend="gtype-conventions"/>.
</para> </para>
<para> <para>
If you want to declare a type named bar with prefix maman, name the type instance If you want to declare a type named bar in namespace maman, name the
<function>MamanBar</function> and its class <function>MamanBarClass</function> type instance <function>MamanBar</function> and its class
(name is case-sensitive). It is customary to declare them with code similar to the <function>MamanBarClass</function> (names are case sensitive). The
following: recommended method of declaring a type differs based on whether the type
is final or derivable.
</para>
<para>
Final types cannot be subclassed further, and should be the default choice
for new types — changing a final type to be derivable is always a change
that will be compatible with existing uses of the code, but the converse
will often cause problems. Final types are declared using
<link linkend="G-DECLARE-FINAL-TYPE:CAPS"><function>G_DECLARE_FINAL_TYPE</function></link>,
and require a structure to hold the instance data to be declared in the
source code (not the header file).
<informalexample><programlisting> <informalexample><programlisting>
/* /*
* Copyright/Licensing information. * Copyright/Licensing information.
@ -102,77 +91,92 @@
* Potentially, include other headers on which this header depends. * Potentially, include other headers on which this header depends.
*/ */
G_BEGIN_DECLS
/* /*
* Type macros. * Type declaration.
*/ */
#define MAMAN_TYPE_BAR (maman_bar_get_type ()) #define MAMAN_TYPE_BAR maman_bar_get_type ()
#define MAMAN_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_TYPE_BAR, MamanBar)) G_DECLARE_FINAL_TYPE (MamanBar, maman_bar, MAMAN, BAR, GObject)
#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
{
/* Parent instance structure */
GObject parent_instance;
/* instance members */
};
struct _MamanBarClass
{
/* Parent class structure */
GObjectClass parent_class;
/* class members */
};
/* used by MAMAN_TYPE_BAR */
GType maman_bar_get_type (void);
/* /*
* Method definitions. * Method definitions.
*/ */
MamanBar *maman_bar_new (void);
G_END_DECLS
#endif /* __MAMAN_BAR_H__ */ #endif /* __MAMAN_BAR_H__ */
</programlisting></informalexample> </programlisting></informalexample>
</para> </para>
<para> <para>
Finally, there are different header include conventions. Again, pick one Derivable types <emphasis>can</emphasis> be subclassed further, and their class and
and stick to it. I personally use indifferently any of the two, depending instance structures form part of the public API which must not be changed
on the codebase I work on: the rule, as always, is consistency. if API stability is cared about. They are declared using
<itemizedlist> <link linkend="G-DECLARE-DERIVABLE-TYPE:CAPS"><function>G_DECLARE_DERIVABLE_TYPE</function></link>:
<listitem><para> <informalexample><programlisting>
Some people add at the top of their headers a number of #include /*
directives to pull in all the headers needed to compile client * Copyright/Licensing information.
code. This allows client code to simply #include "maman-bar.h". */
</para></listitem>
<listitem><para> /* inclusion guard */
Other do not #include anything and expect the client to #include #ifndef __MAMAN_BAR_H__
themselves the headers they need before including your header. This #define __MAMAN_BAR_H__
speeds up compilation because it minimizes the amount of
pre-processor work. This can be used in conjunction with the #include &lt;glib-object.h&gt;
re-declaration of certain unused types in the client code to /*
minimize compile-time dependencies and thus speed up compilation. * Potentially, include other headers on which this header depends.
</para></listitem> */
</itemizedlist>
G_BEGIN_DECLS
/*
* Type declaration.
*/
#define MAMAN_TYPE_BAR maman_bar_get_type ()
G_DECLARE_DERIVABLE_TYPE (MamanBar, maman_bar, MAMAN, BAR, GObject)
struct _MamanBarClass
{
GObjectClass parent_class;
/* Class virtual function fields. */
void (* handle_frob) (MamanBar *bar,
guint n_frobs);
/* Padding to allow adding up to 12 new virtual functions without
* breaking ABI. */
gpointer padding[12];
};
/*
* Method definitions.
*/
MamanBar *maman_bar_new (void);
G_END_DECLS
#endif /* __MAMAN_BAR_H__ */
</programlisting></informalexample>
</para>
<para>
The convention for header includes is to add the minimum number of
<function>#include</function> directives to the top of your headers needed
to compile that header. This
allows client code to simply <function>#include "maman-bar.h"</function>,
without needing to know the prerequisites for
<filename>maman-bar.h</filename>.
</para> </para>
</sect1> </sect1>
<sect1 id="howto-gobject-code"> <sect1 id="howto-gobject-code">
<title>Boilerplate code</title> <title>Boilerplate code</title>
<para> <para>
In your code, the first step is to #include the needed headers: depending In your code, the first step is to <function>#include</function> the
on your header include strategy, this can be as simple as needed headers:
<literal>#include "maman-bar.h"</literal> or as complicated as tens
of #include lines ending with <literal>#include "maman-bar.h"</literal>:
<informalexample><programlisting> <informalexample><programlisting>
/* /*
* Copyright information * Copyright information
@ -180,15 +184,11 @@ GType maman_bar_get_type (void);
#include "maman-bar.h" #include "maman-bar.h"
/* If you use Pimpls, include the private structure /* Private structure definition. */
* definition here. Some people create a maman-bar-private.h header typedef struct {
* which is included by the maman-bar.c file and which contains the gint member1;
* definition for this private structure.
*/
struct _MamanBarPrivate {
int member_1;
/* stuff */ /* stuff */
}; } MamanBarPrivate;
/* /*
* forward definitions * forward definitions
@ -197,7 +197,24 @@ struct _MamanBarPrivate {
</para> </para>
<para> <para>
Call the <function>G_DEFINE_TYPE</function> macro using the name If the class is being declared as final using
<function>G_DECLARE_FINAL_TYPE</function>, its instance structure should
be defined in the C file:
<informalexample><programlisting>
struct _MamanBar
{
GObject parent_instance;
/* Other members, including private data. */
}
</programlisting></informalexample>
</para>
<para>
Call the <function>G_DEFINE_TYPE</function> macro (or
<function>G_DEFINE_TYPE_WITH_PRIVATE</function> if your class needs
private data — final types do <emphasis>not</emphasis> need private data)
using the name
of the type, the prefix of the functions and the parent GType to of the type, the prefix of the functions and the parent GType to
reduce the amount of boilerplate needed. This macro will: reduce the amount of boilerplate needed. This macro will:
@ -206,21 +223,40 @@ struct _MamanBarPrivate {
function</simpara></listitem> function</simpara></listitem>
<listitem><simpara>define a parent class pointer accessible from <listitem><simpara>define a parent class pointer accessible from
the whole .c file</simpara></listitem> the whole .c file</simpara></listitem>
<listitem><simpara>add private instance data to the type (if using
<function>G_DEFINE_TYPE_WITH_PRIVATE</function>)</simpara></listitem>
</itemizedlist> </itemizedlist>
</para>
<para>
If the class has been declared as final using
<function>G_DECLARE_FINAL_TYPE</function> (see
<xref linkend="howto-gobject-header"/>), private data should be placed in
the instance structure, <type>MamanBar</type>, and
<function>G_DEFINE_TYPE</function> should be used instead of
<function>G_DEFINE_TYPE_WITH_PRIVATE</function>. The instance structure
for a final class is not exposed publicly, and is not embedded in the
instance structures of any derived classes (because the class is final);
so its size can vary without causing incompatibilities for code which uses
the class. Conversely, private data for derivable classes
<emphasis>must</emphasis> be included in a private structure, and
<function>G_DEFINE_TYPE_WITH_PRIVATE</function> must be used.
<informalexample><programlisting> <informalexample><programlisting>
G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT) G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT)
</programlisting></informalexample>
or
<informalexample><programlisting>
G_DEFINE_TYPE_WITH_PRIVATE (MamanBar, maman_bar, G_TYPE_OBJECT)
</programlisting></informalexample> </programlisting></informalexample>
</para> </para>
<para> <para>
It is also possible to use the It is also possible to use the
<function>G_DEFINE_TYPE_WITH_CODE</function> macro to control the <function>G_DEFINE_TYPE_WITH_CODE</function> macro to control the
get_type function implementation - for instance, to add a call to <function>get_type</function> function implementation — for instance, to
<function>G_IMPLEMENT_INTERFACE</function> macro which will add a call to the <function>G_IMPLEMENT_INTERFACE</function> macro to
call the <function>g_type_implement_interface</function> function, implement an interface.
or call the <function>G_ADD_PRIVATE</function> macro will add an
instance private data structure.
</para> </para>
</sect1> </sect1>
@ -477,11 +513,34 @@ maman_bar_do_action (MamanBar *self, /* parameters */)
<title>Virtual public methods</title> <title>Virtual public methods</title>
<para> <para>
This is the preferred way to create polymorphic GObjects. All you This is the preferred way to create GObjects with overridable methods:
need to do is to define the common method and its class function in <itemizedlist>
the public header, implement the common method in the source file <listitem><para>
and re-implement the class function in each object which inherits Define the common method and its virtual function in the
from you. class structure in the public header
</para></listitem>
<listitem><para>
Define the common method in the header file and implement it in the
source file
</para></listitem>
<listitem><para>
Implement a base version of the virtual function in the source
file and initialize the virtual function pointer to this
implementation in the objects <function>class_init</function>
function; or leave it as <constant>NULL</constant> for a pure
virtual method which must be overridden by derived classes
</para></listitem>
<listitem><para>
Re-implement the virtual function in each derived class which needs
to override it
</para></listitem>
</itemizedlist>
</para>
<para>
Note that virtual functions can only be defined if the class is
derivable, declared using
<link linkend="G-DECLARE-DERIVABLE-TYPE:CAPS"><function>G_DECLARE_DERIVABLE_TYPE</function></link>
so the class structure can be defined.
<informalexample><programlisting> <informalexample><programlisting>
/* declaration in maman-bar.h. */ /* declaration in maman-bar.h. */
struct _MamanBarClass struct _MamanBarClass
@ -503,8 +562,8 @@ maman_bar_do_action (MamanBar *self, /* parameters */)
MAMAN_BAR_GET_CLASS (self)->do_action (self, /* parameters */); MAMAN_BAR_GET_CLASS (self)->do_action (self, /* parameters */);
} }
</programlisting></informalexample> </programlisting></informalexample>
The code above simply redirects the do_action call to the relevant The code above simply redirects the <function>do_action</function> call
class function. to the relevant virtual function.
</para> </para>
<para> <para>
@ -569,7 +628,7 @@ maman_bar_do_action_two (MamanBar *self, /* parameters */)
<para> <para>
These are very similar to Virtual Public methods. They just don't These are very similar to Virtual Public methods. They just don't
have a public function to call the function directly. The header have a public function to call the function directly. The header
file contains only a declaration of the class function: file contains only a declaration of the virtual function:
<informalexample><programlisting> <informalexample><programlisting>
/* declaration in maman-bar.h. */ /* declaration in maman-bar.h. */
struct _MamanBarClass struct _MamanBarClass
@ -582,7 +641,7 @@ struct _MamanBarClass
void maman_bar_do_any_action (MamanBar *self, /* parameters */); void maman_bar_do_any_action (MamanBar *self, /* parameters */);
</programlisting></informalexample> </programlisting></informalexample>
These class functions are often used to delegate part of the job These virtual functions are often used to delegate part of the job
to child classes: to child classes:
<informalexample><programlisting> <informalexample><programlisting>
/* this accessor function is static: it is not exported outside of this file. */ /* this accessor function is static: it is not exported outside of this file. */
@ -611,7 +670,7 @@ maman_bar_do_any_action (MamanBar *self, /* parameters */)
<para> <para>
Again, it is possible to provide a default implementation for this Again, it is possible to provide a default implementation for this
private virtual class function: private virtual function:
<informalexample><programlisting> <informalexample><programlisting>
static void static void
maman_bar_class_init (MamanBarClass *klass) maman_bar_class_init (MamanBarClass *klass)
@ -633,7 +692,7 @@ maman_bar_subtype_class_init (MamanBarSubTypeClass *klass)
{ {
MamanBarClass *bar_class = MAMAN_BAR_CLASS (klass); MamanBarClass *bar_class = MAMAN_BAR_CLASS (klass);
/* implement pure virtual class function. */ /* implement pure virtual function. */
bar_class->do_specific_action_one = maman_bar_subtype_do_specific_action_one; bar_class->do_specific_action_one = maman_bar_subtype_do_specific_action_one;
} }
</programlisting></informalexample> </programlisting></informalexample>
@ -667,7 +726,7 @@ maman_bar_subtype_class_init (MamanBarSubTypeClass *klass)
<para> <para>
To explicitly chain up to the implementation of the virtual method in the parent class, 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 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. access the original virtual function pointer and invoke it directly.
<footnote> <footnote>
<para> <para>
The <emphasis>original</emphasis> adjective used in this sentence is not innocuous. To fully The <emphasis>original</emphasis> adjective used in this sentence is not innocuous. To fully
@ -1662,7 +1721,7 @@ klass->write_signal_id =
</para> </para>
<para> <para>
If the signal's default handler is just a class function pointer, it is also possible to override If the signal's default handler is just a virtual 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 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. 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 Of course, it is also possible (and recommended) to chain up from the child to the parent's default signal
@ -1693,9 +1752,9 @@ klass->write_signal_id =
<para> <para>
Usually, the <function><link linkend="g-signal-new">g_signal_new</link></function> function is preferred over 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> <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, is used, the default closure is exported as a virtual function. For example,
<filename>gobject.h</filename> contains the declaration of <link linkend="GObjectClass"><type>GObjectClass</type></link> <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> whose notify virtual function is the default handler for the <emphasis>notify</emphasis>
signal: signal:
<informalexample><programlisting> <informalexample><programlisting>
struct _GObjectClass struct _GObjectClass
@ -1713,7 +1772,7 @@ struct _GObjectClass
<para> <para>
<filename>gobject.c</filename>'s <function><link linkend="g-object-do-class-init">g_object_do_class_init</link></function> function <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 registers the <emphasis>notify</emphasis> signal and initializes this virtual function
to NULL: to NULL:
<informalexample><programlisting> <informalexample><programlisting>
static void static void
@ -1736,24 +1795,24 @@ g_object_do_class_init (GObjectClass *class)
} }
</programlisting></informalexample> </programlisting></informalexample>
<function><link linkend="g-signal-new">g_signal_new</link></function> creates a <link linkend="GClosure"><type>GClosure</type></link> which dereferences the <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 type's class structure to access the virtual function pointer and invoke it if it not NULL. The
class function is ignored it is set to NULL. virtual function is ignored it is set to NULL.
</para> </para>
<para> <para>
To understand the reason for such a complex scheme to access the signal's default handler, 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 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 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 one of the virtual 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 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 than just dereferencing a function pointer in a class structure, you must start the whole
process of signal emission which is a bit heavyweight. process of signal emission which is a bit heavyweight.
</para> </para>
<para> <para>
This is why some people decided to use class functions for some signal's default handlers: This is why some people decided to use virtual functions for some signal's default handlers:
rather than having users connect a handler to the signal and stop the signal emission 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 from within that handler, you just need to override the default virtual function which is
supposedly more efficient. supposedly more efficient.
</para> </para>