| GTK+ / Gnome Application Development | |||
|---|---|---|---|
| <<< Previous | Home | Next >>> | |
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.