mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-03-30 12:23:06 +02:00
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:
parent
f870d5abdb
commit
1f6f7e1c4d
@ -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;
|
||||
|
||||
/*< private >*/
|
||||
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;
|
||||
|
||||
/*< private >*/
|
||||
MamanBarPrivate *priv;
|
||||
};
|
||||
</programlisting>
|
||||
<note><simpara>Do not call this <varname>private</varname>, as
|
||||
that is a registered c++ keyword.</simpara></note>
|
||||
|
||||
The private structure is then defined in the .c file, using the
|
||||
g_type_class_add_private() function to notify the presence of
|
||||
a private memory area for each instance and it can either
|
||||
be retrieved using <function>G_TYPE_INSTANCE_GET_PRIVATE()</function>
|
||||
each time is needed, or assigned to the <literal>priv</literal>
|
||||
member of the instance structure inside the object's
|
||||
<function>init</function> function.
|
||||
<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 (&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->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:
|
||||
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user