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 ... */
}
|
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.