GTK+ / Gnome Application Development | |||
---|---|---|---|
<<< Previous | Home | Next >>> |
Before delving further into GtkObject, you will need more details on GTK+'s type system. The type system is used in many contexts:
It allows signals and callbacks with any signature to be dynamically registered and dynamically queried. Function argument lists can be constructed at runtime.
It allows object attributes (values that you can "get" or "set") to be dynamically queried and manipulated.
It exports information about enumerations and bitfields (lists of permitted values, and human-readable names).
It is possible to identify types at runtime and traverse the object class hierarchy.
Because of its type system, GTK+ is particularly easy to manipulate from dynamically-typed, interactive languages. There are bindings available for nearly all popular languages, and the bindings can be lightweight (since GTK+ already includes much of the needed functionality, and types can be handled generically so the amount of glue code is reduced). You can find a complete list of functions for querying and using GTK+'s type system in gtk/gtktypeutils.h; most of these are not useful in applications. Only the functions of general interest are described in this book.
GTK+ has a number of so-called fundamental types which are automatically registered during gtk_init() (or gnome_init()). The fundamental types include all the primitive C types, some GTK+ types such as GTK_TYPE_SIGNAL, and GTK_TYPE_OBJECT. Fundamental types are essentially the "base classes" understood by the GTK+ type system; for example, the fundamental type of any enumeration is GTK_TYPE_ENUM, and the fundamental type of any GtkObject subclass is GTK_TYPE_OBJECT. Fundamental types are supposed to cover all the "special cases" in the GTK+ type system; all types ultimately derive from some fundamental type. A type's fundamental type is extracted from a GtkType with the GTK_FUNDAMENTAL_TYPE() macro. The fundamental types are shown in Table 1.
There is a second category of GtkType values: builtin types are registered by GTK+ and libgnomeui during library initialization and are thus always available. Builtin types include enumerations, flags, and some structs (GdkWindow, or GdkImlibImage, for example). Builtin types are distinct from fundamental types because the GTK+ object system does not have to understand them; for the purposes of getting and setting argument values, they can be treated as fundamental types. They are somewhat arbitrarily distinguished from user-registered enumeration or flag types. (The difference between builtin types and user types is the time of registration.)
Builtin types are all accessible via macros that come with GTK+ and Gnome. These begin with GTK_TYPE_, as in: GTK_TYPE_WINDOW, GTK_TYPE_GDK_WINDOW, GTK_TYPE_RELIEF_STYLE, GTK_TYPE_GNOME_DIALOG. As you can see, the name of the type macro is derived from the name of the GtkObject, struct, or enumeration; if the object name begins with "Gtk," the "Gtk" is dropped. The above examples map to the GtkWindow widget, GdkWindow struct, GtkReliefStyle enumeration, and GnomeDialog widget, respectively.
The final major category of GtkType values consists of the registered GtkObject types. These are registered the first time the _get_type() routine for each object is called.
Table 1. The GTK+ Fundamental Types
GtkType Constant | Corresponding C Type |
---|---|
GTK_TYPE_INVALID | None |
GTK_TYPE_NONE | void |
GTK_TYPE_CHAR | gchar |
GTK_TYPE_UCHAR | guchar |
GTK_TYPE_BOOL | gboolean |
GTK_TYPE_INT | gint |
GTK_TYPE_UINT | guint |
GTK_TYPE_LONG | glong |
GTK_TYPE_ULONG | gulong |
GTK_TYPE_FLOAT | gfloat |
GTK_TYPE_DOUBLE | gdouble |
GTK_TYPE_STRING | gchar* |
GTK_TYPE_ENUM | Any enumeration |
GTK_TYPE_FLAGS | guint |
GTK_TYPE_BOXED | gpointer |
GTK_TYPE_POINTER | gpointer |
GTK_TYPE_SIGNAL | GtkSignalFunc, gpointer |
GTK_TYPE_ARGS | gint, GtkArg* |
GTK_TYPE_CALLBACK | GtkCallbackMarshal, gpointer, GtkDestroyNotify |
GTK_TYPE_C_CALLBACK | GtkFunction, gpointer |
GTK_TYPE_FOREIGN | gpointer, GtkDestroyNotify |
GTK_TYPE_OBJECT | GtkObject* |
Some of the fundamental types require further explanation. In brief:
GTK_TYPE_INVALID: used to signal errors.
GTK_TYPE_NONE: used to indicate a void return value when specifying the signature of a signal.
GTK_TYPE_BOXED: Subtypes of GTK_TYPE_BOXED are used to mark the type of a generic pointer; language bindings will special case these types. Most GDK types, such as GdkWindow, are registered as boxed types.
GTK_TYPE_SIGNAL: special-cased in GtkObject; it allows users to connect signal handlers with gtk_object_set(). It should not be useful in application code.
GTK_TYPE_ARGS: type of an array of GtkArg (when used with gtk_object_set(), an integer array length followed by the array itself are expected as arguments).
GTK_TYPE_CALLBACK: interpreted language bindings can use this to pass signal callbacks around.
GTK_TYPE_C_CALLBACK: this is used for other kinds of callbacks, i.e. callbacks that are not attached to signals (such as the argument to a _foreach() function).
GTK_TYPE_FOREIGN: unused in current GTK+ code. Represents a pointer plus a function used to destroy the pointed-to resource; intended to represent object data (see the section called Attaching Data to Objects), for example.
A fundamental type describes not only describe the data layout but also how memory is managed. For values passed in as arguments, the called function is not allowed to retain the pointer beyond the duration of the call. For returned values, the caller assumes ownership of the memory. GTK_TYPE_BOXED, GTK_TYPE_ARGS, and GTK_TYPE_STRING obey this rule.
Note that you should almost always use the most informative type available. Notably, GTK_TYPE_POINTER should only be used for generic pointers (gpointer); whenever possible, prefer a "subclass" of GTK_TYPE_BOXED such as GTK_TYPE_GDK_WINDOW or GTK_TYPE_GDK_EVENT. Similarly, it is better to use a specific enumeration type, rather than GTK_TYPE_ENUM. GTK_TYPE_CALLBACK is normally preferred to GTK_TYPE_C_CALLBACK or GTK_TYPE_SIGNAL, because GTK_TYPE_CALLBACK includes information about how to marshal the function and destroy the callback data.
GTK+ has a consistent interface for passing typed values around; to do this, it needs a data structure which stores a type tag and a value. GtkArg fills the bill. Here is its definition, from gtk/gtktypeutils.h:
typedef struct _GtkArg GtkArg; struct _GtkArg { GtkType type; gchar *name; union { gchar char_data; guchar uchar_data; gboolean bool_data; gint int_data; guint uint_data; glong long_data; gulong ulong_data; gfloat float_data; gdouble double_data; gchar *string_data; gpointer pointer_data; GtkObject *object_data; struct { GtkSignalFunc f; gpointer d; } signal_data; struct { gint n_args; GtkArg *args; } args_data; struct { GtkCallbackMarshal marshal; gpointer data; GtkDestroyNotify notify; } callback_data; struct { GtkFunction func; gpointer func_data; } c_callback_data; struct { gpointer data; GtkDestroyNotify notify; } foreign_data; } d; }; |
The type field contains the value's GtkType, as you might expect. The name field is an object argument name---more on arguments in a moment. The final union stores a value of the appropriate type; there is one union member for each fundamental type. This value field should be accessed using a special set of macros provided for the purpose, listed in Figure 2; each macro corresponds to a fundamental type. These macros are defined so that you can use the & operator on them; e.g. >K_VALUE_CHAR(arg).
To print a GtkArg's value, you might write code like this:
GtkArg arg; /* ... */ switch (GTK_FUNDAMENTAL_TYPE (arg.type)) { case GTK_TYPE_INT: printf("arg: %d\n", GTK_VALUE_INT(arg)); break; /* ... case for each type ... */ } |
#include <gtk/gtktypeutils.h> |
GTK_VALUE_CHAR
(arg);
GTK_VALUE_UCHAR
(arg);
GTK_VALUE_BOOL
(arg);
GTK_VALUE_INT
(arg);
GTK_VALUE_UINT
(arg);
GTK_VALUE_LONG
(arg);
GTK_VALUE_ULONG
(arg);
GTK_VALUE_FLOAT
(arg);
GTK_VALUE_DOUBLE
(arg);
GTK_VALUE_STRING
(arg);
GTK_VALUE_ENUM
(arg);
GTK_VALUE_FLAGS
(arg);
GTK_VALUE_BOXED
(arg);
GTK_VALUE_POINTER
(arg);
GTK_VALUE_OBJECT
(arg);
GTK_VALUE_SIGNAL
(arg);
GTK_VALUE_ARGS
(arg);
GTK_VALUE_CALLBACK
(arg);
GTK_VALUE_C_CALLBACK
(arg);
GTK_VALUE_FOREIGN
(arg);
Figure 2. Macros for Accessing GtkArg Values
Some uses of GtkArg require you to assign a value to it. The GTK_VALUE_ macros are not appropriate here; instead, a parallel set of macros exist which return a pointer to an assignable location. These are called GTK_RETLOC_CHAR(), GTK_RETLOC_UCHAR(), and so on.