mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-13 15:56:23 +01: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:
parent
0344e6cb83
commit
f1287a9b2f
@ -1424,407 +1424,5 @@ maman_file_write (MamanFile *self,
|
||||
parameter types.
|
||||
</para>
|
||||
</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>
|
||||
|
||||
<!--
|
||||
<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>
|
||||
|
Loading…
Reference in New Issue
Block a user