The GnomeObject object

As usual, the GNOME hackers have done everything they could to ease your development. Bonobo provides a nice default implementation for all its CORBA interfaces, so that (theoretically) the programmer is not obliged to learn CORBA. This implementation is done in plain C, like everywhere in GNOME. The implementation is based on the GTK Object system.

The GTK Object system allows you to do semi object-oriented programming in C. The reader is strongly encouraged to read the GTK code (gtk/gtk/gtkobject.c, gtk/gtk/gtkobject.h, gtk/gtk/gtkwidget.c and gtk/gtk/gtkwidget.h) and books detailing the GTK architecture.

The GnomeObject, which inherits from GtkObject also implements the GNOME::Unknown interface (bonobo/bonobo/gnome-object.c and bonobo/bonobo/gnome-object.h). Here are its basic structures:

typedef struct {
	POA_GNOME_Unknown servant_placeholder;
	gpointer gnome_object;
} GnomeObjectServant;

typedef struct _GnomeObjectPrivate GnomeObjectPrivate;

typedef struct {
	GtkObject base;

	CORBA_Object          corba_objref;
	gpointer              servant;
	GnomeObjectPrivate   *priv;
} GnomeObject;

typedef struct {
	GtkObjectClass parent_class;

	/*
	 * signals.  
	 */
	void  (*query_interface) (GnomeObject *object, 
                                  const char *repo_id, 
                                  CORBA_Object *retval);
	void  (*system_exception)(GnomeObject *object, 
                                  CORBA_Object cobject, 
                                  CORBA_Environment *ev);
	void  (*object_gone)     (GnomeObject *object, 
                                  CORBA_Object cobject, 
                                  CORBA_Environment *ev);
} GnomeObjectClass;

typedef struct {
	int   ref_count;
	GList *objs;
} GnomeAggregateObject;

struct _GnomeObjectPrivate {
	GnomeAggregateObject *ao;
	int destroy_id;
};

As you may have seen, each GnomeObject is associated with some servant through the "servant" field:

GnomeObject obj;
PortableServer_Servant servant = obj->servant;

Also, each servant is associated with GnomeObject through the "gnome_object" field:

PortableServer_Servant servant;
GnomeObject obj = servant->gnome_object;

Thus, the GnomeObject is a wrapper around the CORBA servant which effectively implements the GNOME::Unknown interface. We have wrapper functions: gnome_object_get_servant and gnome_object_from_servant to switch from the servant to the GnomeObject. We have also the gnome_object_bind_to_servant which is used to establish this one to one mapping.

The gnome_object_ref() and gnome_object_unref() functions implement the ref() and unref() interface functions. These functions are called internally by the impl_GNOME_Unknown_ref() and impl_GNOME_Unknown_unref() functions.

void
gnome_object_ref (GnomeObject *object)
{
	g_return_if_fail (object != NULL);
	g_return_if_fail (GNOME_IS_OBJECT (object));
	g_return_if_fail (object->priv->ao->ref_count != 0);

	object->priv->ao->ref_count++;
}

void
gnome_object_unref (GnomeObject *object)
{
	g_return_if_fail (object != NULL);
	g_return_if_fail (GNOME_IS_OBJECT (object));
	g_return_if_fail (object->priv->ao->ref_count > 0);

	object->priv->ao->ref_count--;

	if (object->priv->ao->ref_count == 0){
		GnomeAggregateObject *ao = object->priv->ao;
		GList *l;
		
		for (l = ao->objs; l; l = l->next){
			GnomeObject *o = l->data;
			
			gtk_signal_disconnect (GTK_OBJECT (o), o->priv->destroy_id);
			gtk_object_destroy (l->data);
		}

		g_list_free (ao->objs);
		g_free (ao);
	}
}

static void
impl_GNOME_Unknown_ref (PortableServer_Servant servant, CORBA_Environment *ev)
{
	GnomeObject *object;

	object = gnome_object_from_servant (servant);          
        /* baaad !! we MUST call gnome_object_ref() */
	object->priv->ao->ref_count++;
}

static void
impl_GNOME_Unknown_unref (PortableServer_Servant servant, CORBA_Environment *ev)
{
	GnomeObject *object;

	object = gnome_object_from_servant (servant);

	gnome_object_unref (object);
}

The implementation of the query_interface function is a little more complex: the GnomeObject structure has a pointer to the GnomeObjectPrivate structure which itself has a pointer to a GnomeAggregateObject which itself has a pointer to a GList which holds the list of the GnomeObjects linked to this GnomeObject.

GnomeObject object;
GList list = object->priv->ao->objs;

This means that your GnomeObjects are aggregated independently. Let's look at the container/component example. First, i implement the component stuff. I have lots of code which builds a GnomeObject which is used to browse the interfaces of a component.

Now, i can implement the container side, independently of the component side. When this is done, all i have to do is to aggregate my GnomeObject object implementing the component to the GnomeObject implementing the container:

GnomeObject container;
GnomeObject component;
/* this aggregates only the first object to my GnomeObject object */
/* one should loop through the GList */
container->priv->ao->objs = g_list_prepend (container->priv->ao->objs, 
                                            component->priv->ao->objs->data)

This is exactly what is being done in the gnome_object_add_interface function.

In general, the GnomeObject class is used as the basis of all the bonobo objects. To implement a CORBA interface which inherits from the GNOME::Unknown interface, the Bonobo hackers implement it by defining an object which inherits from the GnomeObject object.

Then, they can aggregate these objects with the gnome_object_add_interface.