GTK+ has an extensive type system, which is to some extent
independent of its object system. However, the object
system makes use of the larger type system. Every object
has a type, and every type has a unique integer identifier.
When writing a GtkObject,
it's customary to provide a function which returns the
type's identifier.
In the case of GtkButton, the
relevant function is:
GtkType gtk_button_get_type();
|
The first time this function is invoked, it will register a
GtkButton type with the
object system, and in the process obtain a type identifier.
On subsequent calls, the type identifier is simply
returned. GtkType is a
typedef (unsigned int is
the actual type of GTK+'s type identifiers).
The type system allows GTK+ to check the validity of casts.
To facilitate this, objects customarily provide macros like
these in their header file:
#define GTK_TYPE_BUTTON (gtk_button_get_type ())
#define GTK_BUTTON(obj) (GTK_CHECK_CAST ((obj), \
GTK_TYPE_BUTTON, GtkButton))
#define GTK_BUTTON_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), \
GTK_TYPE_BUTTON, GtkButtonClass))
#define GTK_IS_BUTTON(obj) (GTK_CHECK_TYPE ((obj), \
GTK_TYPE_BUTTON))
#define GTK_IS_BUTTON_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), \
GTK_TYPE_BUTTON))
|
Instead of simply casting an object, you can use the GTK_BUTTON() macro. If GTK_NO_CHECK_CASTS is defined, these macros
are equivalent to simple casts. Otherwise, they retrieve
the type of the object and compare it to the type you're
attempting to cast to.
GTK+ also provides convenient runtime type checking, with
the GTK_IS_BUTTON() macro. This
is often used in preconditions; for example, a function
expecting a button as an argument might have this check at
the beginning:
g_return_if_fail(GTK_IS_BUTTON(widget));
|
The GTK+ and Gnome library functions have many such checks.
You can also use the macro to make certain code conditional
on an object's type, though this is most likely a poor idea
from a design standpoint.
To give you an idea what sort of information GTK+ stores
about each object type, here's the implementation of gtk_button_get_type():
GtkType
gtk_button_get_type (void)
{
static GtkType button_type = 0;
if (!button_type)
{
static const GtkTypeInfo button_info =
{
"GtkButton",
sizeof (GtkButton),
sizeof (GtkButtonClass),
(GtkClassInitFunc) gtk_button_class_init,
(GtkObjectInitFunc) gtk_button_init,
/* reserved_1 */ NULL,
/* reserved_2 */ NULL,
(GtkClassInitFunc) NULL,
};
button_type = gtk_type_unique (GTK_TYPE_BIN, &button_info);
gtk_type_set_chunk_alloc (button_type, 16);
}
return button_type;
}
|
The code fills in a struct with information about the
class, then hands that struct to GTK+ to get a type
identifier (GtkType). Only
six components of the
GtkTypeInfo struct are important. GtkButton gives GTK+ a human-readable name
for the class, used in error messages and the like; the
size of the instance and class structs; then a function to
initialize the class struct and another to initialize each
new instance. The sixth and seventh members of the struct
(reserved_1 and reserved_2) are obsolete and
preserved only for compatibility. The final member is a
pointer to a base class initialization function, used to
initialize the class struct of any subclasses.
gtk_type_unique() registers the
new type and obtains a type identifier. The GTK_TYPE_BIN argument is a macro
containing the type of
GtkButton's parent class,
GtkBin. The call to
gtk_type_set_chunk_alloc() optimizes memory allocation
for this type; it is never required, and should only be
used for frequently-allocated types like GtkButton.
Given a registered GtkButton
type, the following code creates a type instance:
GtkWidget*
gtk_button_new (void)
{
return GTK_WIDGET (gtk_type_new (gtk_button_get_type ()));
}
|
The newborn GtkButton will be
initialized by its instance initializer. The instance
initialization function is called each time an instance of
the type is created; it gives the object's data members
reasonable default values:
static void
gtk_button_init (GtkButton *button)
{
GTK_WIDGET_SET_FLAGS (button, GTK_CAN_FOCUS);
GTK_WIDGET_UNSET_FLAGS (button, GTK_NO_WINDOW);
button->child = NULL;
button->in_button = FALSE;
button->button_down = FALSE;
button->relief = GTK_RELIEF_NORMAL;
}
|
Remember that gtk_button_init()
was passed to gtk_type_unique()
when the GtkButton type was
created. GTK+ stores the function pointer and uses it to
create GtkButton instances.
Instance structs are created with all bits set to 0; so
settings members to 0 or
NULL is not strictly necessary. Still, most GTK+
code does initialize the members, for clarity.
The class initialization and base class initialization
functions require some background information to understand
fully; you will know how to write them after you read this
chapter.