Wednesday, July 22, 2009

The Missing GObject Tutorial Sample


Well, perhaps not exactly, but still -- I think it should work.

I've recently started poking around the GObject library, which is part of GLib. GObject is a C library aimed at providing OOP programmability that easily integrates with (usually) dynamic third-party languages. Basically, it allows you to write "glue" code between $your_dynamic_language and the GObject library just once, and then hook into any libraries created with GObject without writing further glue.

A good, detailed tutorial is available here, which I've been working through. After getting the gist of something, I get itchy for some sample code to play around with. So I Googled and found this how-to. An older version of the documentation mentioned some sample code that I was never able to find.

After giving up on that, I decided that I should be able to use the tutorial to scrap together my own sample. The tutorial was pretty detailed, after all, and apparently referenced a sample that did (or does) exist somewhere. So that's what I did. I've created a fully functioning sample based more-or-less on the tutorial mentioned above. The code is below, with comments.

I have compiled this on my Ubuntu 8.10 and 9.04 machines using the following command:

gcc `pkg-config --libs gtk+-2.0` `pkg-config --cflags gtk+-2.0` maman-bar.c

maman-bar.h

/*
* Copyright/Licensing information.
*
* Reference:
*
* http://library.gnome.org/devel/gobject/unstable/howto-gobject.html
* http://library.gnome.org/devel/gobject/unstable/chapter-gobject.html
*
*
*/


/* inclusion guard */
#ifndef __MAMAN_BAR_H__
#define __MAMAN_BAR_H__

#include <glib-object.h>

/*
* Potentially, include other headers on which this header depends.
*/

/*
* Type macros.
*/
#define MAMAN_TYPE_BAR (maman_bar_get_type ())
#define MAMAN_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_TYPE_BAR, MamanBar))
#define MAMAN_IS_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_BAR))
#define MAMAN_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MAMAN_TYPE_BAR, MamanBarClass))
#define MAMAN_IS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MAMAN_TYPE_BAR))
#define MAMAN_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MAMAN_TYPE_BAR, MamanBarClass))

typedef struct _MamanBar MamanBar;
typedef struct _MamanBarClass MamanBarClass;

/*
* Private instance fields
* Uses the Pimpl method:
*
* http://www.gotw.ca/gotw/024.htm
* http://www.gotw.ca/gotw/028.htm
*
*/
typedef struct _MamanBarPrivate MamanBarPrivate;


/* object */
struct _MamanBar
{
GObject parent_instance;

/* public */
int public_int;


/*< private >*/
MamanBarPrivate *priv;
};

/* class */
struct _MamanBarClass
{
GObjectClass parent_class;

/* class members */

/* Virtual public method */
void (*do_action_virt) (MamanBar *self, gchar *msg);

};


/*
* Non-virtual public method
*/
void maman_bar_do_action (MamanBar *self, gchar *msg /*, other params */);

/* Virtual method call declaration */
void maman_bar_do_action_virt (MamanBar *self, gchar *msg /*, other params */);
/* Virtual method default 'super' class method */
void maman_bar_do_action_virt_default (MamanBar *self, gchar *msg);


#endif /* __MAMAN_BAR_H__ */


maman-bar.c

#include "maman-bar.h"

/*
http://library.gnome.org/devel/gobject/2.21/gobject-Type-Information.html#G-DEFINE-TYPE--CAPS

A convenience macro for type implementations, which declares a class
initialization function, an instance initialization function (see GTypeInfo
for information about these) and a static variable named t_n_parent_class
pointing to the parent class. Furthermore, it defines a *_get_type()
function. See G_DEFINE_TYPE_EXTENDED() for an example.
*/
G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT);


/* Define the private structure in the .c file */
#define MAMAN_BAR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MAMAN_TYPE_BAR, MamanBarPrivate))

struct _MamanBarPrivate
{
int hsize;
gchar *msg;
};


/* Init functions */
static void
maman_bar_class_init (MamanBarClass *klass)
{
g_type_class_add_private (klass, sizeof (MamanBarPrivate));

/* Setup the default handler for virtual method */
klass->do_action_virt = maman_bar_do_action_virt_default;
}


static void
maman_bar_init (MamanBar *self)
{

g_print("maman_bar_init() - init object\n");


/* Initialize all public and private members to reasonable default values. */

/* Initialize public fields */
self->public_int = 99;

g_print(" initializing public_int to %d\n", self->public_int);


/* Initialize private fields */
MamanBarPrivate *priv;
self->priv = priv = MAMAN_BAR_GET_PRIVATE(self);
priv->hsize = 42;

g_print(" init'd private variable priv->hsize to %d\n", priv->hsize);


/* If you need specific construction properties to complete initialization,
* delay initialization completion until the property is set.
*/

}


/* Object non-virtual method */
void maman_bar_do_action (MamanBar *self, gchar *msg) {
/* First test that 'self' is of the correct type */
g_return_if_fail (MAMAN_IS_BAR (self));


// Assign to private 'msg'
self->priv->msg = msg;

g_print("maman_bar_do_action() - %s\n", self->priv->msg);

}

/* Object virtual method call - performs the override */
void maman_bar_do_action_virt (MamanBar *self, gchar *msg) {
/* First test that 'self' is of the correct type */
g_return_if_fail (MAMAN_IS_BAR (self));

g_print("maman_bar_do_action_virt() -> ");
MAMAN_BAR_GET_CLASS (self)->do_action_virt(self, msg);
}

/* Object virtual method default action (can be overridden) */
void maman_bar_do_action_virt_default (MamanBar *self, gchar *msg) {

g_print("maman_bar_do_action_virt_default() - %s\n", msg );

}

int
main (int argc, char *argv[])
{
/*
* Prior to any use of the type system, g_type_init() has to be called
* to initialize the type system and assorted other code portions
* (such as the various fundamental type implementations or the signal
* system).
*/
g_type_init();

/* Create our object */
MamanBar *bar = g_object_new (MAMAN_TYPE_BAR, NULL);

bar->public_int +=1;
g_print("incremented bar->public_int: %d\n", bar->public_int);

/* Call object method */
maman_bar_do_action(bar, "helowrld");

/* Call virtual object method - we could subclass and override... */
maman_bar_do_action_virt(bar, "HELOWRLD");

return 0;
}

And here's what I get when I run a.out:
ok ./a.out
maman_bar_init() - init object
initializing public_int to 99
init'd private variable priv->hsize to 42
incremented bar->public_int: 100
maman_bar_do_action() - helowrld
maman_bar_do_action_virt() -> maman_bar_do_action_virt_default() - HELOWRLD
You can download the source files directly from here.

9 comments:

  1. Your working GObject example is a great help. I've been working on getting a small DBus/Glib demonstration together and this completed the picture for me. Many thanks!

    ReplyDelete
  2. Hello JA,

    Your tutorial was VERY helpful for me, thank you:-)) I am trying though to implement the singleton as described here:
    http://library.gnome.org/devel/gobject/unstable/gobject-The-Base-Object-Type.html#GObjectClass

    but it does not behave as a singleton for me.
    I do the following(pseudo-code):
    single1 = g_object_new(MY_TYPE_SINGLE,...);
    my_single_set_foo(single1, 10);
    single2 = g_object_new(MY_TYPE_SINGLE...);
    int foo = my_single_get_foo(single2);

    but 'foo' is not 10.
    do you have any idea?
    cheers
    simon:)

    ReplyDelete
  3. Great job! It really helped me! You could add some properties to demonstrate how they work though

    ReplyDelete
  4. I guess I *have* been ignoring the blog for a while. Sorry I didn't see the comments earlier.

    simonbolek, I'm not entirely sure, but on first blush I'd say be certain that you're doing what they propose in Example 1 at the URL you provided, in the constructor function - maman_bar_init().

    Line 15 of Example 1 should probably be something like:

    the_singleton = MAMAN_BAR (object);

    ReplyDelete
  5. This doesn't work on linux mint for me..

    gcc `pkg-config --libs gtk+-2.0` `pkg-config --cflags gtk+-2.0` maman-bar.c

    dies with a bunch of linker errors similar to this:
    "undefined reference to `g_type_class_peek_parent'"


    this does..
    gcc `pkg-config --cflags gtk+-2.0` maman-bar.c `pkg-config --libs gtk+-2.0`

    ... for newbies.



    ReplyDelete
    Replies
    1. Also thanks for putting the code up. I've found it quite fiddly to get right from the code snippets.

      Delete
  6. hi, I added a comment in
    https://bugzilla.gnome.org/show_bug.cgi?id=658766
    hoping that your files may become part of the official tutorial

    ReplyDelete