Archive

Posts Tagged ‘GObject’

OOP Using GObject (9) – A Dynamic Type

March 12th, 2011 No comments

Recall there are 3 types in GObject type system: Fundamental, static and dynamic. A fundamental type is a top-most type which has no parent type. Most of them are pre-defined. Static types never load/unload its class type (say, their class struct) at runtime, since they are static. On the contrary, dynamic types can be dynamically loaded/unloaded at runtime. They are normally used within a module.

We can call g_type_register_dynamic() to register a dynamic type. When used in a module of GObject library (may be a GTypeModule type), We can also call g_type_module_register_type() to create your dynamic types. g_type_register_dynamic() is invoked for you in that function. Let’s go through the code:

NOTE: PLEASE READ ALL COMMENT CAREFULLY.

// bartype.h
#ifndef BAR_TYPE_H_
#define BAR_TYPE_H_

#include <glib-object.h>

/* Bar type class struct */
typedef struct _BarTypeClass {
    GObjectClass parent;
} BarTypeClass;

/* Far type object struct */
typedef struct _BarType {
    GObject parent;
} BarType;

/* type function */
GType bar_type_get_type();

/* register type */
void bar_type_register_type(GTypeModule *type_module);


#endif /* BAR_TYPE_H_ */
// bartype.c
#include "bartype.h"

static gpointer parent_klass = NULL;
static GType bar_type_type_id = 0;

static void bar_type_instance_init(BarType *self) {
    g_print("Calling bar_type_instance_init()\n");
}

static void bar_type_instance_finalize(GObject *object) {
    /* do some finalize, maybe release some dynamically allocated memory */
    g_print("Calling bar_type_instance_finalize()\n");
    /* chain to parent's finalize */
    G_OBJECT_CLASS(parent_klass)->finalize(object);
}

static void bar_type_class_init(BarTypeClass *klass) {
    g_print("Calling bar_type_class_init()\n");
    parent_klass = g_type_class_peek_parent(klass);
    G_OBJECT_CLASS(klass)->finalize = bar_type_instance_finalize;
}

static void bar_type_class_finalize(BarTypeClass *klass) {
    g_print("Calling bar_type_class_finalize()\n");
}

GType bar_type_get_type() {
    return bar_type_type_id;
}

void bar_type_register_type(GTypeModule *type_module) {
    const GTypeInfo type_info = {
        sizeof(BarTypeClass), /* class_size */
        NULL,                   /* base_init */
        NULL,                   /* base_finalize */
        (GClassInitFunc)bar_type_class_init, /* class_init */
        (GClassFinalizeFunc)bar_type_class_finalize, /* class_finalize */
        NULL,                   /* class_data */
        sizeof(BarType),        /* instance_size */
        0,                      /* n_preallocs */
        (GInstanceInitFunc)bar_type_instance_init, /* instance_init */
        NULL                    /* value_table */
    };
    bar_type_type_id = g_type_module_register_type(
        type_module, G_TYPE_OBJECT, "BarTypeDynamicClass", &type_info, 0);
}

The implementation structure may be a little different with the stuff when creating a static type. An additional parameter GTypeModule is passed in. It represents the module your dynamic type belongs to. So, when the module is unloaded, all dynamic types in it are unaccessible.

Also note the bar_type_class_finalize() function. We use it to override the finalize() virtual function in GObjectClass. Now you can do un-initialiation in this function. It is like the destructor in a C++ class.

Let’s move on to the module type. This type inherits GTypeModule:

// fakemodule.h
#ifndef FAKE_MODULE_H_
#define FAKE_MODULE_H_

#include <glib-object.h>

/* module object struct */
typedef struct _FakeModule {
    GTypeModule parent;
} FakeModule;

/* module class struct */
typedef struct _FakeModuleClass {
    GTypeModuleClass parent;
} FakeModuleClass;

/* type method */
GType fake_module_get_type();


#endif /* FAKE_MODULE_H_ */
// fakemodule.c
#include "fakemodule.h"

/*
 * If you implement a real shared library module, you
 * can init module variables, assign virtual function here.
 */
gboolean fake_module_load(GTypeModule *module) {
    g_print("Invoking fake_module_load()\n");
    /* successfully loaded */
    return TRUE;
}

/*
 * If you implement a real shared library module, you
 * can uninit module variables, and make all cleanups here.
 */
void fake_module_unload(GTypeModule *module) {
    /* noop */
    g_print("Invoking fake_module_unload()\n");
}

static void fake_module_class_init(FakeModuleClass *klass, gpointer data) {
    g_print("Calling fake_module_class_init()\n");
    GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS(klass);
    module_class->load = fake_module_load;
    module_class->unload = fake_module_unload;
}

static void fake_module_instance_init(FakeModule *instance, gpointer data) {
    g_print("Calling fake_module_instance_init()\n");
}

GType fake_module_get_type() {
    static GType type_id = 0;
    if (type_id == 0) {
        static const GTypeInfo type_info = {
            sizeof(FakeModuleClass), /* class_size */
            NULL,                   /* base_init */
            NULL,                   /* base_finalize */
            (GClassInitFunc)fake_module_class_init, /* class_init */
            NULL,                   /* class_finalize */
            NULL,                   /* class_data */
            sizeof(FakeModule),     /* instance_size */
            0,                      /* n_preallocs */
            (GInstanceInitFunc)fake_module_instance_init, /* instance_init */
            NULL                    /* value_table */
        };
        type_id = g_type_register_static(
            G_TYPE_TYPE_MODULE, "FakeModuleStaticClass", &type_info, 0);
    }
    return type_id;
}

GTypeModule is an abstract type. We should implements its load() and unload() virtual function.

Our test code:

// main.c
#include "footype.h"
#include "bartype.h"
#include "fakemodule.h"
#include <glib-object.h>

/*
 * Module entry point. If you implement a real shared library module,
 * you can use dlopen()/dlsym() or g_module_open()/g_module_symbol() to
 * load this module dynamically.
 */
void module_init(GTypeModule *type_module) {
    foo_type_register_type(type_module);
    bar_type_register_type(type_module);
}

int main() {
    g_type_init();

    FakeModule *module = (FakeModule *)g_object_new(fake_module_get_type(), NULL);
    module_init((GTypeModule *)module);

    /*
     * Add a reference to foo type class here. Otherwise, the fake module
     * will be unloaded right after the free() of foo type object and cause error.
     */
    FooTypeClass *foo_type_class = (FooTypeClass *)g_type_class_ref(foo_type_get_type());

    FooType *foo_type = (FooType *)g_object_new(foo_type_get_type(), NULL);
    BarType *bar_type = (BarType *)g_object_new(bar_type_get_type(), NULL);

    /* Test for override finalize() */
    g_object_unref(foo_type);
    g_object_unref(bar_type);

    g_type_class_unref(foo_type_class);

    return 0;
}

Another dynamic type BarType is defined in addition to FooType to demo the usage. The output maybe:

Calling fake_module_class_init()
Calling fake_module_instance_init()
Invoking fake_module_load()
Calling foo_type_class_init()
Calling foo_type_instance_init()
Calling bar_type_class_init()
Calling bar_type_instance_init()
Calling foo_type_instance_finalize()
Calling bar_type_instance_finalize()
Calling bar_type_class_finalize()
Calling foo_type_class_finalize()
Invoking fake_module_unload()

See the init/finalize process?

At the end of my note, Let me summarize to compare GObject library with C++ implementation:

1. Member Variables:

GObject C++
in class struct class meta info
in object struct class instance member
global variable class static member

2. Function Callbacks:

GObject C++
base_init init class dynamic meta info
base_finalize finalize dynamic class meta info, only dynamic types use it
class_init init class static meta info
class_finalize finalize class static meta info, only dynamic types use it
instance_init init instace, like C++ constructor
override finalize in GObjectClass finalize instance, like C++ destructor

All source code is available in my skydrive: http://cid-481cbe104492a3af.office.live.com/browse.aspx/share/dev/TestOO. In the TestGObject-{date}.zip/TestGObject7 folder.

Categories: C/C++ Tags: ,

OOP Using GObject (8) – An interface

March 11th, 2011 No comments

Interfaces usage in library is like class usage. We need to define a interface struct, but no object struct is needed:

NOTE: PLEASE READ ALL COMMENT CAREFULLY.

typedef struct _FakeIServer FakeIServer; /* dummy object */
typedef struct _FakeIServerInterface {
    GTypeInterface parent;
    void (*response)(FakeIServer *instance);
} FakeIServerInterface;

Then we register the interface using g_type_register_static() with G_TYPE_INTERFACE as first parameter. For interfaces, we only need to assign base_init() and base_finalize()callbacks.

static void fake_iserver_base_init(gpointer g_class) {
    static gboolean is_initialized = FALSE;
    if (!is_initialized) {
        /* add properties and signals to the interface here */
        is_initialized = TRUE;
    }
}

GType fake_iserver_get_type() {
    static GType type_id = 0;
    if (type_id == 0) {
        static const GTypeInfo interface_info = {
            sizeof(FakeIServerInterface),   /* class_size */
            fake_iserver_base_init,         /* base_init */
            NULL,                           /* base_finalize */
        };
        type_id = g_type_register_static(
            G_TYPE_INTERFACE, "FakeIServerInterface", &interface_info, 0);
    }
    return type_id;
}

As described in the official document, we should allocate dynamic memebers of class struct inbase_init(). Otherwise, all copies of the class struct share only one copy of dynamic members. This leads to problems.

Let’s define the type which implements the interface:

// fakedesktop.h
#ifndef FAKE_DESKTOP_H_
#define FAKE_DESKTOP_H_

#include <glib-object.h>

#define FAKE_TYPE_DESKTOP              ( fake_desktop_get_type() )
#define FAKE_DESKTOP(obj)              \
 ( G_TYPE_CHECK_INSTANCE_CAST((obj), FAKE_TYPE_DESKTOP, FakeDesktop) )
#define FAKE_IS_DESKTOP(obj)           \
 ( G_TYPE_CHECK_INSTANCE_TYPE((obj), FAKE_TYPE_DESKTOP) )
#define FAKE_DESKTOP_CLASS(cls)        \
 ( G_TYPE_CHECK_CLASS_CAST((cls), FAKE_TYPE_DESKTOP, FakeDesktopClass) )
#define FAKE_IS_DESKTOP_CLASS(cls)     \
 ( G_TYPE_CHECK_CLASS_TYPE((cls), FAKE_TYPE_DESKTOP) )
#define FAKE_DESKTOP_GET_CLASS(obj)    \
 ( G_TYPE_INSTANCE_GET_CLASS((obj), FAKE_TYPE_DESKTOP, FakeDesktopClass ) )

/* Base object struct */
typedef struct _FakeDesktop {
    /* GObject as the first field */
    GObject parent;
} FakeDesktop;

/* Base class struct */
typedef struct _FakeDesktopClass {
    /* GObjectClass as the first field */
    GObjectClass parent;
} FakeDesktopClass;

/* type method */
GType fake_desktop_get_type();


#endif /* FAKE_DESKTOP_H_ */

Note the naming convention I used here. Our FakeDesktop class will implement the FakeIServer interface and another FakeIClient interface. This time do not use corresponding interface struct as the first members of FakeDesktop and FakeDesktopClass. Interface info will be added dynamically when initialize a real instance of FakeDesktop. Let’s move to the *.c code:

// fakedesktop.c
#include "fakedesktop.h"
#include "fakeiface.h"

void fake_desktop_request(FakeIClient *instance) {
    g_print("Invoking fake_desktop_request()\n");
}

void fake_desktop_response(FakeIServer *instance) {
    g_print("Invoking fake_desktop_response()\n");
}

static void fake_desktop_class_init(FakeDesktopClass *klass, gpointer data) {
}

static void fake_desktop_instance_init(FakeDesktop *instance, gpointer data) {
}

static void fake_desktop_interface_init_iclient(FakeIClientInterface* iface, gpointer iface_data) {
    iface->request = fake_desktop_request;
}

static void fake_desktop_interface_init_iserver(FakeIServerInterface* iface, gpointer iface_data) {
    iface->response = fake_desktop_response;
}

GType fake_desktop_get_type() {
    static GType type_id = 0;
    if (type_id == 0) {
        static const GTypeInfo type_info = {
            sizeof(FakeDesktopClass),  /* class_size */
            NULL,                   /* base_init */
            NULL,                   /* base_finalize */
            (GClassInitFunc)fake_desktop_class_init, /* class_init */
            NULL,                   /* class_finalize */
            NULL,                   /* class_data */
            sizeof(FakeDesktop),    /* instance_size */
            0,                      /* n_preallocs */
            (GInstanceInitFunc)fake_desktop_instance_init, /* instance_init */
            NULL                    /* value_table */
        };
        type_id = g_type_register_static(G_TYPE_OBJECT, "FakeDesktopClass", &type_info, 0);

        /* add interface */
        GInterfaceInfo interface_info_iclient = {
            (GInterfaceInitFunc)fake_desktop_interface_init_iclient, /* interface_init */
            NULL,   /* interface_finalize */
            NULL,   /* interface_data */
        };
        GInterfaceInfo interface_info_iserver = {
            (GInterfaceInitFunc)fake_desktop_interface_init_iserver, /* interface_init */
            NULL,   /* interface_finalize */
            NULL,   /* interface_data */
        };
        g_type_add_interface_static(type_id, FAKE_TYPE_ICLIENT, &interface_info_iclient);
        g_type_add_interface_static(type_id, FAKE_TYPE_ISERVER, &interface_info_iserver);
    }

    return type_id;
}

Note the g_type_add_interface_static() function call to add interface info. The interface info is defined in a GInterfaceInfo struct. We just make use of the interface_init() callback. In it, we assign function pointers of corresponding interface to our implementation function. We can add multiple interface infos to implement them.

Finally, the test code:

// main.c
#include "fakeiface.h"
#include "fakedesktop.h"
#include "fakelaptop.h"
#include <glib-object.h>

void my_dump_type(GType type_id) {
    g_print("Type id: %d\n", type_id);
    g_print("Type name: %s\n", g_type_name(type_id));
    g_print("Is fundamental? %s\n", G_TYPE_IS_FUNDAMENTAL(type_id) ? "yes" : "no");
    g_print("Is derived? %s\n", G_TYPE_IS_DERIVED(type_id) ? "yes" : "no");
    g_print("Is interface? %s\n", G_TYPE_IS_INTERFACE(type_id) ? "yes" : "no");
    g_print("Is classed? %s\n", G_TYPE_IS_CLASSED(type_id) ? "yes" : "no");
    g_print("Is instantiatable? %s\n", G_TYPE_IS_INSTANTIATABLE(type_id) ? "yes" : "no");
    g_print("Is derivable? %s\n", G_TYPE_IS_DERIVABLE(type_id) ? "yes" : "no");
    g_print("Is deep derivable? %s\n", G_TYPE_IS_DEEP_DERIVABLE(type_id) ? "yes" : "no");
    g_print("Is abstract? %s\n", G_TYPE_IS_ABSTRACT(type_id) ? "yes" : "no");
    g_print("Is value abstract? %s\n", G_TYPE_IS_VALUE_ABSTRACT(type_id) ? "yes" : "no");
    g_print("Is value type: %s\n", G_TYPE_IS_VALUE_TYPE(type_id) ? "yes" : "no");
    g_print("Has value table: %s\n", G_TYPE_HAS_VALUE_TABLE(type_id) ? "yes" : "no");
}

int main() {
    g_type_init();
    my_dump_type(FAKE_TYPE_ICLIENT);
    my_dump_type(FAKE_TYPE_ISERVER);
    my_dump_type(FAKE_TYPE_LAPTOP);
    my_dump_type(FAKE_TYPE_DESKTOP);

    FakeLaptop *laptop = (FakeLaptop *)g_object_new(FAKE_TYPE_LAPTOP, NULL);
    FakeDesktop *desktop = (FakeDesktop *)g_object_new(FAKE_TYPE_DESKTOP, NULL);
    g_print("laptop is FakeIServer? %s\n", FAKE_IS_ISERVER(laptop) ? "yes" : "no");
    g_print("laptop is FakeIClient? %s\n", FAKE_IS_ICLIENT(laptop) ? "yes" : "no");
    g_print("desktop is FakeIServer? %s\n", FAKE_IS_ISERVER(desktop) ? "yes" : "no");
    g_print("desktop is FakeIClient? %s\n", FAKE_IS_ICLIENT(desktop) ? "yes" : "no");

    /* Polynophysm */
    int i;
    FakeIServer *servers[2] = { (FakeIServer *)laptop, (FakeIServer *)desktop };
    for (i = 0; i < 2; i++) {
        FakeIServer *inst = servers[i];
        FakeIServerInterface *iface = FAKE_ISERVER_GET_INTERFACE(inst);
        if (iface) {
            iface->response(inst);
        }
    }
    FakeIClient *clients[2] = { (FakeIClient *)laptop, (FakeIClient *)desktop };
    for (i = 0; i < 2; i++) {
        FakeIClient *inst = clients[i];
        FakeIClientInterface *iface = FAKE_ICLIENT_GET_INTERFACE(inst);
        if (iface) {
            iface->request(inst);
        }
    }

    return 0;
}

In runtime, if your classed type implements an interface, it will be considered as the interface type (is-a).

All source code is available in my skydrive: http://cid-481cbe104492a3af.office.live.com/browse.aspx/share/dev/TestOO. In the TestGObject-{date}.zip/TestGObject6 folder.

Categories: C/C++ Tags: ,

OOP Using GObject (7) – Signals

March 11th, 2011 No comments

Signals in GObject are used to support a event-driven programming. Signals can be connected to callback handlers. When they are emitted, these handlers are invoked. To add signals to a type, notice following lines of code:

NOTE: PLEASE READ ALL COMMENT CAREFULLY.

static void fake_base_class_init(FakeBaseClass *klass, gpointer data) {
    // ...
    /* signals */
    g_signal_new("base-signal-int", FAKE_TYPE_BASE, G_SIGNAL_RUN_LAST, 0,
        NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT, NULL);
    g_signal_new("base-signal-string", FAKE_TYPE_BASE, G_SIGNAL_RUN_LAST, 0,
        NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING, NULL);
    // ...
}

Like properties, signals can be inherited too. Use signals like:

int main() {
    // ...
    FakeBase *base = (FakeBase *)g_object_new(FAKE_TYPE_BASE, NULL);
    FakeDerived *derived = (FakeDerived *)g_object_new(FAKE_TYPE_DERIVED, NULL);
    /* Test for signals */
    /* 1 <-> 1 */
    g_signal_connect(base, "base-signal-int", G_CALLBACK(print_int1), NULL);
    g_signal_connect(base, "base-signal-string", G_CALLBACK(print_string1), NULL);
    g_signal_emit_by_name(base, "base-signal-int", 12345);
    g_signal_emit_by_name(base, "base-signal-string", "abcde");
    /* 1 <-> 1+ */
    g_signal_connect(base, "base-signal-int", G_CALLBACK(print_int2), NULL);
    g_signal_emit_by_name(base, "base-signal-int", 123456);
    /* signal inheritance */
    g_signal_connect(derived, "base-signal-int", G_CALLBACK(print_int1), NULL);
    g_signal_connect(derived, "base-signal-string", G_CALLBACK(print_string1), NULL);
    g_signal_connect(derived, "derived-signal-int", G_CALLBACK(print_int2), NULL);
    g_signal_connect(derived, "derived-signal-string", G_CALLBACK(print_string2), NULL);
    g_signal_emit_by_name(derived, "base-signal-int", 1234567);
    g_signal_emit_by_name(derived, "base-signal-string", "abcdefg");
    g_signal_emit_by_name(derived, "derived-signal-int", 1234567);
    g_signal_emit_by_name(derived, "derived-signal-string", "abcdefg");
    // ...
}

The callback functions are defined simply like:

void print_int1(GObject *sender, int i, gpointer data) {
    if (FAKE_IS_DERIVED(sender)) {
        g_print("Invoking print_int1(): derived.i=%d\n", i);
    } else if (FAKE_IS_BASE(sender)) {
        g_print("Invoking print_int1(): base.i=%d\n", i);
    }
}

void print_int2(GObject *sender, int i, gpointer data) {
    if (FAKE_IS_DERIVED(sender)) {
        g_print("Invoking print_int2(): derived.i=%d\n", i);
    } else if (FAKE_IS_BASE(sender)) {
        g_print("Invoking print_int2(): base.i=%d\n", i);
    }
}

void print_string1(GObject *sender, gchar* str, gpointer data) {
    if (FAKE_IS_DERIVED(sender)) {
        g_print("Invoking print_string1(): derived.str=\"%s\"\n", str);
    } else if (FAKE_IS_BASE(sender)) {
        g_print("Invoking print_string1(): base.str=\"%s\"\n", str);
    }
}

void print_string2(GObject *sender, gchar* str, gpointer data) {
    if (FAKE_IS_DERIVED(sender)) {
        g_print("Invoking print_string2(): derived.str=\"%s\"\n", str);
    } else if (FAKE_IS_BASE(sender)) {
        g_print("Invoking print_string2(): base.str=\"%s\"\n", str);
    }
}

All source code is available in my skydrive: http://cid-481cbe104492a3af.office.live.com/browse.aspx/share/dev/TestOO. In the TestGObject-{date}.zip/TestGObject5 folder.

Categories: C/C++ Tags: ,

OOP Using GObject (6) – Properties

March 11th, 2011 No comments

Properties are supported by the GObject library. To use it, you should override the set_property() and get_property() function in GObjectClass:

NOTE: PLEASE READ ALL COMMENT CAREFULLY.

enum {
    PROP_0,
    PROP_BASE_ID,
    PROP_BASE_NAME
};

static void fake_base_set_property(GObject *object,
    guint property_id, const GValue *value, GParamSpec *pspec) {
    FakeBase *self = FAKE_BASE(object);
    switch (property_id) {
        case PROP_BASE_ID:
            self->priv->id = g_value_get_int(value);
            break;
        case PROP_BASE_NAME:
            g_free(self->priv->name);
            self->priv->name = g_value_dup_string(value);
            break;
        default:
            /* We don't have any other property... */
            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
            break;
    }
}

static void fake_base_get_property(GObject *object,
    guint property_id, GValue *value, GParamSpec *pspec) {
    FakeBase *self = FAKE_BASE(object);
    switch (property_id) {
        case PROP_BASE_ID:
            g_value_set_int(value, self->priv->id);
            break;
        case PROP_BASE_NAME:
            g_value_set_string(value, self->priv->name);
            break;
        default:
            /* We don't have any other property... */
            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
            break;
    }
}

static void fake_base_class_init(FakeBaseClass *klass, gpointer data) {
    // ...
    /* properties */
    GObjectClass *gobject_klass = G_OBJECT_CLASS(klass);
    gobject_klass->set_property = fake_base_set_property;
    gobject_klass->get_property = fake_base_get_property;
    GParamSpec *pspec;
    pspec = g_param_spec_int("base-id", "Base ID", 
        "Set/Get Base ID", -1000, 1000, 0, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
    g_object_class_install_property(gobject_klass, PROP_BASE_ID, pspec);
    pspec = g_param_spec_string("base-name",
        "Base Name", "Set/Get Base Name ", NULL, G_PARAM_READWRITE);
    g_object_class_install_property(gobject_klass, PROP_BASE_NAME, pspec);
    // ...
}

All APIs are clear and easy to use, please refer to the official document. Last but not least, properties can be inherited by derived classes. Here’s my test code:

int main() {
    // ...
    /* Base object */
    FakeBase *base = (FakeBase *)g_object_new(FAKE_TYPE_BASE, "base-id", 111, NULL);
    GValue base_name = { 0 };
    g_value_init(&base_name, G_TYPE_STRING);
    g_value_set_static_string(&base_name, "aaa");
    g_object_set_property(G_OBJECT(base), "base-name", &base_name);
    g_value_unset(&base_name);
    /* Derived object */
    FakeDerived *derived = (FakeDerived *)g_object_new(
        FAKE_TYPE_DERIVED, "base-id", 222, "derived-age", 333, NULL);
    g_object_set(derived, "base-name", "bbb", "derived-hash", "ccc", NULL);
    // ...
}

As you see, we can get/set properties one by one or using a parameter list.

All source code is available in my skydrive: http://cid-481cbe104492a3af.office.live.com/browse.aspx/share/dev/TestOO. In the TestGObject-{date}.zip/TestGObject5 folder.

Categories: C/C++ Tags: ,

OOP Using GObject (5) – Private Members

March 11th, 2011 No comments

Here’s some trivial note on using GObject library.

1. Private members

Recall our definition of Base type:

NOTE: PLEASE READ ALL COMMENT CAREFULLY.

/* Base object struct */
typedef struct _Base {
    GTypeInstance parent;
    /* instance variable, should be hidden */
    gint base_instance_i;
} Base;

/* Base class struct */
typedef struct _BaseClass {
    GTypeClass parent;
    /* instance method, used as a virtual method */
    void (*base_instance_dump)(struct _Base *instance);
} BaseClass;

It expose the visibility of base_instance_i field. We should keep encapsulation in OOP. GObject library has support for this. We can define the class as:

/* private data of Base object */
typedef struct _FakeBasePrivate FakeBasePrivate;

/* Base object struct */
typedef struct _FakeBase {
    /* GObject as the first field */
    GObject parent;
    /* private data */
    FakeBasePrivate *priv;
} FakeBase;

/* Base class struct */
typedef struct _FakeBaseClass {
    /*
     * The type GObject is supposed to be the base class of other user-defined classes.
     *   - Reference count support.
     *   - Support adding properties to GObject subclasses.
     *   - Support signals for asynchronized event handling like "event" in C#.
     */
    /* GObjectClass as the first field */
    GObjectClass parent;
    /*
     * Since glib 2.24, there're new functions to keep privacy:
     *   - g_type_add_class_private()
     *   - g_type_class_get_private()
     */
    /* private static field */
    gint version;
    /* private dynamic field */
    gchar *author;
    /* instance method, used as a virtual method */
    void (*virtual_dump)(struct _FakeBase *instance);
} FakeBaseClass;

We declare a new FakeBasePrivate struct to contain all private field used in FakeBase type. And the private struct is defined in *.c file, so its internal representation remains invisible. Then in *.c file, we got:

struct _FakeBasePrivate {
    gint id;
    gchar *name;
};

static void fake_base_class_init(FakeBaseClass *klass, gpointer data) {
    // ...
    /* Registers a private structure for an instantiable type. */
    g_type_class_add_private(klass, sizeof(FakeBasePrivate));
    // ...
}

static void fake_base_instance_init(FakeBase *instance, gpointer data) {
    // ...
    instance->priv = 
        G_TYPE_INSTANCE_GET_PRIVATE(instance, FAKE_TYPE_BASE, FakeBasePrivate);
    // ...
}

The private member is malloc in class_init() callback, and is ready to use after invoking instance_init(). When we will use property mechanism to get/set these private field later.

2. Naming convention

Official document: http://library.gnome.org/devel/gobject/stable/gtype-conventions.html. Just follow it to make your code more readable.

All source code is available in my skydrive: http://cid-481cbe104492a3af.office.live.com/browse.aspx/share/dev/TestOO. In the TestGObject-{date}.zip/TestGObject5 folder.

Categories: C/C++ Tags: ,

OOP Using GObject (4) – An Inheritable Class

March 11th, 2011 No comments

Now, we will begin to implement some real OO mechanism using GObject library. In this article, we will make our fundamental type Inheritable.

Here’s comes our Base type:

NOTE: PLEASE READ ALL COMMENT CAREFULLY.

// base.h
#ifndef BASE_H_
#define BASE_H_

#include <glib-object.h>

/* Base object struct */
typedef struct _Base {
    GTypeInstance parent;
    /* instance variable, should be hidden */
    gint base_instance_i;
} Base;

/* Base class struct */
typedef struct _BaseClass {
    GTypeClass parent;
    /* instance method, used as a virtual method */
    void (*base_instance_dump)(struct _Base *instance);
} BaseClass;

/* static method of Base class */
void base_class_set_i(gint i);

/* non-virtual public method for Base object */
void base_instance_set_i(Base *instance, gint i);

/* virtual public method for Base object, both version are supported */
void base_instance_dump(Base *instance);

/* type method */
GType base_get_type();


#endif /* BASE_H_ */
// base.c
#include "base.h"

/* static field of Base class */
gint base_class_i;

/* static method of Base class */
void base_class_set_i(gint i) {
    base_class_i = i;
    g_print("Invoking base_class_set_i(): base_class_i=%d\n", base_class_i);
}

void base_instance_set_i(Base *instance, gint i) {
    instance->base_instance_i = i;
    g_print("Invoking base_instance_set_i(): base_instance_i=%d\n", instance->base_instance_i);
}

void base_instance_dump(Base *instance) {
    g_print("Invoking base_instance_dump(): base_instance_i=%d\n", instance->base_instance_i);
}

static void base_class_init(BaseClass *klass, gpointer data) {
    base_class_i = 100;
    g_print("Calling base_class_init(): base_class_i=%d\n", base_class_i);
    klass->base_instance_dump = base_instance_dump;
}

static void base_instance_init(Base *instance, gpointer data) {
    instance->base_instance_i = 200;
    g_print("Calling base_instance_init(): base_instance_i=%d\n", instance->base_instance_i);
}

GType base_get_type() {
    static GType base_type = 0;
    if (base_type == 0) {
        static const GTypeInfo base_type_info = {
            sizeof(BaseClass),  /* class_size */
            NULL,               /* base_init */
            NULL,               /* base_finalize */
            (GClassInitFunc)base_class_init, /* class_init */
            NULL,               /* class_finalize */
            NULL,               /* class_data */
            sizeof(Base),       /* instance_size */
            0,                  /* n_preallocs */
            (GInstanceInitFunc)base_instance_init, /* instance_init */
            NULL                /* value_table */
        };
        GTypeFundamentalInfo foo_type_fundamental_info = {
            G_TYPE_FLAG_CLASSED           /* Indicates a classed type */
            | G_TYPE_FLAG_INSTANTIATABLE  /* Indicates an instantiable type */
            | G_TYPE_FLAG_DERIVABLE       /* Indicates a flat derivable type */
            | G_TYPE_FLAG_DEEP_DERIVABLE  /* Indicates a deep derivable type */
        };
        base_type = g_type_register_fundamental(g_type_fundamental_next(),
            "BaseFundamentalType", &base_type_info, &foo_type_fundamental_info, 0);
    }
    return base_type;
}

In base_instance_init(), we assigned the base_instance_dump() callback. Thus, we can invoke this function by both global function or instance function of BaseClass class. Additional flags G_TYPE_FLAG_DERIVABLE and G_TYPE_FLAG_DEEP_DERIVABLE are also passed to the GTypeFundamentalInfo struct to enable inheritance.

It’s time to define our Derived type:

// derived.h
#ifndef DERIVED_H_
#define DERIVED_H_

#include "base.h"
#include <glib-object.h>

/* Derived object struct */
typedef struct _Derived {
    /* The GTypeClass structure is still the first member of the class structure */
    Base parent;
    /* should be hidden */
    gint derived_instance_i;
} Derived;

/* Derived class struct */
typedef struct _DerivedClass {
    /* The TypeInstance structure is still the first member of the instance structure */
    BaseClass parent;
} DerivedClass;

/* non-virtual public method for Derived object */
void derived_instance_set_i(Derived *instance, gint i);

/* (Overwrite) virtual public method for Derived object, both version are supported */
void derived_instance_dump(Base *instance);

/* type method */
GType derived_get_type();


#endif /* DERIVED_H_ */
// derived.c
#include "derived.h"

void derived_instance_set_i(Derived *instance, gint i) {
    instance->derived_instance_i = i;
    g_print("Invoking derived_instance_set_i(): derived_instance_i=%d\n", instance->derived_instance_i);
}

void derived_instance_dump(Base *instance) {
    Derived *derived = G_TYPE_CHECK_INSTANCE_CAST(instance, derived_get_type(), Derived);
    g_print("Invoking derived_instance_dump(): base_instance_i=%d, derived_instance_i=%d\n", instance->base_instance_i, derived->derived_instance_i);
}

static void derived_class_init(DerivedClass *klass, gpointer data) {
    g_print("Calling derived_class_init()\n");
    base_class_set_i(300);
    /* override */
    BaseClass *base_klass = G_TYPE_CHECK_CLASS_CAST(klass, base_get_type(), BaseClass);
    base_klass->base_instance_dump = derived_instance_dump;
}

static void derived_instance_init(Derived *instance, gpointer data) {
    instance->derived_instance_i = 400;
    g_print("Calling derived_instance_init(): derived_instance_i=%d\n", instance->derived_instance_i);
}

GType derived_get_type() {
    static GType derived_type = 0;
    if(derived_type == 0) {
        static const GTypeInfo derived_type_info = {
            sizeof(DerivedClass), /* class_size */
            NULL,               /* base_init */
            NULL,               /* base_finalize */
            (GClassInitFunc)derived_class_init, /* class_init */
            NULL,               /* class_finalize */
            NULL,               /* class_data */
            sizeof(Derived),    /* instance_size */
            0,                  /* n_preallocs */
            (GInstanceInitFunc)derived_instance_init, /* instance_init */
            NULL                /* value_table */
        };
        derived_type = g_type_register_static(
            base_get_type(), "DerivedStaticClass", &derived_type_info, 0);
    }
    return derived_type;
}

Our Derived type inherits Base by replacing GTypeClass and GTypeInstance with the corresponding struct of the Base type. According to the memory layout of structs, GTypeClass and GTypeInstance are still the first member of corresponding struct. In derived_get_type(), we register Derived type using g_type_register_static() since it’s not a fundamental at all. And the first parameter is the type id of Base type.

Let’s have some time to look up how to implement polymorphism. In derived_class_init(), we re-assign the base_instance_dump() callback to from the Base‘s implementation to Derived‘s implementation.

Test code:

int main() {
    g_type_init();
    my_dump_type(base_get_type());
    my_dump_type(derived_get_type());

    /*
     * Official document:
     * Use of g_type_create_instance() is reserved for implementators of
     * fundamental types only. E.g. instances of the GObject hierarchy should
     * be created via g_object_new() and never directly through
     * g_type_create_instance() which doesn't handle things like singleton
     * objects or object construction.
     */
    Base *base = (Base *)g_type_create_instance(base_get_type());
    base_class_set_i(101);
    base_instance_set_i(base, 201);
    Derived *derived = (Derived *)g_type_create_instance(derived_get_type());
    derived_instance_set_i(derived, 401);

    /* Test polymorphism */
    Base *instances[2] = { base, (Base *)derived };
    int i;
    for (i = 0; i < 2; i++) {
        Base *inst = instances[i];
        BaseClass *klass = G_TYPE_INSTANCE_GET_CLASS(inst, base_get_type(), BaseClass);
        klass->base_instance_dump(inst);
    }

    return 0;
}

All source code is available in my skydrive: http://cid-481cbe104492a3af.office.live.com/browse.aspx/share/dev/TestOO. In the TestGObject-{date}.zip/TestGObject4 folder.

Categories: C/C++ Tags: ,

OOP Using GObject (3) – An Instantiatable Class

March 11th, 2011 No comments

We will make fundamental type instantiatable and complete our first usage sample in this article. An object class should be defined firstly:

NOTE: PLEASE READ ALL COMMENT CAREFULLY.

/* Foo object struct */
typedef struct _Foo {
    /*
     * Official document:
     * All instance structures must contain as first member a TypeInstance structure.
     */
    GTypeInstance parent;
    /* instance variable */
    int foo_instance_i;
} Foo;

Also, we re-define the class struct:

/* Foo class struct */
typedef struct _FooClass {
    /*
     * Official document:
     * All class structures must contain as first member a GTypeClass structure.
     */
    GTypeClass parent;
} FooClass;

/* static field of Foo class */
int foo_class_i;

/* static method of Foo class */
void foo_class_set_i(int i) {
    foo_class_i = i;
    printf("Invoking foo_class_set_i(): foo_class_i=%d\n", foo_class_i);
}

GTypeClass should be the first member of a class struct, while TypeInstance the first member of a object struct. You may wonder why there’s two int variable in both struct. The foo_class_i is like a static variable in C++ class, while The foo_instance_i is like an instance variable in C++ class. And remember fields in a class struct? It is used as meta info.

The registry function also need modification:

GType foo_get_type() {
    static GType foo_type = 0;
    if (foo_type == 0) {
        static const GTypeInfo foo_type_info = {
            sizeof(FooClass),   /* class_size */
            NULL,               /* base_init */
            NULL,               /* base_finalize */
            (GClassInitFunc)foo_class_init, /* class_init */
            NULL,               /* class_finalize */
            NULL,               /* class_data */
            sizeof(Foo),        /* instance_size */
            0,                  /* n_preallocs */
            (GInstanceInitFunc)foo_instance_init, /* instance_init */
            NULL                /* value_table */
        };
        /* G_TYPE_FLAG_INSTANTIATABLE: Indicates an instantiable type (implies classed) */
        GTypeFundamentalInfo foo_type_fundamental_info = {
            G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_INSTANTIATABLE
        };
        foo_type = g_type_register_fundamental(g_type_fundamental_next(),
            "FooClassedFundamentalType", &foo_type_info, &foo_type_fundamental_info, 0);
    }
    return foo_type;
}

We assigned the instance_init() callback. It is called when a instance of our Foo class is created. You may ask where is the corresponding instance_finalize() callback? Hey, we will discuss it in upcoming articles. The instance_init() callback can be regarded as the constructor of a C++ class. Note, an additional G_TYPE_FLAG_INSTANTIATABLE flag is also added in the GTypeFundamentalInfo struct.

Let’s see how to create an instance:

int main() {
    g_type_init();
    my_dump_type(foo_get_type());

    /* Use g_type_create_instance if implement a fundamental class */
    Foo *foo = (Foo *)g_type_create_instance(foo_get_type());
    foo_class_set_i(101);
    foo_instance_set_i(foo, 201);

    printf("Is instance of int? %s\n",
        G_TYPE_CHECK_INSTANCE_TYPE(foo, G_TYPE_INT) ? "yes" : "no");
    printf("Is instance of FooClassedFundamentalType? %s\n",
        G_TYPE_CHECK_INSTANCE_TYPE(foo, foo_get_type()) ? "yes" : "no");

    return 0;
}

Congratulations! You’ve finished the learning of our fundamental sample.

All source code is available in my skydrive: http://cid-481cbe104492a3af.office.live.com/browse.aspx/share/dev/TestOO. In the TestGObject-{date}.zip/TestGObject3 folder.

Categories: C/C++ Tags: ,

OOP Using GObject (2) – A Classed Type

March 11th, 2011 No comments

In last article, we defined a fundamental type. But nothing can be done with it. Now, we will extend it to be a classed type, say adding class info into our fundamental type. To do this, we should define a class struct, which can be regard as the meta info of a C++ class:

NOTE: PLEASE READ ALL COMMENT CAREFULLY.

typedef struct _FooClass {
    /*
     * Official document:
     * All class structures must contain as first member a GTypeClass structure.
     */
    GTypeClass parent;
    /*
     * Since glib 2.24, there're new functions to keep privacy.
     */
    int i;
    void (*bar)();
} FooClass;

GTypeClass should be the first member of a class struct. You can image the i field to be the version of the class. And we can add a string field to hold the author of this class. There’s also a function pointer bar(). As you may already know, it is used to implement polymorphism, which can be regard as virtual function of a C++ class.

When registering our fundamental type, additional field in GTypeInfo and GTypeFundamentalInfo are filled:

GType foo_get_type() {
    static GType foo_type = 0;
    if (foo_type == 0) {
        static const GTypeInfo foo_type_info = {
            sizeof(FooClass),   /* class_size */
            NULL,               /* base_init */
            NULL,               /* base_finalize */
            (GClassInitFunc)foo_class_init, /* class_init */
            NULL,               /* class_finalize */
            NULL,               /* class_data */
            0,                  /* instance_size */
            0,                  /* n_preallocs */
            NULL,               /* instance_init */
            NULL                /* value_table */
        };
        /* G_TYPE_FLAG_CLASSED: Indicates a classed type */
        GTypeFundamentalInfo foo_type_fundamental_info = { G_TYPE_FLAG_CLASSED };
        foo_type = g_type_register_fundamental(g_type_fundamental_next(),
            "FooClassedFundamentalType", &foo_type_info, &foo_type_fundamental_info, 0);
    }
    return foo_type;
}

GTypeInfo is the key data structure of GObject type system. It defines how a classed type should be initialized and finalized. Here, we just assigned the class_init() callback. It is called when our FooClass needs initialization. For fundamental and static types, their class_finalize() are never called. We will demo the usage of this callback when introducing dynamic types. Please also note the G_TYPE_FLAG_CLASSED flag passed into GTypeFundamentalInfo struct.

Now, let’s implement our foo_class_init() function. This function is used to initialize fields and assign virtual functions in most time:

void foo_class_bar();

void foo_class_init(FooClass *klass, gpointer data) {
    klass->i = 129;
    klass->bar = foo_class_bar;
    printf("Calling foo_class_init(): i=%d\n", klass->i);
}

void foo_class_bar() {
    printf("Invoking foo_class_bar()\n");
}

Now, we’ve finished our definition of the class struct. Let’s see how to use it:

int main() {
    g_type_init();
    my_dump_type(foo_get_type());

    FooClass *klass = (FooClass *)g_type_class_ref(foo_get_type());
    klass->bar();
    g_type_class_unref(klass);

    return 0;
}

See? We use g_type_class_ref() and g_type_class_unref() to ref/unref a class, and invoke a function. But its function is still limited. We can just get/set its meta info. It still cannot be instantiated. This will be discussed in the next article.

All source code is available in my skydrive: http://cid-481cbe104492a3af.office.live.com/browse.aspx/share/dev/TestOO. In the TestGObject-{date}.zip/TestGObject2 folder.

Categories: C/C++ Tags: ,

OOP Using GObject (1) – A Fundamental Type

March 11th, 2011 No comments

These days, I tried to write C code with OO support. I found GObject is such a well-designed library to simplify my implementation. However, the official documents is not so clear sometimes.  It do not provide sufficient information about all its stuff. I had to write my own demo applications to test the usage of some function. Moreover, the source code were also checked for reference.

There are 3 types in GObject type system: Fundamental, static and dynamic. Fundamental types are top-most types. The do not have parent types. They are seldom used, since all fundamental types are pre-defined rather than user-defined.

In this article, I will try to define a fundamental type using in GObject.Here’s the code on how to register a basic fundamental type in GObject, and how it can be used.

NOTE: PLEASE READ ALL COMMENT CAREFULLY.

#include <stdio.h>
#include <glib-object.h>

int main() {
    /* Initialize type system */
    g_type_init();

    /* This is not important */
    GTypeInfo my_type_info = { 0, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL };
    /* This is not important */
    GTypeFundamentalInfo my_type_fundamental_info = { 0 };
    /* Register a fundamental type */
    GType my_type_id = g_type_register_fundamental(g_type_fundamental_next(),
        "MyFundamentalType", &my_type_info, &my_type_fundamental_info, 0);

    /* Print type names */
    printf("Type name of int: %s\n", g_type_name(G_TYPE_INT));
    printf("Type name of float: %s\n", g_type_name(G_TYPE_FLOAT));
    printf("Type name of object: %s\n", g_type_name(G_TYPE_OBJECT));
    printf("Type name of my fundamental type: %s\n", g_type_name(my_type_id));
    /* Print type id and name of MyFundamentalType */
    printf("Type id: %d\n", g_type_from_name("MyFundamentalType"));
    printf("Type name: %s\n", g_type_name(g_type_from_name("MyFundamentalType")));
    /* Print attributes of MyFundamentalType */
    printf("Is fundamental? %s\n", G_TYPE_IS_FUNDAMENTAL(my_type_id) ? "yes" : "no");
    printf("Is derived? %s\n", G_TYPE_IS_DERIVED(my_type_id) ? "yes" : "no");
    printf("Is interface? %s\n", G_TYPE_IS_INTERFACE(my_type_id) ? "yes" : "no");
    printf("Is classed? %s\n", G_TYPE_IS_CLASSED(my_type_id) ? "yes" : "no");
    printf("Is instantiatable? %s\n", G_TYPE_IS_INSTANTIATABLE(my_type_id) ? "yes" : "no");
    printf("Is derivable? %s\n", G_TYPE_IS_DERIVABLE(my_type_id) ? "yes" : "no");
    printf("Is deep derivable? %s\n", G_TYPE_IS_DEEP_DERIVABLE(my_type_id) ? "yes" : "no");
    printf("Is abstract? %s\n", G_TYPE_IS_ABSTRACT(my_type_id) ? "yes" : "no");
    printf("Is value abstract? %s\n", G_TYPE_IS_VALUE_ABSTRACT(my_type_id) ? "yes" : "no");
    printf("Is value type: %s\n", G_TYPE_IS_VALUE_TYPE(my_type_id) ? "yes" : "no");
    printf("Has value table: %s\n", G_TYPE_HAS_VALUE_TABLE(my_type_id) ? "yes" : "no");

    return 0;
}

My fundamental type is created by calling g_type_register_fundamental() function. A GTypeInfo and a GTypeFundamentalInfo struct are passed as parameters. And here comes the linux Makefile. You can use pkg-config to replace my include and lib paths:

CC      := gcc
CFLAGS  := -ansi -Wall
INCPATH := -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include
LIBS    := -lgobject-2.0
TARGET  := TestGObject1
OBJS    := main.o

all: $(OBJS)
    $(CC) $(CFLAGS) ${LIBS} $(OBJS) -o $(TARGET)

%.o: %.c
    $(CC) $(CFLAGS) $(INCPATH) -c $<

clean:
    rm -f *~
    rm -f *.o
    rm -f $(TARGET)

NOTE: REPLACE THE WHITESPACE WITH TAB IN MAKEFILES, IF YOU COPY THE CODE DIRECTLY.

The fundamental type is of no use at all presently. In the next article, I will extend my code to add class meta info.

All source code is available in my skydrive: http://cid-481cbe104492a3af.office.live.com/browse.aspx/share/dev/TestOO. In the TestGObject-{date}.zip/TestGObject1 folder.

Categories: C/C++ Tags: ,