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.