mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-27 07:56:14 +01:00
docs: Update GObject how-to for G_DECLARE_*_TYPE macros
Restructure the section of the how-to which covers the header and source code boilerplate for declaring and defining GObjects to use the new G_DECLARE_*_TYPE macros. Present both final and derivable types. Trim various supporting paragraphs. Rename ‘class functions’ to ‘virtual functions’ to use consistent, modern terminology. https://bugzilla.gnome.org/show_bug.cgi?id=744060
This commit is contained in:
parent
b6fc1df022
commit
b6b0f5f305
@ -7,8 +7,8 @@
|
|||||||
<partintro>
|
<partintro>
|
||||||
<para>
|
<para>
|
||||||
This chapter tries to answer the real-life questions of users and presents
|
This chapter tries to answer the real-life questions of users and presents
|
||||||
the most common scenario use cases I could come up with.
|
the most common use cases in order from most likely to least
|
||||||
The use cases are presented from most likely to less likely.
|
likely.
|
||||||
</para>
|
</para>
|
||||||
</partintro>
|
</partintro>
|
||||||
|
|
||||||
@ -30,24 +30,12 @@
|
|||||||
The first step before writing the code for your GObject is to write the
|
The first step before writing the code for your GObject is to write the
|
||||||
type's header which contains the needed type, function and macro
|
type's header which contains the needed type, function and macro
|
||||||
definitions. Each of these elements is nothing but a convention which
|
definitions. Each of these elements is nothing but a convention which
|
||||||
is followed not only by GTK+'s code but also by most users of GObject.
|
is followed by almost all users of GObject, and has been refined over
|
||||||
If you feel the need not to obey the rules stated below, think about it
|
multiple years of experience developing GObject-based code. If you are
|
||||||
twice:
|
writing a library, it is particularly important for you to adhere closely
|
||||||
<itemizedlist>
|
to these conventions; users of your library will assume that you have.
|
||||||
<listitem><para>If your users are a bit accustomed to GTK+ code or any
|
Even if not writing a library, it will help other people who want to work
|
||||||
GLib code, they will be a bit surprised and getting used to the
|
on your project.
|
||||||
conventions you decided upon will take time (money) and will make them
|
|
||||||
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>
|
|
||||||
</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>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@ -67,27 +55,28 @@
|
|||||||
names easier for those with poor eyesight.
|
names easier for those with poor eyesight.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
|
||||||
When you need some private (internal) declarations in several
|
|
||||||
(sub)classes, you can define them in a private header file which
|
|
||||||
is often named by appending the <emphasis>private</emphasis> keyword
|
|
||||||
to the public header name. For example, one could use
|
|
||||||
<filename>maman-bar-private.h</filename>,
|
|
||||||
<filename>maman_bar_private.h</filename> or
|
|
||||||
<filename>mamanbarprivate.h</filename>. Typically, such private header
|
|
||||||
files are not installed.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
The basic conventions for any header which exposes a GType are described
|
The basic conventions for any header which exposes a GType are described
|
||||||
in <xref linkend="gtype-conventions"/>.
|
in <xref linkend="gtype-conventions"/>.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
If you want to declare a type named bar with prefix maman, name the type instance
|
If you want to declare a type named ‘bar’ in namespace ‘maman’, name the
|
||||||
<function>MamanBar</function> and its class <function>MamanBarClass</function>
|
type instance <function>MamanBar</function> and its class
|
||||||
(name is case-sensitive). It is customary to declare them with code similar to the
|
<function>MamanBarClass</function> (names are case sensitive). The
|
||||||
following:
|
recommended method of declaring a type differs based on whether the type
|
||||||
|
is final or derivable.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Final types cannot be subclassed further, and should be the default choice
|
||||||
|
for new types — changing a final type to be derivable is always a change
|
||||||
|
that will be compatible with existing uses of the code, but the converse
|
||||||
|
will often cause problems. Final types are declared using
|
||||||
|
<link linkend="G-DECLARE-FINAL-TYPE:CAPS"><function>G_DECLARE_FINAL_TYPE</function></link>,
|
||||||
|
and require a structure to hold the instance data to be declared in the
|
||||||
|
source code (not the header file).
|
||||||
|
|
||||||
<informalexample><programlisting>
|
<informalexample><programlisting>
|
||||||
/*
|
/*
|
||||||
* Copyright/Licensing information.
|
* Copyright/Licensing information.
|
||||||
@ -102,77 +91,92 @@
|
|||||||
* Potentially, include other headers on which this header depends.
|
* Potentially, include other headers on which this header depends.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Type macros.
|
* Type declaration.
|
||||||
*/
|
*/
|
||||||
#define MAMAN_TYPE_BAR (maman_bar_get_type ())
|
#define MAMAN_TYPE_BAR maman_bar_get_type ()
|
||||||
#define MAMAN_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_TYPE_BAR, MamanBar))
|
G_DECLARE_FINAL_TYPE (MamanBar, maman_bar, MAMAN, BAR, GObject)
|
||||||
#define MAMAN_IS_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_BAR))
|
|
||||||
#define MAMAN_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MAMAN_TYPE_BAR, MamanBarClass))
|
|
||||||
#define MAMAN_IS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MAMAN_TYPE_BAR))
|
|
||||||
#define MAMAN_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MAMAN_TYPE_BAR, MamanBarClass))
|
|
||||||
|
|
||||||
typedef struct _MamanBar MamanBar;
|
|
||||||
typedef struct _MamanBarClass MamanBarClass;
|
|
||||||
|
|
||||||
struct _MamanBar
|
|
||||||
{
|
|
||||||
/* Parent instance structure */
|
|
||||||
GObject parent_instance;
|
|
||||||
|
|
||||||
/* instance members */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct _MamanBarClass
|
|
||||||
{
|
|
||||||
/* Parent class structure */
|
|
||||||
GObjectClass parent_class;
|
|
||||||
|
|
||||||
/* class members */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* used by MAMAN_TYPE_BAR */
|
|
||||||
GType maman_bar_get_type (void);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Method definitions.
|
* Method definitions.
|
||||||
*/
|
*/
|
||||||
|
MamanBar *maman_bar_new (void);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* __MAMAN_BAR_H__ */
|
#endif /* __MAMAN_BAR_H__ */
|
||||||
</programlisting></informalexample>
|
</programlisting></informalexample>
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Finally, there are different header include conventions. Again, pick one
|
Derivable types <emphasis>can</emphasis> be subclassed further, and their class and
|
||||||
and stick to it. I personally use indifferently any of the two, depending
|
instance structures form part of the public API which must not be changed
|
||||||
on the codebase I work on: the rule, as always, is consistency.
|
if API stability is cared about. They are declared using
|
||||||
<itemizedlist>
|
<link linkend="G-DECLARE-DERIVABLE-TYPE:CAPS"><function>G_DECLARE_DERIVABLE_TYPE</function></link>:
|
||||||
<listitem><para>
|
<informalexample><programlisting>
|
||||||
Some people add at the top of their headers a number of #include
|
/*
|
||||||
directives to pull in all the headers needed to compile client
|
* Copyright/Licensing information.
|
||||||
code. This allows client code to simply #include "maman-bar.h".
|
*/
|
||||||
</para></listitem>
|
|
||||||
<listitem><para>
|
/* inclusion guard */
|
||||||
Other do not #include anything and expect the client to #include
|
#ifndef __MAMAN_BAR_H__
|
||||||
themselves the headers they need before including your header. This
|
#define __MAMAN_BAR_H__
|
||||||
speeds up compilation because it minimizes the amount of
|
|
||||||
pre-processor work. This can be used in conjunction with the
|
#include <glib-object.h>
|
||||||
re-declaration of certain unused types in the client code to
|
/*
|
||||||
minimize compile-time dependencies and thus speed up compilation.
|
* Potentially, include other headers on which this header depends.
|
||||||
</para></listitem>
|
*/
|
||||||
</itemizedlist>
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Type declaration.
|
||||||
|
*/
|
||||||
|
#define MAMAN_TYPE_BAR maman_bar_get_type ()
|
||||||
|
G_DECLARE_DERIVABLE_TYPE (MamanBar, maman_bar, MAMAN, BAR, GObject)
|
||||||
|
|
||||||
|
struct _MamanBarClass
|
||||||
|
{
|
||||||
|
GObjectClass parent_class;
|
||||||
|
|
||||||
|
/* Class virtual function fields. */
|
||||||
|
void (* handle_frob) (MamanBar *bar,
|
||||||
|
guint n_frobs);
|
||||||
|
|
||||||
|
/* Padding to allow adding up to 12 new virtual functions without
|
||||||
|
* breaking ABI. */
|
||||||
|
gpointer padding[12];
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Method definitions.
|
||||||
|
*/
|
||||||
|
MamanBar *maman_bar_new (void);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __MAMAN_BAR_H__ */
|
||||||
|
</programlisting></informalexample>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The convention for header includes is to add the minimum number of
|
||||||
|
<function>#include</function> directives to the top of your headers needed
|
||||||
|
to compile that header. This
|
||||||
|
allows client code to simply <function>#include "maman-bar.h"</function>,
|
||||||
|
without needing to know the prerequisites for
|
||||||
|
<filename>maman-bar.h</filename>.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
||||||
<sect1 id="howto-gobject-code">
|
<sect1 id="howto-gobject-code">
|
||||||
<title>Boilerplate code</title>
|
<title>Boilerplate code</title>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
In your code, the first step is to #include the needed headers: depending
|
In your code, the first step is to <function>#include</function> the
|
||||||
on your header include strategy, this can be as simple as
|
needed headers:
|
||||||
<literal>#include "maman-bar.h"</literal> or as complicated as tens
|
|
||||||
of #include lines ending with <literal>#include "maman-bar.h"</literal>:
|
|
||||||
<informalexample><programlisting>
|
<informalexample><programlisting>
|
||||||
/*
|
/*
|
||||||
* Copyright information
|
* Copyright information
|
||||||
@ -180,15 +184,11 @@ GType maman_bar_get_type (void);
|
|||||||
|
|
||||||
#include "maman-bar.h"
|
#include "maman-bar.h"
|
||||||
|
|
||||||
/* If you use Pimpls, include the private structure
|
/* Private structure definition. */
|
||||||
* definition here. Some people create a maman-bar-private.h header
|
typedef struct {
|
||||||
* which is included by the maman-bar.c file and which contains the
|
gint member1;
|
||||||
* definition for this private structure.
|
|
||||||
*/
|
|
||||||
struct _MamanBarPrivate {
|
|
||||||
int member_1;
|
|
||||||
/* stuff */
|
/* stuff */
|
||||||
};
|
} MamanBarPrivate;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* forward definitions
|
* forward definitions
|
||||||
@ -197,7 +197,24 @@ struct _MamanBarPrivate {
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Call the <function>G_DEFINE_TYPE</function> macro using the name
|
If the class is being declared as final using
|
||||||
|
<function>G_DECLARE_FINAL_TYPE</function>, its instance structure should
|
||||||
|
be defined in the C file:
|
||||||
|
<informalexample><programlisting>
|
||||||
|
struct _MamanBar
|
||||||
|
{
|
||||||
|
GObject parent_instance;
|
||||||
|
|
||||||
|
/* Other members, including private data. */
|
||||||
|
}
|
||||||
|
</programlisting></informalexample>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Call the <function>G_DEFINE_TYPE</function> macro (or
|
||||||
|
<function>G_DEFINE_TYPE_WITH_PRIVATE</function> if your class needs
|
||||||
|
private data — final types do <emphasis>not</emphasis> need private data)
|
||||||
|
using the name
|
||||||
of the type, the prefix of the functions and the parent GType to
|
of the type, the prefix of the functions and the parent GType to
|
||||||
reduce the amount of boilerplate needed. This macro will:
|
reduce the amount of boilerplate needed. This macro will:
|
||||||
|
|
||||||
@ -206,21 +223,40 @@ struct _MamanBarPrivate {
|
|||||||
function</simpara></listitem>
|
function</simpara></listitem>
|
||||||
<listitem><simpara>define a parent class pointer accessible from
|
<listitem><simpara>define a parent class pointer accessible from
|
||||||
the whole .c file</simpara></listitem>
|
the whole .c file</simpara></listitem>
|
||||||
|
<listitem><simpara>add private instance data to the type (if using
|
||||||
|
<function>G_DEFINE_TYPE_WITH_PRIVATE</function>)</simpara></listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
If the class has been declared as final using
|
||||||
|
<function>G_DECLARE_FINAL_TYPE</function> (see
|
||||||
|
<xref linkend="howto-gobject-header"/>), private data should be placed in
|
||||||
|
the instance structure, <type>MamanBar</type>, and
|
||||||
|
<function>G_DEFINE_TYPE</function> should be used instead of
|
||||||
|
<function>G_DEFINE_TYPE_WITH_PRIVATE</function>. The instance structure
|
||||||
|
for a final class is not exposed publicly, and is not embedded in the
|
||||||
|
instance structures of any derived classes (because the class is final);
|
||||||
|
so its size can vary without causing incompatibilities for code which uses
|
||||||
|
the class. Conversely, private data for derivable classes
|
||||||
|
<emphasis>must</emphasis> be included in a private structure, and
|
||||||
|
<function>G_DEFINE_TYPE_WITH_PRIVATE</function> must be used.
|
||||||
|
|
||||||
<informalexample><programlisting>
|
<informalexample><programlisting>
|
||||||
G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT)
|
G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT)
|
||||||
|
</programlisting></informalexample>
|
||||||
|
or
|
||||||
|
<informalexample><programlisting>
|
||||||
|
G_DEFINE_TYPE_WITH_PRIVATE (MamanBar, maman_bar, G_TYPE_OBJECT)
|
||||||
</programlisting></informalexample>
|
</programlisting></informalexample>
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
It is also possible to use the
|
It is also possible to use the
|
||||||
<function>G_DEFINE_TYPE_WITH_CODE</function> macro to control the
|
<function>G_DEFINE_TYPE_WITH_CODE</function> macro to control the
|
||||||
get_type function implementation - for instance, to add a call to
|
<function>get_type</function> function implementation — for instance, to
|
||||||
<function>G_IMPLEMENT_INTERFACE</function> macro which will
|
add a call to the <function>G_IMPLEMENT_INTERFACE</function> macro to
|
||||||
call the <function>g_type_implement_interface</function> function,
|
implement an interface.
|
||||||
or call the <function>G_ADD_PRIVATE</function> macro will add an
|
|
||||||
instance private data structure.
|
|
||||||
</para>
|
</para>
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
||||||
@ -477,11 +513,34 @@ maman_bar_do_action (MamanBar *self, /* parameters */)
|
|||||||
<title>Virtual public methods</title>
|
<title>Virtual public methods</title>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
This is the preferred way to create polymorphic GObjects. All you
|
This is the preferred way to create GObjects with overridable methods:
|
||||||
need to do is to define the common method and its class function in
|
<itemizedlist>
|
||||||
the public header, implement the common method in the source file
|
<listitem><para>
|
||||||
and re-implement the class function in each object which inherits
|
Define the common method and its virtual function in the
|
||||||
from you.
|
class structure in the public header
|
||||||
|
</para></listitem>
|
||||||
|
<listitem><para>
|
||||||
|
Define the common method in the header file and implement it in the
|
||||||
|
source file
|
||||||
|
</para></listitem>
|
||||||
|
<listitem><para>
|
||||||
|
Implement a base version of the virtual function in the source
|
||||||
|
file and initialize the virtual function pointer to this
|
||||||
|
implementation in the object’s <function>class_init</function>
|
||||||
|
function; or leave it as <constant>NULL</constant> for a ‘pure
|
||||||
|
virtual’ method which must be overridden by derived classes
|
||||||
|
</para></listitem>
|
||||||
|
<listitem><para>
|
||||||
|
Re-implement the virtual function in each derived class which needs
|
||||||
|
to override it
|
||||||
|
</para></listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Note that virtual functions can only be defined if the class is
|
||||||
|
derivable, declared using
|
||||||
|
<link linkend="G-DECLARE-DERIVABLE-TYPE:CAPS"><function>G_DECLARE_DERIVABLE_TYPE</function></link>
|
||||||
|
so the class structure can be defined.
|
||||||
<informalexample><programlisting>
|
<informalexample><programlisting>
|
||||||
/* declaration in maman-bar.h. */
|
/* declaration in maman-bar.h. */
|
||||||
struct _MamanBarClass
|
struct _MamanBarClass
|
||||||
@ -503,8 +562,8 @@ maman_bar_do_action (MamanBar *self, /* parameters */)
|
|||||||
MAMAN_BAR_GET_CLASS (self)->do_action (self, /* parameters */);
|
MAMAN_BAR_GET_CLASS (self)->do_action (self, /* parameters */);
|
||||||
}
|
}
|
||||||
</programlisting></informalexample>
|
</programlisting></informalexample>
|
||||||
The code above simply redirects the do_action call to the relevant
|
The code above simply redirects the <function>do_action</function> call
|
||||||
class function.
|
to the relevant virtual function.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@ -569,7 +628,7 @@ maman_bar_do_action_two (MamanBar *self, /* parameters */)
|
|||||||
<para>
|
<para>
|
||||||
These are very similar to Virtual Public methods. They just don't
|
These are very similar to Virtual Public methods. They just don't
|
||||||
have a public function to call the function directly. The header
|
have a public function to call the function directly. The header
|
||||||
file contains only a declaration of the class function:
|
file contains only a declaration of the virtual function:
|
||||||
<informalexample><programlisting>
|
<informalexample><programlisting>
|
||||||
/* declaration in maman-bar.h. */
|
/* declaration in maman-bar.h. */
|
||||||
struct _MamanBarClass
|
struct _MamanBarClass
|
||||||
@ -582,7 +641,7 @@ struct _MamanBarClass
|
|||||||
|
|
||||||
void maman_bar_do_any_action (MamanBar *self, /* parameters */);
|
void maman_bar_do_any_action (MamanBar *self, /* parameters */);
|
||||||
</programlisting></informalexample>
|
</programlisting></informalexample>
|
||||||
These class functions are often used to delegate part of the job
|
These virtual functions are often used to delegate part of the job
|
||||||
to child classes:
|
to child classes:
|
||||||
<informalexample><programlisting>
|
<informalexample><programlisting>
|
||||||
/* this accessor function is static: it is not exported outside of this file. */
|
/* this accessor function is static: it is not exported outside of this file. */
|
||||||
@ -611,7 +670,7 @@ maman_bar_do_any_action (MamanBar *self, /* parameters */)
|
|||||||
|
|
||||||
<para>
|
<para>
|
||||||
Again, it is possible to provide a default implementation for this
|
Again, it is possible to provide a default implementation for this
|
||||||
private virtual class function:
|
private virtual function:
|
||||||
<informalexample><programlisting>
|
<informalexample><programlisting>
|
||||||
static void
|
static void
|
||||||
maman_bar_class_init (MamanBarClass *klass)
|
maman_bar_class_init (MamanBarClass *klass)
|
||||||
@ -633,7 +692,7 @@ maman_bar_subtype_class_init (MamanBarSubTypeClass *klass)
|
|||||||
{
|
{
|
||||||
MamanBarClass *bar_class = MAMAN_BAR_CLASS (klass);
|
MamanBarClass *bar_class = MAMAN_BAR_CLASS (klass);
|
||||||
|
|
||||||
/* implement pure virtual class function. */
|
/* implement pure virtual function. */
|
||||||
bar_class->do_specific_action_one = maman_bar_subtype_do_specific_action_one;
|
bar_class->do_specific_action_one = maman_bar_subtype_do_specific_action_one;
|
||||||
}
|
}
|
||||||
</programlisting></informalexample>
|
</programlisting></informalexample>
|
||||||
@ -667,7 +726,7 @@ maman_bar_subtype_class_init (MamanBarSubTypeClass *klass)
|
|||||||
<para>
|
<para>
|
||||||
To explicitly chain up to the implementation of the virtual method in the parent class,
|
To explicitly 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
|
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.
|
access the original virtual function pointer and invoke it directly.
|
||||||
<footnote>
|
<footnote>
|
||||||
<para>
|
<para>
|
||||||
The <emphasis>original</emphasis> adjective used in this sentence is not innocuous. To fully
|
The <emphasis>original</emphasis> adjective used in this sentence is not innocuous. To fully
|
||||||
@ -1662,7 +1721,7 @@ klass->write_signal_id =
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
If the signal's default handler is just a class function pointer, it is also possible to override
|
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
|
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.
|
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
|
Of course, it is also possible (and recommended) to chain up from the child to the parent's default signal
|
||||||
@ -1693,9 +1752,9 @@ klass->write_signal_id =
|
|||||||
<para>
|
<para>
|
||||||
Usually, the <function><link linkend="g-signal-new">g_signal_new</link></function> function is preferred over
|
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>
|
<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 class function. For example,
|
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>
|
<filename>gobject.h</filename> contains the declaration of <link linkend="GObjectClass"><type>GObjectClass</type></link>
|
||||||
whose notify class function is the default handler for the <emphasis>notify</emphasis>
|
whose notify virtual function is the default handler for the <emphasis>notify</emphasis>
|
||||||
signal:
|
signal:
|
||||||
<informalexample><programlisting>
|
<informalexample><programlisting>
|
||||||
struct _GObjectClass
|
struct _GObjectClass
|
||||||
@ -1713,7 +1772,7 @@ struct _GObjectClass
|
|||||||
|
|
||||||
<para>
|
<para>
|
||||||
<filename>gobject.c</filename>'s <function><link linkend="g-object-do-class-init">g_object_do_class_init</link></function> function
|
<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 class function
|
registers the <emphasis>notify</emphasis> signal and initializes this virtual function
|
||||||
to NULL:
|
to NULL:
|
||||||
<informalexample><programlisting>
|
<informalexample><programlisting>
|
||||||
static void
|
static void
|
||||||
@ -1736,24 +1795,24 @@ g_object_do_class_init (GObjectClass *class)
|
|||||||
}
|
}
|
||||||
</programlisting></informalexample>
|
</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
|
<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 class function pointer and invoke it if it not NULL. The
|
type's class structure to access the virtual function pointer and invoke it if it not NULL. The
|
||||||
class function is ignored it is set to NULL.
|
virtual function is ignored it is set to NULL.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
To understand the reason for such a complex scheme to access the signal's default handler,
|
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
|
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
|
a part of the process to the user without requiring the user to subclass the object to override
|
||||||
one of the class functions. The alternative to subclassing, that is, the use of signals
|
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
|
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
|
than just dereferencing a function pointer in a class structure, you must start the whole
|
||||||
process of signal emission which is a bit heavyweight.
|
process of signal emission which is a bit heavyweight.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
This is why some people decided to use class functions for some signal's default handlers:
|
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
|
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 class function which is
|
from within that handler, you just need to override the default virtual function which is
|
||||||
supposedly more efficient.
|
supposedly more efficient.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user