How to implement a type by inheriting

About

This document describes how to inherit a type defined in the tinymail framework. This documentation is useful for both application developers and people who are planning to contribute to the tinymail framework itself.

The following websites and manuals describe the GObject type system in depth:

You can use all of the information that you can find in those three documents in the Tinymail framework too. Tinymail is, indeed, GObject based.

Check out how to add a type in case you are adding a type to the Tinymail framework.

By examle

We will assume that we are going to inherit a TnyCamelFolder

Create a tny-specific-camel-folder.h in libtinymail-camel

#ifndef TNY_CAMEL_SPECIFIC_FOLDER_H
#define TNY_CAMEL_SPECIFIC_FOLDER_H
/* Above ifdef magic is to make this header unique per .c file compilation */

/* GObject and glib includes */
#include <glib.h>
#include <glib-object.h>

/* You will typically need at least the header of the type from which you
are inheriting here */

#include <tny-camel-folder.h>

/* The DECLS stuff makes this header usable for C++ compilers */
G_BEGIN_DECLS

/* The typical GObject cpp crap goes like this */
#define TNY_TYPE_CAMEL_SPECIFIC_FOLDER  (tny_camel_specific_folder_get_type ())
#define TNY_CAMEL_SPECIFIC_FOLDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
	TNY_TYPE_CAMEL_SPECIFIC_FOLDER, TnyCamelSPECIFICFolder))
#define TNY_CAMEL_SPECIFIC_FOLDER_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), 
	TNY_TYPE_CAMEL_SPECIFIC_FOLDER, TnyCamelSPECIFICFolderClass))
#define TNY_IS_CAMEL_SPECIFIC_FOLDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
	TNY_TYPE_CAMEL_SPECIFIC_FOLDER))
#define TNY_IS_CAMEL_SPECIFIC_FOLDER_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), 
	TNY_TYPE_CAMEL_SPECIFIC_FOLDER))
#define TNY_CAMEL_SPECIFIC_FOLDER_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), 
	TNY_TYPE_CAMEL_SPECIFIC_FOLDER, TnyCamelSPECIFICFolderClass))

/* This is the (forward) typedef of your new type */
typedef struct _TnyCamelSPECIFICFolder TnyCamelSPECIFICFolder;
typedef struct _TnyCamelSPECIFICFolderClass TnyCamelSPECIFICFolderClass;

struct _TnyCamelSPECIFICFolder
{
	/* This is type parent type from which you are inheriting. It
	   MUST be the first member of the struct */
	TnyCamelFolder parent;
};

struct _TnyCamelSPECIFICFolderClass 
{
	/* This is type parent's class type from which you are inheriting.
	   it MUST be the first member of the struct. */
	TnyCamelFolderClass parent;

	/* it's a good idea to put "signals", "Virtual methods", 
	"Abstract methods" as comments above these function pointers.

	Virtual basically means that somebody who inherits your type can
	override your method. 

	We will start using this function pointer in the example where we
	will override the method (not yet in the upcoming example which
	implements this type, but in the example after that) */

	/* Virtual methods */
	void (*expunge_func) (TnyFolder *self);

};

GType tny_camel_specific_folder_get_type (void);

/* It's advisable to always return the interface type here */
TnyFolder* tny_camel_specific_folder_new (void);

G_END_DECLS

#endif

Create a tny-specific-camel-folder.c in libtinymail-camel

#include <config.h>
#include <glib/gi18n-lib.h>
#include <glib.h>
#include <string.h>


#include <tny-folder.h>
#include <tny-folder-store.h>
#include <tny-camel-folder.h>
#include <tny-camel-specific-folder.h>

#include <camel/camel.h>
#include <camel/camel-session.h>
#include <camel/camel-store.h>

#include <tny-folder.h>

#include "tny-camel-account-priv.h"
#include "tny-camel-store-account-priv.h"
#include "tny-camel-folder-priv.h"
#include "tny-camel-common-priv.h"

#include <tny-camel-shared.h>
#include <tny-account-store.h>


static GObjectClass *parent_class = NULL;


/**
 * tny_camel_specific_folder_new:
 * 
 *
 * Return value: A new SPECIFIC #TnyFolder instance implemented for Camel
 **/
TnyFolder*
tny_camel_specific_folder_new (void)
{
	TnyCamelSPECIFICFolder *self = g_object_new (TNY_TYPE_CAMEL_SPECIFIC_FOLDER, NULL);

	return TNY_FOLDER (self);
}

static void
tny_camel_specific_folder_finalize (GObject *object)
{
    
	(*parent_class->finalize) (object);

	return;
}

static void 
tny_camel_specific_folder_class_init (TnyCamelSPECIFICFolderClass *class)
{
	GObjectClass *object_class;

	parent_class = g_type_class_peek_parent (class);
	object_class = (GObjectClass*) class;

	object_class->finalize = tny_camel_specific_folder_finalize;

	return;
}


static void
tny_camel_specific_folder_instance_init (GTypeInstance *instance, gpointer g_class)
{
	return;
}

GType 
tny_camel_specific_folder_get_type (void)
{
	static GType type = 0;
	if (G_UNLIKELY(type == 0))
	{
		static const GTypeInfo info = 
		{
		  sizeof (TnyCamelSPECIFICFolderClass),
		  NULL,   /* base_init */
		  NULL,   /* base_finalize */
		  (GClassInitFunc) tny_camel_specific_folder_class_init,   /* class_init */
		  NULL,   /* class_finalize */
		  NULL,   /* class_data */
		  sizeof (TnyCamelSPECIFICFolder),
		  0,      /* n_preallocs */
		  tny_camel_specific_folder_instance_init    /* instance_init */
		};
	    
		type = g_type_register_static (TNY_TYPE_CAMEL_FOLDER,
			"TnyCamelSPECIFICFolder",
			&info, 0);	    
	}

	return type;
}

Adapt the Makefile.am of libtinymail-camel

libtinymail_camel_1_0_headers =     \
+       tny-camel-specific-folder.h \
        ...

libtinymail_camel_1_0_la_SOURCES = \
+       tny-camel-specific-folder.c \
        ...

The TnyCamelFolder type is a virtual class

This means that it has virtual methods that can be overwritten. The virtual methods can be found in the tny-camel-folder.h file in libtinymail-camel.

Look at the public .h file to get the class structure of the type. For example the TnyCamelFolder, which has its class structure defined in libtinymail-camel/tny-camel-folder.h, looks like this:

struct _TnyCamelFolderClass 
{
	GObjectClass parent;

	/* virtual methods */
	void (*add_msg_func) (TnyFolder *self, TnyMsg *msg);
	void (*remove_msg_func) (TnyFolder *self, TnyHeader *header);
	void (*expunge_func) (TnyFolder *self);
	TnyMsgRemoveStrategy* (*get_msg_remove_strategy_func) (TnyFolder *self);
	void (*set_msg_remove_strategy_func) (TnyFolder *self, TnyMsgRemoveStrategy *st);
	TnyMsg* (*get_msg_func) (TnyFolder *self, TnyHeader *header);
	void (*get_headers_func) (TnyFolder *self, TnyList *headers, gboolean refresh);
	const gchar* (*get_name_func) (TnyFolder *self);
	const gchar* (*get_id_func) (TnyFolder *self);
	TnyStoreAccount* (*get_account_func) (TnyFolder *self);
	void (*set_name_func) (TnyFolder *self, const gchar *name);
	TnyFolderType (*get_folder_type_func) (TnyFolder *self);
	guint (*get_all_count_func) (TnyFolder *self);
	guint (*get_unread_count_func) (TnyFolder *self);
	gboolean (*is_subscribed_func) (TnyFolder *self);
	void (*refresh_async_func) (TnyFolder *self, TnyRefreshFolderCallback callback, 
		TnyRefreshFolderStatusCallback status_callback, gpointer user_data);
	void (*refresh_func) (TnyFolder *self);

	void (*get_folders_async_func) (TnyFolderStore *self, TnyList *list, 
		TnyGetFoldersCallback callback, TnyFolderStoreQuery *query, 
		gpointer user_data);
	void (*get_folders_func) (TnyFolderStore *self, TnyList *list, TnyFolderStoreQuery *query);
	void (*remove_folder_func) (TnyFolderStore *self, TnyFolder *folder);
	TnyFolder* (*create_folder_func) (TnyFolderStore *self, const gchar *name);
};

In tny_camel_specific_folder_class_init you can overwrite the function pointers and let them point to your implementations. In this example we will both override the expunge method of TnyCamelFolder "yet" we will keep it virtual in TnyCamelSPECIFICFolder. Which means that you can override it once more. We will also call the super method as defined in TnyCamelFolder. In Java that would be super.expunge() and in .NET that would be base.expunge().

/* Our real implementation */
static void 
tny_camel_specific_folder_expunge_default (TnyFolderStore *self)
{
    /* Your juicy special tricks to remove a folder */
    WebService *service = web_service_get_instance ();
    web_service_expunge (service, tny_folder_get_id (folder));
    g_object_unref (G_OBJECT (service));

    /* Call the method on super. This will call expunge on TnyCamelFolder. */
    TNY_CAMEL_SPECIFIC_FOLDER_CLASS (parent_class)->expunge_func (self, folder);
}

/* The marshaller */
tny_camel_specific_folder_expunge (TnyFolderStore *self)
{
	/* Unless we are overridden, this will call our expunge_default */
	TNY_CAMEL_SPECIFIC_FOLDER_GET_CLASS (self)->expunge_func (self);
	return;
}

static void 
tny_camel_specific_folder_class_init (TnyCamelSPECIFICFolderClass *class)
{
	GObjectClass *object_class;

	parent_class = g_type_class_peek_parent (class);
	object_class = (GObjectClass*) class;

	/* This is our real implementation */
	class->expunge_func = tny_camel_folder_expunge_default;

	object_class->finalize = tny_camel_specific_folder_finalize;

        /* Override the method of TnyCamelFolder with our marshaller */
        TNY_CAMEL_FOLDER_CLASS (class)->expunge_func = tny_camel_specific_folder_expunge;

	return;
}

A real in-use existing example

An example of this in action is the TnyMozEmbedMsgView type in libtinymailui-mozembed

Pitfalls

  • Don't mix GET_CLASS and CLASS macros. Do it right, like in this sample
  • Don't mix the location of the method_name_default and method_name function pointer assignments
  • Don't override to override, only override the methods that you are changing. This will ease debugging (following function pointers is not always fun)