mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-09-28 01:57:14 +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:
|
||||||
@@ -1034,20 +1064,20 @@ uses this method. The code below shows how the user can connect a callback to th
|
|||||||
for this simple example is located in <filename>sample/signal/maman-file.{h|c}</filename> and
|
for this simple example is located in <filename>sample/signal/maman-file.{h|c}</filename> and
|
||||||
in <filename>sample/signal/test.c</filename>
|
in <filename>sample/signal/test.c</filename>
|
||||||
<programlisting>
|
<programlisting>
|
||||||
file = g_object_new (MAMAN_FILE_TYPE, NULL);
|
file = g_object_new (MAMAN_FILE_TYPE, NULL);
|
||||||
|
|
||||||
g_signal_connect (G_OBJECT (file), "write",
|
g_signal_connect (G_OBJECT (file), "write",
|
||||||
(GCallback)write_event,
|
(GCallback)write_event,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
maman_file_write (file, buffer, 50);
|
maman_file_write (file, buffer, 50);
|
||||||
</programlisting>
|
</programlisting>
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
The <type>MamanFile</type> signal is registered in the class_init function:
|
The <type>MamanFile</type> signal is registered in the class_init function:
|
||||||
<programlisting>
|
<programlisting>
|
||||||
klass->write_signal_id =
|
klass->write_signal_id =
|
||||||
g_signal_newv ("write",
|
g_signal_newv ("write",
|
||||||
G_TYPE_FROM_CLASS (g_class),
|
G_TYPE_FROM_CLASS (g_class),
|
||||||
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
|
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
|
||||||
@@ -1110,16 +1140,16 @@ and use the Makefile provided in <filename>sample/signal/Makefile</filename> to
|
|||||||
of the object <type>MamanFileComplex</type> (full source for this object is included in
|
of the object <type>MamanFileComplex</type> (full source for this object is included in
|
||||||
<filename>sample/signal/maman-file-complex.{h|c}</filename>):
|
<filename>sample/signal/maman-file-complex.{h|c}</filename>):
|
||||||
<programlisting>
|
<programlisting>
|
||||||
GClosure *default_closure;
|
GClosure *default_closure;
|
||||||
GType param_types[2];
|
GType param_types[2];
|
||||||
|
|
||||||
default_closure = g_cclosure_new (G_CALLBACK (default_write_signal_handler),
|
default_closure = g_cclosure_new (G_CALLBACK (default_write_signal_handler),
|
||||||
(gpointer)0xdeadbeaf /* user_data */,
|
(gpointer)0xdeadbeaf /* user_data */,
|
||||||
NULL /* destroy_data */);
|
NULL /* destroy_data */);
|
||||||
|
|
||||||
param_types[0] = G_TYPE_POINTER;
|
param_types[0] = G_TYPE_POINTER;
|
||||||
param_types[1] = G_TYPE_UINT;
|
param_types[1] = G_TYPE_UINT;
|
||||||
klass->write_signal_id =
|
klass->write_signal_id =
|
||||||
g_signal_newv ("write",
|
g_signal_newv ("write",
|
||||||
G_TYPE_FROM_CLASS (g_class),
|
G_TYPE_FROM_CLASS (g_class),
|
||||||
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
|
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
|
||||||
@@ -1277,7 +1307,7 @@ maman_file_simple_class_init (gpointer g_class,
|
|||||||
</programlisting>
|
</programlisting>
|
||||||
Finally, the signal is created with <function>g_signal_new</function> in the same class_init function:
|
Finally, the signal is created with <function>g_signal_new</function> in the same class_init function:
|
||||||
<programlisting>
|
<programlisting>
|
||||||
klass->write_signal_id =
|
klass->write_signal_id =
|
||||||
g_signal_new ("write",
|
g_signal_new ("write",
|
||||||
G_TYPE_FROM_CLASS (g_class),
|
G_TYPE_FROM_CLASS (g_class),
|
||||||
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
|
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
|
||||||
|
Reference in New Issue
Block a user