GtkArg and the Type System

Before delving further into GtkObject, you will need more details on GTK+'s type system. The type system is used in many contexts:

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:

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. &GTK_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.