2.2 Defining Classes


2.2 Defining Classes

To put it bluntly, defining a class in GObject isn't easy. If you're used to programming languages like C++, Java, and Smalltalk, this procedure is going to look quite awkward . [1] However, you should try to look at it from an unbiased perspective.

To use GObject, you must include the glib-object.h header file and invoke g_type_init() somewhere in your program initialization to set up the GObject type system (GType). Keep in mind that some other function might call g_type_init() , such as gtk_init() , as you'll see in Chapter 3.

2.2.1 Structure Definitions

A GObject class consists of two structures. First, the instance structure (or object structure ) holds the class attributes and is the basis for an object in memory. The other structure, the class structure , contains prototypes for certain methods and all signals that the object can provide (you will encounter signals in Section 2.6).

For the Media class in Section 2.1.2, you could define Media as the instance structure and MediaClass as the class structure:

 /* instance structure */ typedef struct _Media {   GObject parent_instance;   guint inv_nr;   GString *location;   GString *title;   gboolean orig_package; } Media; /* class structure */ typedef struct _MediaClass {   GObjectClass parent_class;   /* Signals */   void (*unpacked)(Media *media);   void (*throw_out)(Media *media, gboolean permanent); } MediaClass; 

The attributes in the instance structure include inv_nr , location , title , and orig_package (this last attribute indicates whether the item is still in the original package). The class structure includes handler prototypes for two signals: unpacked and throw-out . Ignore them for now; they will reappear in Section 2.6.

Note  

As you may have noticed by now, there are no prototypes for methods in this class definition. As you will see in Section 2.3, most method prototypes appear outside the class structure.

There is a parent_instance pointer at the beginning of the instance structure, as well as a corresponding parent_class pointer in the class structure. These two definitions are necessary for inheritance; without them, you wouldn't be able do much with your objects (all classes inherit important common features from a base GObject class).

2.2.2 Utility Macros

The following macros are practically essential for smooth operation (you could theoretically write them out by hand each time that you wanted to use these features, but it would test your patience):

 #define TYPE_MEDIA (media_get_type()) #define MEDIA(object) \   (G_TYPE_CHECK_INSTANCE_CAST((object), TYPE_MEDIA, Media)) #define MEDIA_CLASS(klass) \   (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_MEDIA, MediaClass)) #define IS_MEDIA(object) \   (G_TYPE_CHECK_INSTANCE_TYPE((object), TYPE_MEDIA)) #define IS_MEDIA_CLASS(klass) \   (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_MEDIA)) #define MEDIA_GET_CLASS(object) \   (G_TYPE_INSTANCE_GET_CLASS((object), TYPE_MEDIA, MediaClass)) 

These are somewhat difficult to digest at once, so let's go over them one by one. Note the ongoing difference between the instance and class structures.

  • TYPE_MEDIA returns the class type identifier, a GType assignment for the Media class. It calls media_get_type() ” see Section 2.2.3 for more details. You'll use this macro in any place that calls for an object or type identifier. When you see the term (class) type identifier in this section, you need to remember that this is different from a C type.

You will use TYPE_MEDIA when creating Media objects.

  • MEDIA() is a casting macro that casts an object (the instance structure) to the Media type, or, in other words, the Media structure. This is often simply known as a cast, like its C counterpart .

    Casting macros are useful when calling a method from an object's superclass.

  • MEDIA_CLASS() is another casting macro. This one operates on the class structure , returning a casted MediaClass structure.

  • IS_MEDIA() checks the membership of an object for the Media structure. It is TRUE if the object belongs to the class, and FALSE otherwise .

  • IS_MEDIA_CLASS() checks the membership of a class for the MediaClass structure.

  • MEDIA_GET_CLASS() yields the class structure of Media when given an object (again, an instance) of the Media structure.

As you can see, these macros build on other macros:

  • G_TYPE_CHECK_INSTANCE_CAST( object, type_id, name ) If object , a class type identifier type_id (for example, TYPE_MEDIA ), and the instance structure name match, this macro expands to a casted pointer to object 's instance structure for type_id . Note that matching includes super-classes of object .

  • G_TYPE_CHECK_CLASS_CAST( klass, type_id, class_name ) Same as the preceding , but for class structure pointers.

Note  

name and class_name are the type names from the C structure definitions. Make sure that you understand that these are very different from type_id , a runtime identifier for the entire class.

The preceding macros provide warnings when you attempt to make invalid casts.

  • G_TYPE_CHECK_INSTANCE_TYPE( object, type_id )

    Returns TRUE if object belongs to type_id 's class.

  • G_TYPE_CHECK_CLASS_TYPE( klass, type_id )

    Returns TRUE if klass belongs to type_id 's class.

  • G_TYPE_INSTANCE_GET_CLASS( object, klass )

    Returns the class structure for object , casted as the C type class_name .

Note  

These macros use the parameter klass instead of class . That's because class is a reserved C++ keyword; if you tried to use one of these macros in C++ source code, your program would not compile. The GLib, GTK+, and GNOME source distributions reflect this. However, with parameters to functions in regular C code, this is not a problem, because you wouldn't use a C++ compiler on that. You can always use class if you never plan to use C++, or if you just feel like being mean to C++ people.

To keep C++ compatibility at link time, use G_BEGIN_DECLS and G_END_DECLS to encapsulate your C code:

 G_BEGIN_DECLS   << header code >> G_END_DECLS 

2.2.3 Initializing Type Identifiers

The previous section made many references to the GObject class type identifier, with the macro TYPE_MEDIA expanding to a media_get_type() call to return the Media class type identifier. As mentioned before, the type identifier is an actual piece of runtime data that conforms to the GType standards.

This is perhaps easier to understand when you look at the actual definition of media_get_type() :

 GType media_get_type(void) {   static GType media_type = 0;   if (!media_type)   {      static const GTypeInfo media_info = {         sizeof(MediaClass),                /* class structure size */         NULL,                              /* base class initializer */         NULL,                              /* base class finalizer */         (GClassInitFunc)media_class_init,  /* class initializer */         NULL,                              /* class finalizer */         NULL,                              /* class data */         sizeof(Media),                     /* instance structure size */         16,                                /* preallocated instances */         NULL,                              /* instance initializer */         NULL                               /* function table */      };      media_type = g_type_register_static(             G_TYPE_OBJECT,                 /* parent class */             "Media",                       /* type name */             &media_info,                   /* GTypeInfo struct (above) */             0);                            /* flags */   }   return media_type; } 

The return value of media_get_type() is the C type GType ” a type of a type (if that makes any sense). This function returns the same class type identifier every time, because the variable used to store and return the class identifier has a static declaration ( media_type ). Because of the if statement, media_get_type() does some work to initialize media_type upon first invocation, but needs to return the value of this variable only on subsequent calls.

media_type gets its actual value from

 g_type_register_static(  parent_id, name, type_info, options  ) 

These arguments are as follows :

  • parent_id : The parent type identifier. In this case, it's the GObject base class ( G_TYPE_OBJECT ).

  • name : A name (a string) for the type.

  • type_info : A GTypeInfo structure containing class details (see the following paragraphs).

  • options : Option flags (using bitwise OR), or zero if you don't need any. For example, G_TYPE_FLAG_ABSTRACT indicates an abstract class.

Warning  

The type name must be at least three characters long.

The fields of the GTypeInfo structure are as follows (in this order):

  • class_size ( guint16 ): Size of the class structure. In this case, it's easy: sizeof(MediaClass) .

  • base_init ( GBaseInitFunc ): The base class initializer. When you create a new class, GObject calls the base initializers of all classes in the hierarchy chain leading up from the class. This is normally necessary only if there is some dynamically allocated part of the class ( not object) that GObject must copy to a derived class, and therefore, you need it only when your class has such data and is the parent of some other class. Normally, it isn't necessary, and you can use NULL .

  • base_finalize ( GBaseFinalizeFunc ): The base class finalizer; essentially , the reverse operation of the base class initializer. You almost certainly need it when you have a base class initializer; otherwise, use NULL .

  • class_init ( GClassInitFunc ): The class initializer. This function prepares class variables , in particular, properties (see Sections 2.4) and signals (Section 2.6). Note that this is not the constructor for the objects; however, the property definitions in the class initializer set up many of the defaults for the constructor. The class initializer appears throughout this chapter; it is instrumental in figuring out how classes work.

  • class_finalize ( GClassFinalizeFunc ): The class finalizer. You normally won't need to do anything when GObject gets rid of a class, unless you have a very complicated mechanism for saving the state over program invocations.

  • class_data ( gconstpointer ): GObject passes the class data to the class initializer and class finalizer. One example of a situation in which you might use this function is when you want to use the same initializer for several classes. Of course, that could make things even more complicated than they already are.

  • instance_size ( guint16 ): The size of the instance structure is an allocated object structure's memory requirement; sizeof(Media) shown earlier in the code is typical.

  • n_preallocs ( guint16 ): The number of instances to preallocate depends on the number of instances that you think a typical program will use. If you specify 0, GObject allocates memory when it needs to. Be careful when using this function, because you can waste a lot of memory if you go nuts with many different kinds of classes.

  • instance_init ( GInstanceInitFunc ): The instance initializer runs every time you create a new object of the class. It's not strictly necessary, because the constructor is also at your disposal. This field is present because it is a part of GType; GObject doesn't need it.

  • value_table ( const GTypeValueTable * ): This function table can map certain kinds of values to functions, and it is really important only if you're doing something very involved with the type system, such as creating your own object system. This book does not cover that topic, and it's probably best if you forget that this function even exists.

Here are the type definitions for the various function prototypes you just saw:

 typedef void (*GBaseInitFunc)      (gpointer g_class); typedef void (*GBaseFinalizeFunc)  (gpointer g_class); typedef void (*GClassInitFunc)     (gpointer g_class, gpointer class_data); typedef void (*GClassFinalizeFunc) (gpointer g_class, gpointer class_data); typedef void (*GInstanceInitFunc)  (GTypeInstance *instance, gpointer g_class); 
Note  

You can get rid of the GTypeInfo structure as soon as you're finished with it ” g_type_register_static() makes a complete copy for itself. So in the media example, you could free up any dynamically allocated parts of media_info that you like.

Try not to get tangled up in the initializers and finalizers. Normally, you won't be dealing with dynamically allocated data in the classes themselves , so you won't need anything except a class initializer; you'll see that in Section 2.4.1. You should not need to bother with the instance initializer, because you can use properties.

2.2.4 The Base Class: GObject

This section illustrates the makeup of the base class, GObject . All GObject classes inherit from this class. Not only do you need some of the utility macros to create new classes, but it helps to know the methods that you'll come across later.

Macros

In Section 2.2.2, you defined several utility macros for the Media class. Here are the GObject versions:

  • G_TYPE_OBJECT returns GObject 's type identifier. Don't confuse this with G_OBJECT_TYPE .

  • G_OBJECT( object ) casts object to the GObject instance structure.

  • G_OBJECT_CLASS( klass ) casts an object class structure klass to the GObjectClass class structure.

  • G_IS_OBJECT( object ) returns TRUE if the object parameter is an instance of a GObject . This should return TRUE for any object that you define with GObject, unless you're very daring and decide to make your own base object.

  • G_IS_OBJECT_CLASS( klass ) returns TRUE if klass is a class structure. It should return TRUE for any class structure within the GObject system.

  • G_OBJECT_GET_CLASS( object ) returns the class structure ( GObjectClass ) corresponding to any instance structure.

Of these, you will encounter G_TYPE_OBJECT and G_OBJECT() the most: G_TYPE_OBJECT when you need to know the type identifier of GObject (for instance, when defining a class like Media ), and G_OBJECT() when you need to pass an object instance as a GObject instance to a function.

Note  

Many common functions that take GObject parameters don't require class type casts, but rather just expect an untyped gpointer pointer and do the casting work on their own. Although this approach isn't terribly consistent, it can save you an awful lot of typing. The functions that do this include g_object_get() , g_object_set() , g_object_ref() , g_object_unref() , and the entire g_signal_connect() family.

Base Class Methods

Here is the part of the GObject class structure that contains public methods and a signal handler prototype. You will see some of these time and again. (This definition comes straight from the gobject.h header file.)

 typedef struct _GObjectClass GObjectClass; typedef struct _GObjectConstructParam GObjectConstructParam;   << ... >> struct _GObjectClass {   GTypeClass g_type_class;   << ... >>   /* public overridable methods */   GObject* (*constructor) (GType type,                            guint n_construct_properties,                            GObjectConstructParam *construct_properties);   void (*set_property) (GObject *object,                         guint property_id,                         const GValue *value,                         GParamSpec *pspec);   void (*get_property) (GObject *object,                         guint property_id,                         GValue *value,                         GParamSpec *pspec);   void (*dispose) (GObject *object);   void (*finalize) (GObject *object);   << ... >>   /* signals/handlers */   void (*notify) (GObject *object, GParamSpec *pspec);   << ... >> }; struct _GObjectConstructParam {   GParamSpec *pspec;   GValue     *value; }; 

The methods are as follows:

  • constructor : This is the object's constructor, called when you create an instance of a class. It takes a type identifier parameter ( type ), the number of properties to create ( n_construct_properties ), and an array of property descriptions ( construct_properties ). You'll read about properties and the GValue s that they employ in Section 2.4.

    The constructor creates the object and initializes its data. If you want to create your own constructor, you should always run the constructor of the parent class first and then extend the resulting parent class object by yourself, so that your object isn't missing anything. Keep in mind, though, that you do not usually need to make your own constructors; there is a further example of inheritance in Section 2.7 that provides an alternative.

  • set_property : Writer function for properties.

  • get_property : Reader function for properties.

  • dispose : The destructor; GObject calls this when removing an object that is no longer in use (see Section 2.5.2). Destructors take a single parameter: the object to destroy. Destructors clean up after signal handlers and sort out other internal matters.

  • finalize : GObject calls the finalizer when an object's reference count goes to zero (again, see Section 2.5.2), before it gets around to calling the destructor. Use this for housekeeping functions that require immediate attention, such as removing dynamically allocated memory. You should always call an object's parent finalizer at the end of your own finalizers. After running the finalizer, the object is officially dead, and you should do nothing more with the data.

  • notify : GObject calls this special method for property change signals (see Section 2.6). There is no reasonable default, and you should not touch this (that is, inherit notify from the parent).

[1] It might be some consolation that C++ and most scripting languages have GNOME bindings.




The Official GNOME 2 Developers Guide
The Official GNOME 2 Developers Guide
ISBN: 1593270305
EAN: 2147483647
Year: 2004
Pages: 108

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net