docs: Clean up the GObject tutorial a bit

Started off by using the new instance private data macro, ended up
cleaning up the obscure, out of date, or simply broken concepts and
paragraphs.

https://bugzilla.gnome.org/show_bug.cgi?id=700035
This commit is contained in:
Emmanuele Bassi 2013-06-12 15:18:29 +01:00
parent f870d5abdb
commit 1f6f7e1c4d

View File

@ -40,8 +40,14 @@
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>
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>
@ -111,6 +117,7 @@ typedef struct _MamanBarClass MamanBarClass;
struct _MamanBar
{
/* Parent instance structure */
GObject parent_instance;
/* instance members */
@ -118,6 +125,7 @@ struct _MamanBar
struct _MamanBarClass
{
/* Parent class structure */
GObjectClass parent_class;
/* class members */
@ -134,51 +142,47 @@ GType maman_bar_get_type (void);
</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.
Types that require per-instance private data should use the
G_DEFINE_TYPE_WITH_PRIVATE() macro, or use the G_ADD_PRIVATE()
macro with the G_DEFINE_TYPE_WITH_CODE() or the G_DEFINE_TYPE_EXTENDED()
macros. The private structure is then defined in the .c file,
and can be accessed using the <function>get_private()</function>
function generated by the G_DEFINE_TYPE_* macros.
<programlisting>
struct _MamanBar
struct _MamanBarPrivate
{
GObject parent_instance;
/*&lt; private &gt;*/
int hsize;
};
G_DEFINE_TYPE_WITH_PRIVATE (MamanBar, maman_bar, G_TYPE_OBJECT)
static void
maman_bar_class_init (MamanBarClass *klass)
{
}
static void
maman_bar_init (MamanBar *self)
{
/* maman_bar_get_private() is generated by G_DEFINE_TYPE_WITH_PRIVATE()
* above, and it's local to the current compilation unit.
*/
MamanBarPrivate *priv = maman_bar_get_private (self);
priv->hsize = 42;
}
</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;
/*&lt; private &gt;*/
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.
<listitem><para>
Most GNOME libraries use a pointer inside the instance structure
for simpler access to the private data structure, as described by
Herb Sutter in his Pimpl article (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>
for reference). If you opt to use this idiom, you can assign the
pointer inside the instance initialization function, e.g.:
<programlisting>
#define MAMAN_BAR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MAMAN_TYPE_BAR, MamanBarPrivate))
G_DEFINE_TYPE_WITH_PRIVATE (MamanBar, maman_bar, G_TYPE_OBJECT)
struct _MamanBarPrivate
{
@ -188,27 +192,14 @@ struct _MamanBarPrivate
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;
self->priv = maman_bar_get_private (self);
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>
@ -280,7 +271,7 @@ struct _MamanBarPrivate {
</itemizedlist>
<programlisting>
G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT);
G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT)
</programlisting>
</para>
@ -289,7 +280,9 @@ G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT);
<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.
call the <function>g_type_implement_interface</function> function,
or call the <function>G_ADD_PRIVATE</function> macro will add an
instance private data structure.
</para>
</sect1>
@ -318,29 +311,37 @@ G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT);
until all the construction properties have been set.
</para>
<para>
It is important to note that object construction cannot <emphasis>ever</emphasis>
fail. If you require a fallible GObject construction, you can use the
GInitable and GAsyncInitable interfaces provided by the GIO library
</para>
<para>
As such, I would recommend writing the following code first:
<programlisting>
G_DEFINE_TYPE_WITH_PRIVATE (MamanBar, maman_bar, G_TYPE_OBJECT)
static void
maman_bar_class_init (MamanBarClass *klass)
{
}
static void
maman_bar_init (MamanBar *self)
{
self->priv = MAMAN_BAR_GET_PRIVATE (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.
If you need special construction properties, install the properties in
the <function>class_init()</function> function, override the <function>set_property()</function>
and <function>get_property()</function> methods of the GObject class,
and implement them as described by <xref linkend="gobject-properties"/>.
<informalexample><programlisting>
enum {
PROP_0,
@ -350,6 +351,7 @@ enum {
N_PROPERTIES
};
/* Keep a pointer to the properties definition */
static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
static void
@ -365,32 +367,31 @@ bar_class_init (MamanBarClass *klass)
"Maman construct prop",
"Set maman's name",
"no-name-set" /* default value */,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
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.
If you need this, make sure you can build and run code similar to the
code shown above. Also, make sure your construct properties can be set
without side effects during construction.
</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.
Some people sometimes need to complete the initialization of a instance
of a type only after the properties passed to the constructors have been
set. This is possible through the use of the <function>constructor()</function>
class method as described in <xref linkend="gobject-instantiation"/> or,
more simply, using the <function>constructed()</function> class method
available since GLib 2.12. Note that the <function>constructed()</function>
virtual function will only be invoked after the properties marked as
G_PARAM_CONSTRUCT_ONLY or G_PARAM_CONSTRUCT have been consumed, but
before the regular properties passed to <function>g_object_new()</function>
have been set.
</para>
</sect1>
@ -407,10 +408,11 @@ bar_class_init (MamanBarClass *klass)
<para>
The destruction process of your object might be split in two different
phases: dispose and the finalize.
phases: dispose and the finalize. This split is necessary to handle
potential cycles due to the nature of the reference counting mechanism
used by GObject, as well as dealing with temporary vivification of
instances in case of signal emission during the destruction sequence.
<programlisting>
#define MAMAN_BAR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MAMAN_TYPE_BAR, MamanBarPrivate))
struct _MamanBarPrivate
{
GObject *an_object;
@ -418,31 +420,29 @@ struct _MamanBarPrivate
gchar *a_string;
};
G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT);
G_DEFINE_TYPE_WITH_PRIVATE (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
/* 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.
/* dispose() might be called multiple times, so we must guard against
* calling g_object_unref() on an invalid GObject by setting the member
* NULL; g_clear_object() does this for us, atomically.
*/
if (self->priv->an_object)
{
g_object_unref (self->priv->an_object);
g_clear_object (&amp;self->priv->an_object);
self->priv->an_object = NULL;
}
/* Chain up to the parent class */
/* Always chain up to the parent class; there is no need to check if
* the parent class implements the dispose() virtual function: it is
* always guaranteed to do so
*/
G_OBJECT_CLASS (maman_bar_parent_class)->dispose (gobject);
}
@ -453,7 +453,9 @@ maman_bar_finalize (GObject *gobject)
g_free (self->priv->a_string);
/* Chain up to the parent class */
/* Always chain up to the parent class; as with dispose(), finalize()
* is guaranteed to exist on the parent's class virtual function table
*/
G_OBJECT_CLASS (maman_bar_parent_class)->finalize (gobject);
}
@ -464,14 +466,12 @@ maman_bar_class_init (MamanBarClass *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 = maman_bar_get_private (self);
self->priv->an_object = g_object_new (MAMAN_TYPE_BAZ, NULL);
self->priv->a_string = g_strdup ("Maman");
@ -479,16 +479,11 @@ maman_bar_init (MamanBar *self);
</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.
warn the user, by having a disposed instance revert to an inhert state.
</para>
</sect1>
@ -498,7 +493,7 @@ maman_bar_init (MamanBar *self);
<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.
C++ vocabulary. (Readers are expected to know basic C++ concepts.
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.)
@ -537,8 +532,6 @@ maman_bar_do_action (MamanBar *self, /* parameters */)
}
</programlisting>
</para>
<para>There is really nothing scary about this.</para>
</sect2>
<sect2>
@ -572,28 +565,7 @@ maman_bar_do_action (MamanBar *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.
class function.
</para>
<para>
@ -601,8 +573,8 @@ maman_bar_do_action (MamanBar *self, /* parameters */)
implementation for this class method in the object's
<function>class_init</function> function: initialize the
klass-&gt;do_action field to a pointer to the actual implementation.
You can also make this class method pure virtual by initializing
the klass-&gt;do_action field to NULL:
By default, class method that are not inherited are initialized to
NULL, and thus are to be considered "pure virtual".
<programlisting>
static void
maman_bar_real_do_action_two (MamanBar *self, /* parameters */)
@ -613,7 +585,10 @@ maman_bar_real_do_action_two (MamanBar *self, /* parameters */)
static void
maman_bar_class_init (BarClass *klass)
{
/* pure virtual method: mandates implementation in children. */
/* this is not necessary, except for demonstration purposes.
*
* pure virtual method: mandates implementation in children.
*/
klass->do_action_one = NULL;
/* merely virtual method. */
@ -625,7 +600,17 @@ 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 */);
/* if the method is purely virtual, then it is a good idea to
* check that it has been overridden before calling it, and,
* depending on the intent of the class, either ignore it silently
* or warn the user.
/
if (MAMAN_BAR_GET_CLASS (self)->do_action_one != NULL)
MAMAN_BAR_GET_CLASS (self)->do_action_one (self, /* parameters */);
else
g_warning ("Class '%s' does not override the mandatory "
"MamanBarClass.do_action_one() virtual function.",
G_OBJECT_TYPE_NAME (self));
}
void
@ -728,21 +713,16 @@ maman_bar_subtype_class_init (MamanBarSubTypeClass *klass)
<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:
There are various uses to this idiom:
<itemizedlist>
<listitem><para>You need to change the behaviour of a class without modifying its code. You create
<listitem><para>You need to extend 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.
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>
@ -763,22 +743,25 @@ maman_bar_subtype_class_init (MamanBarSubTypeClass *klass)
</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:
<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. Instead of using this function
directly, though, you should use the <function>parent_class</function>
pointer created and initialized for us by the G_DEFINE_TYPE_* family of
macros, for instance:
<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);
/* call the method_to_call() virtual function on the
* parent of BClass, AClass.
*
* remember the explicit cast to AClass*
*/
A_CLASS (b_parent_class)->method_to_call (obj, a);
/* do stuff after chain up */
}
@ -963,7 +946,7 @@ 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));
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
@ -1095,7 +1078,7 @@ 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));
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:
@ -1326,18 +1309,16 @@ maman_derived_ibaz_interface_init (MamanIbazInterface *iface)
G_DEFINE_TYPE_WITH_CODE (MamanDerivedBaz, maman_derived_baz, MAMAN_TYPE_BAZ,
G_IMPLEMENT_INTERFACE (MAMAN_TYPE_IBAZ,
maman_derived_ibaz_interface_init)
maman_derived_ibaz_interface_init))
static void
maman_derived_baz_class_init (MamanDerivedBazClass *klass)
{
}
static void
maman_derived_baz_init (MamanDerivedBaz *self)
{
}
</programlisting>
</para>
@ -1357,8 +1338,8 @@ maman_derived_baz_init (MamanDerivedBaz *self)
exists)
<footnote>
<para>A Python callback can be connected to any signal on any
C-based GObject.
</para>
C-based GObject, and vice versa, assuming that the Python object
inherits from 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