You are here: start » gobjectutorial

Introduction to GObject

(for Java Programmers?)

(C) 2007-2008 Amos Brocco (amos.brocco _at_ unifr.ch)

Disclaimer: I'm writing this tutorial in my spare time. I give no guarantees about its correctness, and I assume no responsability for its contents. I release this document under the GNU Free Documentation License. If you find errors (I'm sure you will) don't hesitate to drop me an email (I'll appreciate)! Final note of this disclaimer: this tutorial is far from finished, many parts are missing.

This tutorial will show you how to use the GObject object oriented system. If you don't really know what GObject is please take a look at this page. If you don't really want to read all that stuff, GObject is an object system for C (yes, plain old C!). GObject only depends on the standard C library (libc) and GLib.

How to define a class

There are some things to remember (especially if you come from Java or C++):

  • there is no (operator) overloading in C
  • runtime polymorphism in C/GObject requires explicit upcasting
  • encapsulation really is in your hands… very little is enforced by the system

First steps

So, you want to use GObject… why not start by defining a class? In object-oriented languages special constructs like class allow to define a class quite easily and with few lines of code. For example, in Java you define a class with just:

package mypackage;
 
class MyClass {
 
}

With GObject a class definition is composed of different parts:

  • Definition of the instance and class structures (fields, public methods, etc.)
  • Definition of some utility macros
  • Definition of some “helper” functions
  • Definition of the class and instance initialization functions (constructor)
  • Definition of the instance finalization functions (destructor)
  • Public methods implementation
  • Private methods implementation

It seems quite a lot of work, doesn't it? In reality, much of that can be just resumed to a “copy-paste” from a template: lot of smoke, without a fire.

Definition of the type and class structure

The type structure for the class is simply a C-struct that usually just contains the structure of the parent class (which needs to be the first item in the structure) along with pointers to functions (what would be the class methods). Everything about the definition of the class, as well as the boilerplate code we'll see further down in this document is usually put in the header file. So, lets go back to our class… suppose that we want to define a class named Example…so we would write:

typedef struct _ExampleClass
{
	GObjectClass parent_class;
 
        /* Overridable methods */
	void 	(*write)   (Example* self, gchar* text);
	gint 	(*get_char_count)   (Example* self);	
 
} ExampleClass;

Notice the GObjectClass structure that's inside our class structure. The GObjectClass is the class of the base object in GObject; every class derives from GObject, just like every Java class inherits from Object. What about the fact that we define a GObjectClass structure inside our class structure? In fact, with GObject every class structure contains the class structure of its parent class… so if you have many levels of inheritance you'll end up with a matrioska-like structure (the same can be said about instance structures). Thanks to this structure, our newly created class inherits all the methods defined by the parent class.

In the same way, the type structure for the instance contains the parent instance (the first item in the structure), and the public fields definition (public variables). Whereas the class structures contains pointers to the class methods, which will point to the same functions for every instance of our class, the instance structure contains the public fields whose value is specific for every object we create. Note that, in order to define private variables another structure (described later) is used.

typedef struct _Example
{
	GObject parent_instance;
 
        gint value;
} Example;

By encapsulating a structure of the parent instance, we can also access public fields of our parent classes. Small parenthesis about private fields: there are various ways to define private fields, I'll explain only one way (so that you won't be confused by too many ways to do things). The actual definition of the private structure is usually found in the .c file and not in the (public) header file:

typedef struct _ExamplePrivate ExamplePrivate;
 
struct _ExamplePrivate {
	GQueue*		queue;
	guint		index;
	gboolean 	active;
};

The name of the structure is not enforced, you can choose whatever you want, but it is useful to add the “…Private” tag just to be clear with the fact that this is the private fields structure. We'll see later how to connect this structure with our class and how to access private fields.

Definition of some utility macros

In order to ease the usage of the class, some additional macros are added to the header file:

The first macro, just returns a value corresponding to the type of our class:

#define TYPE_EXAMPLE (example_get_type())

This is used as a tag for our class (you'll see again this tag when I'll show you how to create a new instance of our Example class).

Another macro is used to up-cast an instance (object) of a subclass to the Example type:

#define EXAMPLE(object) \
 (G_TYPE_CHECK_INSTANCE_CAST((object), TYPE_EXAMPLE, Example))

This is mostly useful if you want to pass an object of a sub-class of Example to methods defined by Example: you can view this as an object-system-aware cast. Only valid casts will work: for example, you would get an error if you try to up-cast an object of class Banana to Example with “EXAMPLE(bananaobj)” if Banana is not a subclass of Example

The same way, we can also up-cast a subclass to the Example class:

#define EXAMPLE_CLASS(klass) \
 (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_EXAMPLE, ExampleClass))

To test whether an object is an instance of the Example class use:

#define IS_EXAMPLE(object) \
 (G_TYPE_CHECK_INSTANCE_TYPE((object), TYPE_EXAMPLE))

This macro acts like the instanceof function in Java. To test if a given class is a subclass of Example use:

#define IS_EXAMPLE_CLASS(klass) \
 (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_EXAMPLE))

Finally, we can obtain the actual class structure (which can be a subclass of Example) from an object (class instance):

#define EXAMPLE_GET_CLASS(object) \
 (G_TYPE_INSTANCE_GET_CLASS((object), TYPE_EXAMPLE, ExampleClass))

All these macros are to be put in the header file, because they should be available to users of your class. The name of these macros is always the same, it's just the class name that changes. Please note that it is suggested not to use variables named class, because it is a C++ reserved word: I know you are using a C compiler, but think about portability and interoperability with C++, and use klass instead. In the .c file, an additional macro is useful to access private data:

#define EXAMPLE_GET_PRIVATE(o)  \
   (G_TYPE_INSTANCE_GET_PRIVATE ((o), TYPE_EXAMPLE, ExamplePrivate))

In the header file we can also add the prototypes of public methods, for example:

gchar* 		example_get_last_message (Example* ex);
void 		example_add_message (Example* ex, gchar* message);
void 	        example_write (Example* self, gchar* text);
gint 	        example_get_char_count (Example* self);	

These are the methods that we can use to access the Example class. Don't forget to implement these methods in the .c file!

Definition of the object type itself

In the .c file, we need to insert the functions that will register our type in the type system. This is important and required to make our class work with the type system. It is really easy to do so:

G_DEFINE_TYPE (Example, example, G_TYPE_OBJECT);

Where “Example” is the name of the class, 'example' is the prefix given to class methods, and G_TYPE_OBJECT is the parent class to be used.

The G_DEFINE_TYPE macro will create the example_get_type function and the prototypes for two initialization functions (class and instance initialization):

static void     example_init       (Example     *self);
static void     example_class_init (ExampleClass *klass);

as well as a static variable pointing to the parent class:

static gpointer example_parent_class = NULL;

I will not discuss what's inside the …_get_type() function. If you are interested have a look at the GObject documentation, but really, it is not necessary to know it unless you want to do advanced tricks with GObject…

Definition of the class and instance initialization functions

The class initialization function is used to hook properties to the class (see later in this page), as well as to hook finalization functions, add the private structure to the class and initialize the parent class pointer. The G_DEFINE_TYPE macro already creates an hidden class init that initializes the parent class pointer. What you need to implement is the exposed class init (example_class_init). The prototypes for these functions are fixed, and are created by the G_DEFINE_TYPE macro, so you just need to provide the implementation somewhere in your code.

static void
example_class_init (ExampleClass* klass)
{
	GObjectClass	*g_object_class;
 
	/* Add private structure */
	g_type_class_add_private (klass, sizeof (ExamplePrivate));
 
	/* Get the parent gobject class */
	g_object_class = G_OBJECT_CLASS(klass);
 
        /* Hook overridable methods */
        klass->write = real_example_write;
        klass->get_char_count = real_get_char_count;
 
	/* Hook finalization functions */
	g_object_class->dispose = example_dispose; /* instance destructor, reverse of init */
	g_object_class->finalize = example_finalize; /* class finalization, reverse of class init */
}

The instance destructor and class finalization functions are called by the gtype system when the object is destroyed, and are needed to release the memory allocated by the instance and class initialization functions. The prototypes for these functions will be discussed later.

The instance initialization (also called constructor) function is used to initialize public and private data:

static void
example_init (Example* self)
{
        /* Retrieve the private data structure */
	ExamplePrivate* priv = EXAMPLE_GET_PRIVATE (instance);
        /* Alternatively you could do : */
        // ExamplePrivate* priv = self->priv;
        // ...as noted by Christian Dywan
 
        /* Initialize public fields */
        self->value = 0;
 
	/* Initialize private fields */
	priv->queue = g_queue_new() ;
        priv->index = 0;
        priv->active = TRUE;
}

We use the EXAMPLE_GET_PRIVATE macro to retrieve the private structure. It would be possible to save this pointer into a variable in the Example structure, to avoid the need to call this …_GET_PRIVATE thing every time and also to speed up execution, but this does not always work as expected: if you try to access the private data structure from a pointer in the instance structure from a function defined by a parent class you'll end up with the wrong private structure (that is, private data of the subclass and not private data of the parent class).

So, at least in overridable methods, to retrieve the correct private data structure it is preferred to use the *_GET_PRIVATE macro instead of relying on the stored pointer in the instance structure. By using the *_GET_PRIVATE macro you are sure that the structure you get is the one associated with the actual class (the one the code was written for), even if you pass an instance of a subclass that was upcasted.

Definition of the protected fields

In GObject there are no such things as protected fields. The best you can do is to declare them the public structure and eventually marked with a big warning such as /* Protected */. Users of your class should be warned not to use such fields.

Object finalization functions

Destruction of an object is carried out by two functions, commonly called dispose and finalize. The need for two functions comes to solve the problem of circular references. The finalize function is called when the object is not used anymore (that is, the reference count to this object reaches zero). The prototype for this function is the following:

static void example_finalize (GObject* object);

The finalization normally does nothing, and the call is simply forwarded to the parent class:

static void
example_finalize (GObject* object)
{
	/* Reverse what was allocated by class init */
	G_OBJECT_CLASS (example_parent_class)->finalize (object);
}

The dispose function is normally dedicated to deallocate memory and clean up what was done by the initialization function:

static void example_dispose (GObject* object);

In the implementation, just deallocate the memory and then call the parent instance dispose method:

static void
example_dispose (GObject* object)
{
	/* Reverse what was allocated by instance init */
 
	Example *self = EXAMPLE (object);
	ExamplePrivate* priv;
 
	priv = EXAMPLE_GET_PRIVATE (self);
 
	g_queue_foreach (priv->queue, (GFunc) g_object_unref, NULL);
	g_queue_free (priv->queue);
 
	G_OBJECT_CLASS (example_parent_class)->dispose (object);
}

Public methods implementation

Public methods implementations go in the .c file. It is possible to define overridable methods that can be re-defined by subclasses by adding function pointers in the class structure, and a shortcut function. Non overridable methods ('final' methods in Java) can be defined with just a function. As you may notice, you need to pass an instance of the Example class to the method (because there is no way to do things like, for example in Java, object.method(…)).

Let's start with final methods, as they require only a function to be defined:

gchar* example_get_last_message (Example* ex)
{
   g_return_val_if_fail (IS_EXAMPLE (ex), NULL);
 
   /* do things */
 
   return ... ;
}

Note the use of g_return_val_if_fail that exits the function and returns NULL if the 'ex' argument is not of type Example. Clearly a prototype for these function should go in the header file!

void example_add_message (Example* ex, gchar* message)
{
   g_return_if_fail (IS_EXAMPLE (ex));
 
   /* do things */
}

The g_return_if_fail is similar but does not return any value.

For the overridable methods, you need to define the actual implementation and hook it to the function pointers in the class init function. Actual implementation is normally prefixed with a real_ tag to mark that it's the implementation for that class.

static void
real_example_write (Example* self, gchar* text)
{
...
}
 
static gint
real_example_get_char_count (Example* self)
{
...
}

Overridable methods can also be declared virtual by setting them to NULL in the class init function. To call overridable methods you can either use the function pointer directly or (better idea) create access functions that will use whatever implementation the class or a subclass may have provided without having to deal with macros, types and function pointers:

void 
example_write (Example* self, gchar* text)
{
	return EXAMPLE_CLASS (self)->write (self, text);
}

gint
example_get_char_count (Example* self)
{
	return EXAMPLE_CLASS (self)->get_char_count (self);
}

For each method, you need to implement pointers go in the class definition, as seen before:

typedef struct _ExampleClass
{
	GObjectClass parent_class;
 
        /* Overridable methods */
	void 	(*write)   (Example* self, gchar* text);
	gint 	(*get_char_count)   (Example* self);
 
} ExampleClass;

Hooking of these function pointers with the “real” methods is done in the class init function:

(...)
 /* Hook overridable methods */
 klass->write = real_example_write;
 klass->get_char_count = real_get_char_count;

Private methods implementation

Private methods are implemented in the .c file by declaring them as static. Remember that if you need to access private structures from within private methods you need to pass a reference to the current object to them.

How create, use and destroy an object

The first thing to do is to initialize the type system (this needs to be done only once and is valid for all your application):

g_type_init ();

Then, it is possible to create an instance of the Example class:

Example* 	myex;
myex = g_object_new (TYPE_EXAMPLE, NULL);

The NULL is important because it is possible to initialize properties (discussed later) upon creation of the object; the NULL is used to know when the last property is defined.

It is possible to define a constructor that wraps the g_object_new (commonly you'll use typename_new):

Example* example_new (gchar* initial_message)
{
  Example* 	myex;
  myex = g_object_new (TYPE_EXAMPLE, NULL);
  example_add_message (myex, initial_message);
  return myex;
}

Note that, as C does not support overloading, if you want to create different “custom” constructors, you'll need to name them differently (for example _new, _new0, _new1, etc.).

Object instantiation will then be performed by using:

Example* myex;
myex = example_new ("First message");

To use the object just call one of the public methods, for example:

example_add_message (myex, "Message");

If your class inherits from another class, for example Example is a subclass of Tutorial, you can call methods defined by Tutorial and pass the Example object by casting it:

tutorial_somemethod (TUTORIAL (myex), "some parameter");

In the same way, you can access public fields of parent classes:

TUTORIAL (myex)->somepublicfield = 1;

Finally, to destroy the object call g_object_unref:

g_object_unref (myex);

Actually, g_object_unref only decrements the reference count to an object. When the reference count reaches zero, destructors are called. This is the way GObject help managing object memory.

Subclassing

Super(man) we need you!

In Java you can access public fields and methods the parent class by mean of the super keyword. With GObject it is a little bit trickier, but nonetheless possible.

For public fields it is not really a big problem, just up-cast your instance to the parent instance and you will be able to access its public fields. For methods it is not so simple, because if you override a method you overwrite (sorry for the word-trick!) the function pointer in the class definition. The solution to this problem is to save the original function pointer before overriding the method in the class init function.

To uniform the code, I've created a macro that does that. I called it GSuperMacro, and allows you to save a method for super, override that method and call it in an almost clean way:

/*
 * GSuperMacro.h
 *
 * Macros for Java-like 'super' in GObject
 *
 * Copyright (C) 2007 Amos Brocco
 *
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 *
 */
#ifndef GSUPERMACRO_H_
#define GSUPERMACRO_H_
 
/*
 * Defines a static variable to hold the pointer to the parent's method M of type T
 * with signature P.
 * Example:
 * G_SUPER_METHOD(int,my_method,(int a, char* b));
 */
#define G_SUPER_METHOD(T,M,P) \
	static T (* g____super_##M) P;
 
/*
 * Hooks a method with the actual implementation
 * C is the class structure,
 * M is the method name, and
 * I is the name of the actual implementation
 * Example:
 * G_HOOK_METHOD(klass,my_method,real_my_method);
 */
#define G_HOOK_METHOD(C,M,I) \
	C -> M = I;
 
/*
 * Overrides a method while saving the parent's version in the static variable
 * C is the parent class structure,
 * M is the method name, and 
 * I the name of the actual implementation
 * Note: you need to use G_PRESERVE_SUPER_METHOD for every overridden method !
 * Example:
 * G_OVERRIDE_METHOD(klass,my_method,real_my_method);
 */
#define G_OVERRIDE_METHOD(C,M,I) \
	g____super_##M = C -> M; \
	C -> M = I;	
 
/*
 * Calls the super implementation of a method
 * M is the method name,
 * P are the parameters to be passed
 * Example: G_SUPER(my_method,(x,y,z));
 */
#define G_SUPER(M,P) \
	g____super_##M P;
 
#endif /*GSUPERMACRO_H_*/	

How to define, implement, and use an interface

Interfaces are like classes, but with all abstract methods. In this example we will create an interface for writable object called Writable.

Defining an interface

First, we need to define the Writable interface type in an header file:

typedef struct _Writable Writable;
 
typedef struct _WritableInterface
{
	GTypeInterface base_interface;
 
        /* Methods we want to declare for the interface */
	void 	(*write)   (Writable* object, gchar* text);
	gint 	(*get_char_count)   (Writable* object);	
} WritableInterface;

As with classes, some convenience macros are defined:

#define TYPE_WRITABLE (writable_get_type())
 
#define WRITABLE(object) \
	(G_TYPE_CHECK_INSTANCE_CAST((object), TYPE_WRITABLE, Writable))
 
#define IS_WRITABLE(object) \
	(G_TYPE_CHECK_INSTANCE_TYPE((object), TYPE_WRITABLE))
 
#define WRITABLE_GET_INTERFACE(object) \
	(G_TYPE_INSTANCE_GET_INTERFACE((object), TYPE_WRITABLE, WritableInterface))

For the implementation, in the .c file, what we actually do is adding some code to let the system know about our interface, and writing functions hooks that will be used to call the interface methods given an object implementing the interface:

static void writable_base_init (gpointer g_class);
 
GType 
writable_get_type (void)
{
	static GType writable_type = 0;
 
	if (!writable_type) {
		static const GTypeInfo writable_info = {
			sizeof(WritableInterface),
                        NULL,   /* base_init */
			NULL,   /* base_finalize */
      			writable_init,   /* class_init */
			NULL,   /* class_finalize */
			NULL,   /* class_data */
      			0,
      			0,      /* n_preallocs */
      			NULL    /* instance_init */
		};
 
		writable_type = g_type_register_static (G_TYPE_INTERFACE,
							"Writable",
							&writable_info,
							0);
	}
 
	return writable_type;
}

As you see, there is much more code than for classes: this is because the get_type fuction is normally expanded from the G_DEFINE_TYPE macro. All this code can be avoided by using a macro adapted from another one written by Tiago Cogumbreiro called G_DEFINE_INTERFACE_TYPE.

#ifndef G_DEFINE_INTERFACE_TYPE
/*
 * Convenience macro to ease the interface definition. Works like G_DEFINE_TYPE.
 * Copyright (C) 2004 Tiago Cogumbreiro <cogumbreiro@users.sf.net>
 * Fixes (C) 2007 Amos Brocco <amos.brocco@unifr.ch>
 * Released under the GNU/LGPL license, version 2.
 */
#define G_DEFINE_INTERFACE_TYPE(TN,t_n)                     G_DEFINE_INTERFACE_TYPE_WITH_CODE (TN, t_n, {})
#define G_DEFINE_INTERFACE_TYPE_WITH_CODE(TypeName, type_name, CODE) \
static void     type_name##_init        (TypeName##Interface *iface); \
static void     type_name##_finalize    (TypeName##Interface *iface); \
GType \
type_name##_get_type (void) \
{ \
  static GType g_define_interface_id = 0; \
  if (G_UNLIKELY (g_define_interface_id == 0)) \
    { \
      static const GTypeInfo g_define_type_info = { \
        sizeof (TypeName##Interface), \
        (GBaseInitFunc) type_name##_init, \
        (GBaseFinalizeFunc) type_name##_finalize, \
      }; \
      g_define_interface_id = g_type_register_static (G_TYPE_INTERFACE, #TypeName, &g_define_type_info, 0); \
    } \
    { CODE ;} \
  return g_define_interface_id; \
}
#endif /* G_DEFINE_INTERFACE_TYPE */

With this macro, the above code would resume to:

 G_DEFINE_INTERFACE_TYPE (Writable, writable);

Quite more readable, isn't it? For the initialization there is still another thing to do, implement the interface init function:

static void
writable_init (WritableInterface* iface)
{
  static gboolean initialized = FALSE;
 
  if (!initialized) {
    initialized = TRUE;
  }
}

If you used the macro you would still have to declare the class finalizer (that will, for example, unregister signals)

static void
writable_finalize (WritableInterface* iface)
{
  /* Cleanup what was done by init */
}

This code just ensures that the interface is initialized only once. So, now lets define some helper functions for the methods that our interface provides:

void 
writable_write (Writable *self, gchar* text)
{
	WRITABLE_GET_INTERFACE (self)->write (self, text);
}
 
gint
writable_get_char_count (Undoable *self)
{
	return WRITABLE_GET_INTERFACE (self)->get_char_count (self);
}

As you see, the only thing we do inside these functions is to extract the actual object and call the implementation it defines for the method.

Implementing an interface

To implement an interface you need to change the G_DEFINE_TYPE macro to G_DEFINE_TYPE_EXTENDED, in order to declare that your type implements one or more interfaces:

G_DEFINE_TYPE_EXTENDED (Example, example, G_TYPE_OBJECT, 0, G_IMPLEMENT_INTERFACE (TYPE_WRITABLE, 
                                               example_writable_interface_init);

What you need to do is to implement example_writable_interface_init, that will bind interface methods:

/* Prototypes */
void example_writable_write (Writable* self, gchar* text);
gint example_writable_get_char_count (Writable* self);
 
static void
example_writable_interface_init (gpointer g_iface, gpointer iface_data)
{
	/* Bind interface methods with actual implementation */
	WritableInterface* iface = (WritableInterface* ) g_iface;
	iface->write = (void (*) (Writable* self, gchar* text)) example_writable_write;
	iface->get_char_count = (gint (*) (Writable* self)) example_writable_get_char_count;
}

With the above code, your binding the interface methods (on the left) with actual implementation (on the right): for example, the interface method write will be associated with example_writable_write. Note the cast you do from Example* to WritableInterface*. You then proceed by implementing the methods.

Calling an interface method

To call an interface method, just call the methods you defined in the interface “implementation”, for example:

writable_write (WRITABLE (myex), "This is the message");

Implementing multiple interfaces

To implement multiple interfaces, just add G_IMPLEMENT_INTERFACE calls in G_DEFINE_TYPE_EXTENDED. Don't forget to also write the corresponding interface initialization code!

Closures

To encapsulate callbacks from asynchronous operations, an abstraction, called closure, is used. Closure contain a function pointer, a pointer to some data to be used by the callback function and a pointer to a destructor function that will be called when the structure needs to be freed.

Closures can be used to invoke either C code or some other language by mean of marshallers. For C closures, the three functions are available. To create a new closure which uses user_data as the last parameter use:

GClosure* g_cclosure_new (GCallback        callback_func,
                          gpointer         user_data,
                          GClosureNotify   destroy_data);

To create a new closure which passes user_data as first parameter use:

GClosure* g_cclosure_new_swap (GCallback        callback_func,
                               gpointer         user_data,
                               GClosureNotify   destroy_data);

Finally, to create a new closure which invokes the function found at the offset struct_offset in the class structure of the interface or classed type identified by itype use:

GClosure* g_signal_type_cclosure_new (GType  itype,
                                      guint  struct_offset);

Signals

(…future work…)

gobjectutorial/start.txt · Last modified: 2024/05/27 12:22 by 127.0.0.1
Kleine Websites, die ein Wiki als CMS verwenden.de