mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-09-28 18:16:34 +02:00
add chain up section
This commit is contained in:
@@ -592,30 +592,6 @@ maman_bar_class_init (MamanBarClass *klass)
|
|||||||
<para>
|
<para>
|
||||||
Children can then implement the subclass with code such as:
|
Children can then implement the subclass with code such as:
|
||||||
<programlisting>
|
<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>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Finally, it is interesting to note that, just like in C++, it is possible
|
|
||||||
to make each object class method chain to its parent class method:
|
|
||||||
<programlisting>
|
|
||||||
static void
|
|
||||||
maman_bar_real_do_action_two (MamanBar *self, /* parameters */)
|
|
||||||
{
|
|
||||||
MamanBarClass *bar_class = g_type_class_peek_parent (klass);
|
|
||||||
/* chain up */
|
|
||||||
bar_class->do_action (self, /* parameters */);
|
|
||||||
|
|
||||||
/* do local stuff here. */
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
maman_bar_subtype_class_init (MamanBarSubTypeClass *klass)
|
maman_bar_subtype_class_init (MamanBarSubTypeClass *klass)
|
||||||
{
|
{
|
||||||
@@ -631,14 +607,68 @@ maman_bar_subtype_class_init (MamanBarSubTypeClass *klass)
|
|||||||
<sect2 id="howto-gobject-chainup">
|
<sect2 id="howto-gobject-chainup">
|
||||||
<title>Chaining up</title>
|
<title>Chaining up</title>
|
||||||
|
|
||||||
<p>Chaining up is commonly used to implement the Chain Of Responsability pattern in C++: each class in a
|
<para>Chaining up is often loosely defined by the folowing set of conditions:
|
||||||
given inheritance hierarchy is expected to override the same method and then call from within each method
|
<itemizedlist>
|
||||||
the overriden method of the parent. Personally, I am not sure this is a very smart idea (a detailed explanation
|
<listitem><para>Parent class A defines a public virtual method named <function>foo</function> and
|
||||||
of why I think it is a bad idea would take too much space for this document) but I can show you how to do it and
|
provides a default implementation.</para></listitem>
|
||||||
here is an example.
|
<listitem><para>Child class B re-implements method <function>foo</function>.</para></listitem>
|
||||||
</p>
|
<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>
|
||||||
|
|
||||||
<p>To invoke the parent method XXX</p>
|
<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>
|
</sect2>
|
||||||
|
|
||||||
@@ -695,7 +725,7 @@ GType maman_ibaz_get_type (void);
|
|||||||
|
|
||||||
void maman_ibaz_do_action (MamanIbaz *self);
|
void maman_ibaz_do_action (MamanIbaz *self);
|
||||||
|
|
||||||
#endif //MAMAN_IBAZ_H
|
#endif /*MAMAN_IBAZ_H*/
|
||||||
</programlisting>
|
</programlisting>
|
||||||
This code is almost exactly similar to the code for a normal <type>GType</type>
|
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:
|
which derives from a <type>GObject</type> except for a few details:
|
||||||
|
Reference in New Issue
Block a user