mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-08-22 00:48:53 +02:00
docs: Remove commented out sections from GObject how-to
Unused, outdated, and unsalvagable. https://bugzilla.gnome.org/show_bug.cgi?id=744060
This commit is contained in:
@@ -1424,407 +1424,5 @@ maman_file_write (MamanFile *self,
|
|||||||
parameter types.
|
parameter types.
|
||||||
</para>
|
</para>
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
||||||
<!--
|
|
||||||
this is utterly wrong and should be completely removed - or rewritten
|
|
||||||
with a better example than writing a buffer using synchronous signals.
|
|
||||||
|
|
||||||
<sect1>
|
|
||||||
<title>How to provide more flexibility to users?</title>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The previous implementation does the job but the signal facility of
|
|
||||||
GObject can be used to provide even more flexibility to this file
|
|
||||||
change notification mechanism. One of the key ideas is to make the
|
|
||||||
process of writing data to the file part of the signal emission
|
|
||||||
process to allow users to be notified either before or after the
|
|
||||||
data is written to the file.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
To integrate the process of writing the data to the file into the
|
|
||||||
signal emission mechanism, we can register a default class closure
|
|
||||||
for this signal which will be invoked during the signal emission,
|
|
||||||
just like any other user-connected signal handler.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The first step to implement this idea is to change the signature of
|
|
||||||
the signal: we need to pass around the buffer to write and its size.
|
|
||||||
To do this, we use our own marshaller which will be generated
|
|
||||||
through GLib's glib-genmarshal tool. We thus create a file named <filename>marshall.list</filename> which contains
|
|
||||||
the following single line:
|
|
||||||
<informalexample><programlisting>
|
|
||||||
VOID:POINTER,UINT
|
|
||||||
</programlisting></informalexample>
|
|
||||||
and use the Makefile provided in <filename>sample/signal/Makefile</filename> to generate the file named
|
|
||||||
<filename>maman-file-complex-marshall.c</filename>. This C file is finally included in
|
|
||||||
<filename>maman-file-complex.c</filename>.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Once the marshaller is present, we register the signal and its marshaller in the class_init function
|
|
||||||
of the object <type>MamanFileComplex</type> (full source for this object is included in
|
|
||||||
<filename>sample/signal/maman-file-complex.{h|c}</filename>):
|
|
||||||
<informalexample><programlisting>
|
|
||||||
GClosure *default_closure;
|
|
||||||
GType param_types[2];
|
|
||||||
|
|
||||||
default_closure = g_cclosure_new (G_CALLBACK (default_write_signal_handler),
|
|
||||||
(gpointer)0xdeadbeaf /* user_data */,
|
|
||||||
NULL /* destroy_data */);
|
|
||||||
|
|
||||||
param_types[0] = G_TYPE_POINTER;
|
|
||||||
param_types[1] = G_TYPE_UINT;
|
|
||||||
klass->write_signal_id =
|
|
||||||
g_signal_newv ("write",
|
|
||||||
G_TYPE_FROM_CLASS (g_class),
|
|
||||||
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
|
|
||||||
default_closure /* class closure */,
|
|
||||||
NULL /* accumulator */,
|
|
||||||
NULL /* accu_data */,
|
|
||||||
maman_file_complex_VOID__POINTER_UINT,
|
|
||||||
G_TYPE_NONE /* return_type */,
|
|
||||||
2 /* n_params */,
|
|
||||||
param_types /* param_types */);
|
|
||||||
</programlisting></informalexample>
|
|
||||||
The code shown above first creates the closure which contains the code to complete the file write. This
|
|
||||||
closure is registered as the default class_closure of the newly created signal.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Of course, you need to implement completely the code for the default closure since I just provided
|
|
||||||
a skeleton:
|
|
||||||
<informalexample><programlisting>
|
|
||||||
static void
|
|
||||||
default_write_signal_handler (GObject *obj, guint8 *buffer, guint size, gpointer user_data)
|
|
||||||
{
|
|
||||||
g_assert (user_data == (gpointer)0xdeadbeaf);
|
|
||||||
/* Here, we trigger the real file write. */
|
|
||||||
g_print ("default signal handler: 0x%x %u\n", buffer, size);
|
|
||||||
}
|
|
||||||
</programlisting></informalexample>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Finally, the client code must invoke the <function>maman_file_complex_write</function> function which
|
|
||||||
triggers the signal emission:
|
|
||||||
<informalexample><programlisting>
|
|
||||||
void maman_file_complex_write (MamanFileComplex *self, guint8 *buffer, guint size)
|
|
||||||
{
|
|
||||||
/* trigger event */
|
|
||||||
g_signal_emit (self,
|
|
||||||
MAMAN_FILE_COMPLEX_GET_CLASS (self)->write_signal_id,
|
|
||||||
0, /* details */
|
|
||||||
buffer, size);
|
|
||||||
}
|
|
||||||
</programlisting></informalexample>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The client code (as shown in <filename>sample/signal/test.c</filename> and below) can now connect signal handlers before
|
|
||||||
and after the file write is completed: since the default signal handler which does the write itself runs during the
|
|
||||||
RUN_LAST phase of the signal emission, it will run after all handlers connected with <function><link linkend="g-signal-connect">g_signal_connect</link></function>
|
|
||||||
and before all handlers connected with <function><link linkend="g-signal-connect-after">g_signal_connect_after</link></function>. If you intent to write a GObject
|
|
||||||
which emits signals, I would thus urge you to create all your signals with the G_SIGNAL_RUN_LAST such that your users
|
|
||||||
have a maximum of flexibility as to when to get the event. Here, we combined it with G_SIGNAL_NO_RECURSE and
|
|
||||||
G_SIGNAL_NO_HOOKS to ensure our users will not try to do really weird things with our GObject. I strongly advise you
|
|
||||||
to do the same unless you really know why (in which case you really know the inner workings of GSignal by heart and
|
|
||||||
you are not reading this).
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
<informalexample><programlisting>
|
|
||||||
static void complex_write_event_before (GObject *file, guint8 *buffer, guint size, gpointer user_data)
|
|
||||||
{
|
|
||||||
g_assert (user_data == NULL);
|
|
||||||
g_print ("Complex Write event before: 0x%x, %u\n", buffer, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void complex_write_event_after (GObject *file, guint8 *buffer, guint size, gpointer user_data)
|
|
||||||
{
|
|
||||||
g_assert (user_data == NULL);
|
|
||||||
g_print ("Complex Write event after: 0x%x, %u\n", buffer, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_file_complex (void)
|
|
||||||
{
|
|
||||||
guint8 buffer[100];
|
|
||||||
GObject *file;
|
|
||||||
|
|
||||||
file = g_object_new (MAMAN_FILE_COMPLEX_TYPE, NULL);
|
|
||||||
|
|
||||||
g_signal_connect (G_OBJECT (file), "write",
|
|
||||||
(GCallback)complex_write_event_before,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
g_signal_connect_after (G_OBJECT (file), "write",
|
|
||||||
(GCallback)complex_write_event_after,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
maman_file_complex_write (MAMAN_FILE_COMPLEX (file), buffer, 50);
|
|
||||||
|
|
||||||
g_object_unref (G_OBJECT (file));
|
|
||||||
}
|
|
||||||
</programlisting></informalexample>
|
|
||||||
The code above generates the following output on my machine:
|
|
||||||
<programlisting>
|
|
||||||
Complex Write event before: 0xbfffe280, 50
|
|
||||||
default signal handler: 0xbfffe280 50
|
|
||||||
Complex Write event after: 0xbfffe280, 50
|
|
||||||
</programlisting>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!--
|
|
||||||
this is also utterly wrong on so many levels that I don't even want
|
|
||||||
to enumerate them. it's also full of completely irrelevant footnotes
|
|
||||||
about personal preferences demonstrating a severe lack of whatsoever
|
|
||||||
clue. the whole idea of storing the signal ids inside the Class
|
|
||||||
structure is so fundamentally flawed that I'll require a frontal
|
|
||||||
lobotomy just to forget I've ever seen it.
|
|
||||||
|
|
||||||
<sect2>
|
|
||||||
<title>How most people do the same thing with less code</title>
|
|
||||||
|
|
||||||
<para>For many historic reasons related to how the ancestor of GObject used to work in GTK+ 1.x versions,
|
|
||||||
there is a much <emphasis>simpler</emphasis>
|
|
||||||
<footnote>
|
|
||||||
<para>I personally think that this method is horribly mind-twisting: it adds a new indirection
|
|
||||||
which unnecessarily complicates the overall code path. However, because this method is widely used
|
|
||||||
by all of GTK+ and GObject code, readers need to understand it. The reason why this is done that way
|
|
||||||
in most of GTK+ is related to the fact that the ancestor of GObject did not provide any other way to
|
|
||||||
create a signal with a default handler than this one. Some people have tried to justify that it is done
|
|
||||||
that way because it is better, faster (I am extremely doubtful about the faster bit. As a matter of fact,
|
|
||||||
the better bit also mystifies me ;-). I have the feeling no one really knows and everyone does it
|
|
||||||
because they copy/pasted code from code which did the same. It is probably better to leave this
|
|
||||||
specific trivia to hacker legends domain...
|
|
||||||
</para>
|
|
||||||
</footnote>
|
|
||||||
way to create a signal with a default handler than to create
|
|
||||||
a closure by hand and to use the <function><link linkend="g-signal-newv">g_signal_newv</link></function>.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>For example, <function><link linkend="g-signal-new">g_signal_new</link></function> can be used to create a signal which uses a default
|
|
||||||
handler which is stored in the class structure of the object. More specifically, the class structure
|
|
||||||
contains a function pointer which is accessed during signal emission to invoke the default handler and
|
|
||||||
the user is expected to provide to <function><link linkend="g-signal-new">g_signal_new</link></function> the offset from the start of the
|
|
||||||
class structure to the function pointer.
|
|
||||||
<footnote>
|
|
||||||
<para>I would like to point out here that the reason why the default handler of a signal is named everywhere
|
|
||||||
a class_closure is probably related to the fact that it used to be really a function pointer stored in
|
|
||||||
the class structure.
|
|
||||||
</para>
|
|
||||||
</footnote>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>The following code shows the declaration of the <type>MamanFileSimple</type> class structure which contains
|
|
||||||
the <function>write</function> function pointer.
|
|
||||||
<informalexample><programlisting>
|
|
||||||
struct _MamanFileSimpleClass {
|
|
||||||
GObjectClass parent;
|
|
||||||
|
|
||||||
guint write_signal_id;
|
|
||||||
|
|
||||||
/* signal default handlers */
|
|
||||||
void (*write) (MamanFileSimple *self, guint8 *buffer, guint size);
|
|
||||||
};
|
|
||||||
</programlisting></informalexample>
|
|
||||||
The <function>write</function> function pointer is initialized in the class_init function of the object
|
|
||||||
to <function>default_write_signal_handler</function>:
|
|
||||||
<informalexample><programlisting>
|
|
||||||
static void
|
|
||||||
maman_file_simple_class_init (gpointer g_class,
|
|
||||||
gpointer g_class_data)
|
|
||||||
{
|
|
||||||
GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
|
|
||||||
MamanFileSimpleClass *klass = MAMAN_FILE_SIMPLE_CLASS (g_class);
|
|
||||||
|
|
||||||
klass->write = default_write_signal_handler;
|
|
||||||
</programlisting></informalexample>
|
|
||||||
Finally, the signal is created with <function><link linkend="g-signal-new">g_signal_new</link></function> in the same class_init function:
|
|
||||||
<informalexample><programlisting>
|
|
||||||
klass->write_signal_id =
|
|
||||||
g_signal_new ("write",
|
|
||||||
G_TYPE_FROM_CLASS (g_class),
|
|
||||||
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
|
|
||||||
G_STRUCT_OFFSET (MamanFileSimpleClass, write),
|
|
||||||
NULL /* accumulator */,
|
|
||||||
NULL /* accu_data */,
|
|
||||||
maman_file_complex_VOID__POINTER_UINT,
|
|
||||||
G_TYPE_NONE /* return_type */,
|
|
||||||
2 /* n_params */,
|
|
||||||
G_TYPE_POINTER,
|
|
||||||
G_TYPE_UINT);
|
|
||||||
</programlisting></informalexample>
|
|
||||||
Of note, here, is the 4th argument to the function: it is an integer calculated by the <function><link linkend="G-STRUCT-OFFSET">G_STRUCT_OFFSET</link></function>
|
|
||||||
macro which indicates the offset of the member <emphasis>write</emphasis> from the start of the
|
|
||||||
<type>MamanFileSimpleClass</type> class structure.
|
|
||||||
<footnote>
|
|
||||||
<para>GSignal uses this offset to create a special wrapper closure
|
|
||||||
which first retrieves the target function pointer before calling it.
|
|
||||||
</para>
|
|
||||||
</footnote>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
While the complete code for this type of default handler looks less cluttered as shown in
|
|
||||||
<filename>sample/signal/maman-file-simple.{h|c}</filename>, it contains numerous subtleties.
|
|
||||||
The main subtle point which everyone must be aware of is that the signature of the default
|
|
||||||
handler created that way does not have a user_data argument:
|
|
||||||
<function>default_write_signal_handler</function> is different in
|
|
||||||
<filename>sample/signal/maman-file-complex.c</filename> and in
|
|
||||||
<filename>sample/signal/maman-file-simple.c</filename>.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>If you have doubts about which method to use, I would advise you to use the second one which
|
|
||||||
involves <function><link linkend="g-signal-new">g_signal_new</link></function> rather than <function><link linkend="g-signal-newv">g_signal_newv</link></function>:
|
|
||||||
it is better to write code which looks like the vast majority of other GTK+/GObject code than to
|
|
||||||
do it your own way. However, now, you know why.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</sect2>
|
|
||||||
|
|
||||||
</sect1>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!--
|
|
||||||
yet another pointless section. if we are scared of possible abuses
|
|
||||||
from the users then we should not be mentioning it inside a tutorial
|
|
||||||
for beginners. but, obviously, there's nothing to be afraid of - it's
|
|
||||||
just that this section must be completely reworded.
|
|
||||||
|
|
||||||
<sect1>
|
|
||||||
<title>How users can abuse signals (and why some think it is good)</title>
|
|
||||||
|
|
||||||
<para>Now that you know how to create signals to which the users can connect easily and at any point in
|
|
||||||
the signal emission process thanks to <function><link linkend="g-signal-connect">g_signal_connect</link></function>,
|
|
||||||
<function><link linkend="g-signal-connect-after">g_signal_connect_after</link></function> and G_SIGNAL_RUN_LAST, it is time to look into how your
|
|
||||||
users can and will screw you. This is also interesting to know how you too, can screw other people.
|
|
||||||
This will make you feel good and eleet.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
The users can:
|
|
||||||
<itemizedlist>
|
|
||||||
<listitem><para>stop the emission of the signal at anytime</para></listitem>
|
|
||||||
<listitem><para>override the default handler of the signal if it is stored as a function
|
|
||||||
pointer in the class structure (which is the preferred way to create a default signal handler,
|
|
||||||
as discussed in the previous section).</para></listitem>
|
|
||||||
</itemizedlist>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
In both cases, the original programmer should be as careful as possible to write code which is
|
|
||||||
resistant to the fact that the default handler of the signal might not able to run. This is obviously
|
|
||||||
not the case in the example used in the previous sections since the write to the file depends on whether
|
|
||||||
or not the default handler runs (however, this might be your goal: to allow the user to prevent the file
|
|
||||||
write if he wishes to).
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
If all you want to do is to stop the signal emission from one of the callbacks you connected yourself,
|
|
||||||
you can call <function><link linkend="g-signal-stop-by-name">g_signal_stop_by_name</link></function>. Its use is very simple which is why I won't detail
|
|
||||||
it further.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
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
|
|
||||||
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
|
|
||||||
handler to ensure the integrity of the parent object.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Overriding a class method and chaining up was demonstrated in <xref linkend="howto-gobject-methods"/>
|
|
||||||
which is why I won't bother to show exactly how to do it here again.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</sect1>
|
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
</chapter>
|
</chapter>
|
||||||
|
|
||||||
<!--
|
|
||||||
<sect2>
|
|
||||||
<title>Warning on signal creation and default closure</title>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
Most of the existing code I have seen up to now (in both GTK+, GNOME libraries and
|
|
||||||
many GTK+ and GNOME applications) using signals uses a small
|
|
||||||
variation of the default handler pattern I have shown in the previous section.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
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>
|
|
||||||
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>
|
|
||||||
whose notify virtual function is the default handler for the <emphasis>notify</emphasis>
|
|
||||||
signal:
|
|
||||||
<informalexample><programlisting>
|
|
||||||
struct _GObjectClass
|
|
||||||
{
|
|
||||||
GTypeClass g_type_class;
|
|
||||||
|
|
||||||
/* class methods and other stuff. */
|
|
||||||
|
|
||||||
/* signals */
|
|
||||||
void (*notify) (GObject *object,
|
|
||||||
GParamSpec *pspec);
|
|
||||||
};
|
|
||||||
</programlisting></informalexample>
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
<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 virtual function
|
|
||||||
to NULL:
|
|
||||||
<informalexample><programlisting>
|
|
||||||
static void
|
|
||||||
g_object_do_class_init (GObjectClass *class)
|
|
||||||
{
|
|
||||||
|
|
||||||
/* Stuff */
|
|
||||||
|
|
||||||
class->notify = NULL;
|
|
||||||
|
|
||||||
gobject_signals[NOTIFY] =
|
|
||||||
g_signal_new ("notify",
|
|
||||||
G_TYPE_FROM_CLASS (class),
|
|
||||||
G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED | G_SIGNAL_NO_HOOKS,
|
|
||||||
G_STRUCT_OFFSET (GObjectClass, notify),
|
|
||||||
NULL, NULL,
|
|
||||||
g_cclosure_marshal_VOID__PARAM,
|
|
||||||
G_TYPE_NONE,
|
|
||||||
1, G_TYPE_PARAM);
|
|
||||||
}
|
|
||||||
</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
|
|
||||||
type's class structure to access the virtual function pointer and invoke it if it not NULL. The
|
|
||||||
virtual function is ignored it is set to NULL.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
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
|
|
||||||
a part of the process to the user without requiring the user to subclass the object to override
|
|
||||||
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
|
|
||||||
than just dereferencing a function pointer in a class structure, you must start the whole
|
|
||||||
process of signal emission which is a bit heavyweight.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
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
|
|
||||||
from within that handler, you just need to override the default virtual function which is
|
|
||||||
supposedly more efficient.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</sect2>
|
|
||||||
-->
|
|
||||||
</part>
|
</part>
|
||||||
|
Reference in New Issue
Block a user