This covers everything except signals, which is the last thing to do.

Warning currently its in plain text format since I haven't gotten around
to fancifying it yet, however it WILL be prettied up.

I'd appreciate feedback on technical, spelling, grammar, or style
points.

Cheers,
Ryan
GObject Tutorial
Copyright Ryan McDougall (2004)

Purpose
-----
This document is used for two purposes: one is as a tutorial on learning Glib's 
GObject Type System, and the other is a step-by-step how-to for using the system. The 
tutorial proceeds from the point of view of designing an Object-Oriented type system 
in C, where GObject is the presumed solution. It is thought that this manner of 
presenting the material will better justify the form that the library currently takes, 
and help explain the steps required use it. The how-to is presented after the tutorial 
in a step-by-step, matter-of-fact, form with no explanations, so that it will be 
useful to the merely pragmatic programmer.

Audience
-----
The tutorial is meant for those who are familiar with OO concepts, but are just 
beginning to learn GObject or GTK+. I will assume previous knowledge of an OO'ed 
language, and a basic command of C.

Motivation
-----
While writing an OO system in a language that doesn't support it may sound like a 
foolish exercise in masochism to some, there are indeed some good reasons why one 
would want to do such a thing. While I will not try to justify the authors' decision, 
and will assume that the reader has some good reason for using Glib, I will point out 
some important features of the system: 
- C is the most portable programming language
- system is fully dynamic, so types can be added at run-time
- system is more extensible than a standard language, so new features can be added 
quickly

In OO languages object oriented features and abilities are a matter of syntax. However 
since C doesn't support OO natively, the GObject system has to graft on object 
orientation manually. Often this requires some tedious, or occasionally mystifying 
things in order to accomplish this goal. It is my intention to enumerate all the 
necessary steps and incantations necessary to make this process work; and hopefully 
even elaborate on what it means to your program.

1. Creating a single Object with no Inheritance

Design
-----
In OO, an object consists of two types of members bound under one object reference: 
data fields and method functions. One way to accomplish this in C is with C structs, 
where data fields are regular public members and methods are implemented as function 
pointers. This implementation however has several serious flaws: awkward syntax, 
type-safety, and lack of encapsulation to name a few. However there is more practical 
problem -- it is a serious waste of space. Every instance object needs a 4-byte 
pointer for each of its methods; all of which will be identical class wide, and thus 
totally redundant. That is to say if we have a class with only four methods, and a 
program with 1000 instantiation objects of that class, we are wasting almost 16KB. 
Clearly we'd be better off memory-wise if we only kept one copy of those pointers in a 
table that could be accessed by any object in its class.

Such as table is called a virtual method table (vtable), and one copy is kept in 
memory by the GObject system for each class. When you want to call a virtual method, 
you must ask the system to find the object's vtable; which as we have said above is 
just a struct with function pointers. With this you can now dereference the pointer 
and thus call the method. 

<definition>
We will call these two types "instance struct" and "class struct", and instantiations 
of those structs "instance objects" and "class objects" respectively. The combination 
of the two structs as a conceptual unit will be called a "class" and an instantiation 
of that class will be called an "object".
</definition>

The reason why functions given by this process are called "virtual" is because it 
dynamically looks up the appropriate function pointer at run-time and thus allows 
inherited classes to override a class method (by simply assigning a new function 
pointer to the corresponding entry in the vtable). This allows derived objects to 
behave correctly when cast to a base class, and corresponds to what we know of virtual 
methods in C++. 

<convention>
Although this saves space and allows virtual methods, it also means that methods can 
no longer be tied syntactically to an object via the dot operator.Therefore we will 
use the convention that class methods will be called on objects as follows:

NAMESPACE_TYPE_METHOD (OBJECT*, PARAMETERS)
</convention>

Non-virtual methods will be implemented inside a regular C function, and virtual 
functions will be implemented by calling the appropriate method from the vtable inside 
a regular C function. Private methods will be implemented within the source file, but 
not be exported via the header file. 

<notice>
While OO normally uses information hiding as part of encapsulation, there is no easy 
way to hide private members in C. One method is to put your private members into a 
separate struct that is defined in the source file only, then place a pointer to the 
private class in your public object struct. However, in a open-source world this is 
small protection against a user determined to do the wrong thing. Most developers 
simply label with a comment those members they wish to protect as private, and hope 
the user respects the distinction.
</notice>

At this point we have two distinct structs, and no obvious way to find to get the 
proper vtable given an instantiated object. As we implied above, it is the system's 
responsibility to do so, and it is able to handle this only by requiring us to 
register the types we create. The system also requires us to register both (instance 
and class) structs' initialization and finalization functions (and some other 
important information), which will allow the system to properly instantiate our 
objects. The system keeps track of our objects by enumerating all types registered 
with it, and requiring that all instance objects start with a pointer to its own class 
vtable, and each vtable start with the number that corresponds to its enumerated type.

<notice>
The type system requires that all types' instance and class structs start with with a 
special struct. In the case of instance structs, this struct is basically just a 
pointer to the type's class object. Since C guarantees that the first member declared 
in a struct is also the first found in memory, one can get the class object quickly by 
simply casting the instance object. Since the type system also requires that we 
declare the parent struct as the first member when we use inheritance, this same 
feature means that we need only declare this special struct once in the parent class, 
and we can always find the vtable of any instance object via a cast.
</notice>

Lastly we need some functions to define how our objects' lifetimes are managed: a 
function to call when we wish to create a class object, a function to call when we 
wish to create an instance object, and a function to call when we are finished with a 
class object. We do not include a function for finalizing instance objects because 
instance memory management is generally a complicated problem, and we wish to leave 
this up to higher levels of code to handle.

Code (header)
-----

<step>
Create "C-style" objects using the struct keyword to implement our instance and class 
objects.
</step>

<notice>
We prepend an underscore to the name of our struct, then add a forward typedef which 
gives our struct a proper name. This is due to the grammar of C, which won't let you 
declare SomeObject pointers inside SomeObject (which is handy for such things as 
linked lists). We have also created an artificial namespace called "Some", as 
described by our convention above.
</notice>

/* Our "Instance struct" defines all the data fields that make our object unique. */
typedef struct _SomeObject SomeObject;
struct _SomeObject
{
        GTypeInstance   gtype;

        gint            m_a;
        gchar*          m_b;
        gfloat          m_c;
};

/* Our "Class struct" defines all the method functions that our objects will share. */
typedef struct _SomeObjectClass SomeObjectClass;
struct _SomeObjectClass
{
        GTypeClass      gtypeclass;

        void            (*method1)      (SomeObject *self, gint);
        void            (*method2)      (SomeObject *self, gchar*);
};

<step>
Declare the function that will both register our type in the system upon first use, 
then thereafter will return the unique number that the system uses to track the types 
we declare. We will call this function get_type and have it return a GType, which is 
the integral type the system declares to identify registered types. Since this 
function is specific to our type SomeObject by design and definition, we prepend 
"some_object_".
</step>

/* This method returns the GType associated with our new object type. */
GType   some_object_get_type (void);

<step>
Declare the functions which manage our objects' lifetimes; that is they set up an 
object on instantiation, and tear it down on finalization.
</step>

/* These are the Class/Instance Initialize/Finalize functions. Their signature is 
determined in gtype.h. */
void    some_object_class_init          (gpointer g_class, gpointer class_data);
void    some_object_class_final         (gpointer g_class, gpointer class_data);
void    some_object_instance_init       (GTypeInstance *instance, gpointer g_class);

<step>
Declare our class methods using the C function convention we defined above.
</step>

/* All these functions are methods of SomeObject. */
void    some_object_method1 (SomeObject *self, gint);   /* virtual */
void    some_object_method2 (SomeObject *self, gchar*); /* virtual */
void    some_object_method3 (SomeObject *self, gfloat); /* non-virtual */

<step>
Create some boiler-plate code which will make our code comply to existing standards 
and generally make our lives easier.
</step>

/* Handy macros */
#define SOME_OBJECT_TYPE                (some_object_get_type ())
#define SOME_OBJECT(obj)                (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
SOME_OBJECT_TYPE, SomeObject))
#define SOME_OBJECT_CLASS(c)            (G_TYPE_CHECK_CLASS_CAST ((c), 
SOME_OBJECT_TYPE, SomeObjectClass))
#define SOME_IS_OBJECT(obj)             (G_TYPE_CHECK_TYPE ((obj), SOME_OBJECT_TYPE))
#define SOME_IS_OBJECT_CLASS(c)         (G_TYPE_CHECK_CLASS_TYPE ((c), 
SOME_OBJECT_TYPE))
#define SOME_OBJECT_GET_CLASS(obj)      (G_TYPE_INSTANCE_GET_CLASS ((obj), 
SOME_OBJECT_TYPE, SomeObjectClass))

Code (source)
-----
Now we can move on to implementing in the source file what we've just declared. 

<notice>
Since our virtual methods are just function pointers, we have to create some normal C 
functions that actually reside in addressable memory (declared as ending in "_impl" 
and *not* exported in the header), which actually implement the code we want to point 
to.
</notice>

<notice>
All functions preceded by "some_object_" are specific to SomeObject by definition; 
usually because we explicitly cast various pointers to SomeObject, or make use of some 
other class specific feature.
</notice>

<step>
Implement the code corresponding to our virtual methods.
</step>

/* Implementation of virtual functions. */
void    some_object_method1_impl (SomeObject *self, gint a)
{
        self->m_a = a;
        g_print ("Method1: %i\n", self->m_a);
}

void    some_object_method2_impl (SomeObject *self, gchar* b)
{
        self->m_b = b;
        g_print ("Method2: %s\n", self->m_b);
}

<step>
Implement the code for all public methods. In the case of virtual methods, we must use 
the macro "GET_CLASS" to ask the type system to return the class object so we can 
access the vtable where our virtual methods reside. If the method is non-virtual, we 
just write the code.
</step>

/* Public methods. */
void    some_object_method1 (SomeObject *self, gint a)
{
        SOME_OBJECT_GET_CLASS (self)->method1 (self, a);
}

void    some_object_method2 (SomeObject *self, gchar* b)
{
        SOME_OBJECT_GET_CLASS (self)->method2 (self, b);
}

void    some_object_method3 (SomeObject *self, gfloat c)
{
        self->m_c = c;
        g_print ("Method3: %f\n", self->m_c);
}

<step>
Implement the code for initialization/finalization. We are given generic pointers by 
the system (that we trust points to a proper object), so we must cast them to the 
appropriate type before we can do anything.
</step>

/* This is called when the class object is created. */
void    some_object_class_init          (gpointer g_class, gpointer class_data)
{
        SomeObjectClass *this_class     = SOME_OBJECT_CLASS (g_class);
        
        /* fill in the class struct members (in this case just a vtable) */
        this_class->method1 = &some_object_method1_impl;
        this_class->method2 = &some_object_method2_impl;
}

/* This is called when the class object is no longer used. */
void    some_object_class_final         (gpointer g_class, gpointer class_data)
{
        /* No class finalization needed since the class object holds no 
        pointers or references to any dynamic resources which would need 
        to be released when the class object is no longer in use. */
}

/* This is called when a instance object is created. The instance's class is passed as 
g_class. */
void    some_object_instance_init       (GTypeInstance *instance, gpointer g_class)
{
        SomeObject *this_object = SOME_OBJECT (instance);

        /* fill in the instance struct members */
        this_object->m_a = 42;
        this_object->m_b = 3.14;
        this_object->m_c = NULL;
}

<step>
Implement a function for informing the caller of SomeObject's GType. The first time 
this function is run, it determines the GType by fully registering SomeObject with the 
system. Thereafter the GType is stored in a static variable, and returns without any 
processing. While its possible to register the type in a separate function, this 
implementation ensures that the type is always registered before its used, which is 
usually when the first object is instantiated.
</step>

/* Since there is no base class to derive from, base_init/finalize are NULL */
GType   some_object_get_type (void)
{
        static GType type = 0;

        if (type == 0) 
        {
                /* This is the structure that the system uses to fully describe
                how a type should be created, initialized and finalized. */

                static const GTypeInfo type_info = 
                {
                        sizeof (SomeObjectClass),
                        NULL,                           /* base_init */
                        NULL,                           /* base_finalize */
                        some_object_class_init,         /* class_init */
                        some_object_class_final,        /* class_finalize */
                        NULL,                           /* class_data */
                        sizeof (SomeObject),
                        0,                              /* n_preallocs */
                        some_object_instance_init       /* instance_init */
                };

                /* Since our type has no parent, it is considered 
                "fundamental", and we have to inform the system that our
                type is both classed (unlike say a float, int, or pointer),
                and is instantiable (the system can create instance objects.
                for example, Interfaces or Abstract classes are not 
                instantiable. */

                static const GTypeFundamentalInfo fundamental_info =
                {
                        G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_INSTANTIATABLE
                };      

                type = g_type_register_fundamental
                (
                        g_type_fundamental_next (),     /* next available GType number 
*/
                        "SomeObjectType",               /* type name as string */
                        &type_info,                     /* type info as above */
                        &fundamental_info,              /* fundamental info as above */
                        0                               /* type is not abstract */
                );
        }

        return  type;
}

/* Lets build a simple test driver for out our code! */

int     main()
{
        SomeObject      *testobj = NULL;

        /* This gets the type system up and running. */
        g_type_init ();

        /* Create an instance object from the system. */
        testobj = SOME_OBJECT (g_type_create_instance (some_object_get_type()));

        /* Call our methods. */
        if (testobj)
        {
                g_print ("%d\n", testobj->m_a);
                some_object_method1 (testobj, 32);

                g_print ("%s\n", testobj->m_b);
                some_object_method2 (testobj, "New string.");

                g_print ("%f\n", testobj->m_c);
                some_object_method3 (testobj, 6.9);
        }

        return  0;
}

Final Thoughts
-----

We have implemented our first object in C, but it was a lot of work, and there is no 
real object orientation here since we have purposely not said anything about 
inheritance. In the next section we will see how to make our lives much easier when 
interacting with other people's code by deriving our class SomeObject from the 
built-in base class GObject. 

<notice>
While we will reuse the ideas and patterns we have discussed above throughout the 
text, attempting to create a fundamental type that behaves as it should with other 
GTK+ code is very difficult and in-depth. It is recommended that you always create new 
types by deriving from GObject, since it does a lot of background work to make things 
behave how GTK+ assumes it should.
</notice>

2. Making use of built-in Macros to auto generate code

Design
-----
As you may have noticed, much of what we have done above ranges from the merely 
mechanical, to down-right boiler-plate. Most of our funtions are not general-purpose, 
and have to be individually re-written for each type we create. Surely this is why we 
have computers in the first place -- to automate such tasks and make our lives 
simpler! 

Well, we are in luck, since C's preprocessor will allow us to write macros that allow 
us to simply define a new type, which at compile time expands into the proper C code. 
Using macros also helps reduce human errors which may appear when declaring everything 
manually.

However with this automation we lose flexibility. There are a huge number of possible 
variations on the steps we have described above that we may want to make use of, but a 
macro can only implement one expansion. If the macro provides a light-weight 
expansion, and we want a complete type, then we have to write a lot of manual code 
anyways. If the macro provides a complete expansion, and we want a light-weight type 
then we either end up with code bloat, spending a lot of time filling in stubs we will 
never use, or just plain incorrect code. There is also the fact that C's preprocessor 
was simply not designed to do the find of code generation we are interested in, and 
has limited features we can make use of.

Code
-----
The code for creating a new type is pretty simple: 
G_DEFINE_TYPE_EXTENDED (TypeName, function_prefix, PARENT_TYPE, GTypeFlags, CODE)

The first parameter is the name of the type. The second is the prefix that will be 
prepended to the names of functions, and thus conform to our naming convention. The 
third is the GType of the object that we wish to inherit from. The fourth is the 
GTypeFlag which will be added to the GTypeInfo struct. The fifth parameter is for any 
code we want to run immediately after the type has been registered.

What would be more enlightening, however, would be to look at what code the above 
actually expands to.

G_DEFINE_TYPE_EXTENDED (SomeObject, some_object, 0, some_function())

<notice>
The exact code which the macro will expand to is dependent on the version's 
implementation. You should always check the expansion before you make assumptions 
about what a macro genereates.
</notice>

expands to (after cleaning up the whitespace):

static void some_object_init (SomeObject *self);
static void some_object_class_init (SomeObjectClass *klass);
static gpointer some_object_parent_class = ((void *)0);

static void some_object_class_intern_init (gpointer klass) 
{
        some_object_parent_class = g_type_class_peek_parent (klass); 
        some_object_class_init ((SomeObjectClass*) klass);
} 

GType some_object_get_type (void) 
{
        static GType g_define_type_id = 0; 
        if ((g_define_type_id == 0)) 
        { 
                static const GTypeInfo g_define_type_info = 
                { 
                        sizeof (SomeObjectClass), 
                        (GBaseInitFunc) ((void *)0), 
                        (GBaseFinalizeFunc) ((void *)0), 
                        (GClassInitFunc) some_object_class_intern_init, 
                        (GClassFinalizeFunc) ((void *)0), 
                        ((void *)0), 
                        sizeof (SomeObject), 
                        0, 
                        (GInstanceInitFunc) some_object_init, 
                }; 

                g_define_type_id = g_type_register_static 
                (
                        G_TYPE_OBJECT, 
                        "SomeObject", 
                        &g_define_type_info, 
                        (GTypeFlags) 0
                );
                
                { some_function(); } 
        } 

        return g_define_type_id; 
}

<notice>
The macro defines a static variable called "<PREFIX>_parent_class", which is a pointer 
to the parent class of the object we are creating. This is needed when you want to 
discover the parent of the virtual method, and not the parent of the given GObject 
derived object, and is used primarily in chaining dispose/finalize, which are almost 
always virtual. Our code further on will not use this construct, since there are 
functions that do this without keeping a static variable.
</notice>

As you should have noticed, there is not code generated for base_init/finalize or 
class_finalize. If you need these functions, then you should write your code from 
scratch.
 
3. Creating a single Object derived from GObject

Design
-----

While we can now make a rudimentary object, we have intentionally ignored the context 
of our type system: as the basis of a sophisticated suite of libraries; chiefly the 
GUI library GTK+. The design of GTK+ calls for a root object that all others derive 
from. This allows a least common denominator functionality that all objects will 
share: signal support (for passing messages easily from one object to another), 
lifetime management via reference counting, properties support (for easily setting and 
getting an object's data fields), and constructor/destructor support (which know how 
to setup signals,refernces and properties). When we derive our object from GObject, we 
get all of the above, and generally make things easier when interacting with other 
GObject based libraries. However, we will not talk about signals, reference counting, 
properties, or any other specific features in this chapter; instead this chapter will 
concentrate on how inheritance works in the type system.

As we already know, if LuxuryCar inherits from Car, we know that LuxuryCar *is* a Car, 
plus some new specific features. How can we get our system to mimic this behaviour? We 
can use a trick that exploits a feature of C structs: the first member listed in a 
struct's definition must be the first member found in memory. If we insist that all 
objects declare their parent as the first member of their own struct, then we can 
quickly turn a pointer to any object into a pointer to its parent by simply casting 
the pointer to it's parent! While this trick is very handy, and syntactically very 
clean, this kind of cast only works with pointers -- you can't cast regular structs 
this way.

<notice>
This casting trick is *not* at all type safe. Casting an object to something other 
than its parent is perfectly valid, but highly unwise. It is up to the programmer to 
ensure his casts are safe.
</notice>

Creating Type Instances
-----
With this technique in mind, how does the type system instantiate objects? The first 
time we ask the system to create an instance object through the function 
g_type_create_instance, it must first create a class object for the instance to use. 
If the class struct is derived from another, the system needs to create and initialize 
those parent class objects too. The system can do this because we have specified a 
structure (the GTypeInfo struct in *_get_type), that describes each object's instance 
size, class size, initialization functions, and finalize functions.

- to instanciate an object with g_type_create_instace
        if there is no corresponding class object
                create it and add it to the class hierarchy
        create instance object and return a pointer to it

When the system creates a new class object, it first allocates enough space to put the 
final requested class object in. It then proceeds from parent-most class object to 
child-most class object in the inheritance tree, overwriting the final class object's 
fields with its parents' at the memory level. This is how children inherit from their 
parents. After it has copied one parent's data over, the system runs that parent 
class's "base_init" function on the class object in its current state. This process of 
overwriting and executing "base_init"s continues until it has been completed for every 
parent of the final class. Next the system runs the final class's "base_init" and 
"class_init" on the final class object. The function "class_init" takes a parameter, 
which could be considered a class object constructor parameter, called "class_data" 
above.

The observant reader will wonder why the parent's base_init is needed when we have an 
exact copy of the parent object already? We need base_init when exact copy won't do, 
when some data has to be re-created for each class. For example a class member might 
point to another object, and we want each class to point to its own object (a memory 
copy is a "shallow copy", and we may want a "deep copy"). Experienced GObject 
programmers tell me that base_init is rarely used in practice.

When the system creates a new instance object, it first allocates enough space to put 
the instance object in. It then proceeds from parent-most instance object to 
child-most instance object and runs that parent class's "instance_init" function on 
the class object in its current state. Finally the system runs the final class's 
"instance_init" on the final class object.

I will try to summarize the algorithms I have described in some pseudo-code:

- to instantiate class object
        allocate memory for final_object
        for each class object from parent to child
                copy object over final_object
                run object's own base_init on final_object
        run final_object's base_init on final_object
        run final_object's class_init on final_object with class_data

- to instantiate instance object
        allocate memory for final_object
        for each class object from parent to child
                run object's own instance_init on final_object
        run final_object's instance_init on final_object


Now that there are two initialized class and instance objects, the system sets the 
instance object's class pointer to the class object, so that the instance object can 
access its class object containing its vtable. This is how the system instantiates any 
registered type; however GObject implements its own constructor and destructor 
semantics on top of what we have described above!

Creating GObject Instances
-----

Previously we used the function g_type_create_instance to give us a working instance 
object. However GObject gives us a new API for creating gobjects, built on top of 
everything we have discussed so far. GObject implements three new methods which are 
used by this API to create and destroy new GObjects: constructor, dispose, and 
finalize.

Since C lacks many of the polymorphic features of true OO languages, specifically the 
ability to admit multiple constructors, GObject's constructor requires some digression:

How can we flexibly pass various kinds of initialization information to our object in 
a way that makes object construction easy to do? We might consider restricting 
ourselves to only using copy constructors, filling in a static "init object" with what 
ever specific fields we want, and passing the "init object" to the copy constructor to 
finish the job -- simple but not very flexible. 

However the GObject authors have devised a much more general solution that also 
provides a handy getting-and-setting mechanism called "properties", which encasulates 
raw access to an object's data fields. With this system our properties are named with 
a string, and protected with bounds and type checking. Properties can also be 
specified to be writable at contruction-time only, like const variables in C++.

Properties makes use of a polymorphic type called GValue, which allows the programmer 
to safely copy a value without knowing the specific type beforehand. GValue works by 
tracking the GType of the value it holds, and using the type system to make sure it 
always has a virtual function which can handle copying to another GValue or conversion 
to another GType. We will discuss GValues and Properties in following sections.

To create a new property for a GObject, we define it's type, name, and default value, 
then create an object that ecapsulates that information called a "property 
specification". In the GObject's class_init function we use the function 
g_object_class_install_property to attach the property specification to the GObject's 
class object.

<notice>
Any child object that adds a new property must override the set_property and 
get_property virtual functions it inherited from GObject. Exactly what these methods 
do will be covered in a later section.
</notice>

With properties we can pass our constructor an array on property specifications, and 
the initial values we want, then simply call set_property on the GObject, and get all 
the benefits of properties magically. However as we will see later, we never call the 
constructor directly.

Another feature of GObject's constructor that is not so obvious is that each 
constructor needs to accept a GType argument and pass that GType to its parent's 
constructor in case it becomes a parent to another object. This is because GObject's 
constructor must know the GType of the final child object since it is GObject's 
constructor that calls g_type_create_instance using the that child's GType.

<notice>
If we specify our own constructor, then we must override the default constructor given 
to us from our parent. The constructor must then "chain up" to the parent class by 
calling the parent's constructor *before* it has done any work. However since we are 
using properties, in practice we never need to override the default constructor.
</notice>

I must appologize of the above digressions, but it is a complexity we must come to 
grips with to understand how the whole things works. With what we have discussed 
above, we can now understand GObject's constuction function, g_object_new. This 
function takes a GType corresponding to the GType of the GObject derived object we 
wish to create, and a series of property names (which you recall are just C strings) 
and GValue pairs. 

This series is converted into a list of pairs, and the corresponding property 
specifications, that we installed in the system in our class_init function, are found. 
The constructor defined in the class object is called with the GType and construction 
properties as arguments. From child-most constructor to parent-most constructor, the 
chain is followed until GObject's constructor is run -- in effect it is the first real 
initialization code to run. GObject's constructor first calls g_type_create_instance 
using the GType we carried all the way from g_object_new, and the process we descibed 
in detail previously occurs in order to create an instance. Next it gets the final 
object's class, and calls the set_property method on all the construction properties 
passed via the constructor. That is why we always have to override the 
get_/set_preoprty methods any time we add a new property. As the chained constructors 
return, the code contained therein gets executed from parent to child. 

As parent constructors return, it becomes the child's turn to execute its own 
initialization code. In effect the order of execution of code becomes:
1. from GObject to ChildObject run instance_init
2. from GObject to ChildObject run constructor

Lastly any remaining properties that weren't passed to the constructor are set using 
the set_property methods one last time. 

The astute read may wonder then under what circumstances should they override the 
default constructor and put their own code in their own constructor? Since all our 
properties are set with the virtual method set_property, there is almost never any 
need to override GObject's default constructor.

I will attempt to summarize the GObject construction process with the following 
pseudo-code:

- to create a proper GObject based object given type a list of property+value pairs:
        lookup the specifications corresponding to pairs and place in list
        call final_object's constructor on with specification_list and type
                recursively descend to GObject's constructor
                        call g_type_create_instance with type
                        call virtual set_property with specification_list
        call virtual set_property with remaining properties

<notice>
GObject divides properties into two classes, construct and "regular" properties.
</notice>

Destroying GObject instances
-----

With that work done, we can look to what happens when we no longer need the object. 
However the destructor concept from other OO languages is in GObject decomposed into 
two methods: dispose and finalize.

The dispose method is called when the object first knows it will be destroyed. It is 
supposed to release any references to resources that may need some advance warning due 
to reference cycles, or may be scarce and thus contended for by other objects. The 
dispose method may be called any number of times, and thus the code therein should be 
safe in that case. Guarding the dispose code with a static variable is a common 
practise. The object is also expected to be usable without unrecoverable error (such 
as a SEGFAULT) after dispose has been run, so some members cannot be freed or altered 
during the dispose process. Recoverable errors, such as returning error codes or NULL 
values, are fine.

The finalize method finishes releasing the remaining resources just before the object 
itself will be freed from memory, and therefore it will only be called once. The two 
step process helps reduce cycles in reference counting schemes. 

<notice>
If we specify our own dispose and finalize, then we must override the default dispose 
and finalize given to us from our parent. Both dispose and finalize must "chain up" to 
the parent class by calling the parent's respective dispose and finalize methods 
*after* they have destroyed their own members.
</notice>

Unlike the constructor, we will frequently need to override the dispose and finalize 
methods whenever our object allocates resources which must be freed.

Knowing which method is the appropriate place to put certain destruction code is in 
general not an easy problem to solve. However, when dealing with reference counted 
libraries (such as GTK+), one should unreference all objects in dispose, and free all 
memory or close file descriptors in finalize.

We discussed g_object_new, but when do we get to destroy our objects? As we have 
alluded to above, GObjects are reference counted, which is to say that it keeps an 
integer value counting how many other objects or functions are currently "using" or 
referencing it. When you want to work with a GObject, and thus want to guarentee that 
it doesn't destroy itself while you're using it, you must call g_object_ref with the 
object as a parameter as soon as possible. This simply increments the reference count. 
Failing to do so may allow the object to be destroyed, and cause your program to crash.

Similarly, when you are finished working with the object, you must call 
g_object_unref. This will decrement the reference count, and check if it is zero. When 
the count reaches zero, the object will be disposed then eventually finalized. Failing 
to unreference a GObject is a memory leak since the count can never reach zero.

Finally, we are now ready to write some code! But don't let the above lengthy and 
difficult prose intimidate you. If you don't totally understand what is being said 
yet, don't worry -- GObject is complex! Read on for more details, try out some sample 
programs, or just get some sleep and read it over tomorrow.

What we write will look very similar to our first example, in fact I will leave some 
of the more inconsequential or redundant code out. 

Code (header)
-----

<step>
We proceed as before, but this time we create our structs with the first member being 
the parent object. In this case the parent object is GObject.
</step>

/* Our "Instance struct" defines all the data fields that make our object unique. */
typedef struct _SomeObject SomeObject;
struct _SomeObject
{
        GObject         parent_obj;

        /* Some useful fields may follow. */
};

/* Our "Class struct" defines all the method functions that our objects will share. */
typedef struct _SomeObjectClass SomeObjectClass;
struct _SomeObjectClass
{
        GTypeClass      parent_class;

        /* Some useful methods may follow. */
};

<step>
The rest of the header file proceeds familiarly.
</step>


Code (source)
-----

<notice>
We need to add declarations for the GObject specific methods we are going to override.
</notice>

/* These are the GObject Construct/Destroy methods. Their signatures are determined in 
gobject.h. */
void    some_object_constructor (GType this_type, guint n_properties, 
GObjectConstructParam *properties)
{
        /* If our class is derived from, and the child wants to chain to us, then 
this_type will
           NOT be SOME_OBJECT_TYPE, rather g_type_peek_parent will be 
SOME_OBJECT_TYPE, and we will 
           have an infinite loop. */

        GObjectClass *parent_class = g_type_class_peek (g_type_peek_parent 
(SOME_OBJECT_TYPE()));
         
        some_object_parent_class-> constructor (self_type, n_properties, properties);

        /* There is rarely need to do work here */
}

void    some_object_dispose (GObject *self)
{
        GObjectClass *parent_class = g_type_class_peek (g_type_peek_parent 
(SOME_OBJECT_TYPE()));
        static gboolean first_run = TRUE;

        if (first_run)
        {
                first_run = FALSE;
                
                /* Call g_object_unref on any GObjects that we hold, but don't break 
the object */

                parent_class-> dispose (self);
        }
}

void    some_object_finalize (GObject *self)
{
        GObjectClass *parent_class = g_type_class_peek (g_type_peek_parent 
(SOME_OBJECT_TYPE()));

        /* Free any memory or close any files */ 

        parent_class-> finalize (self);
}

<notice>
GObjectConstructParam is a struct with two members, one is an array of parameter 
specifications of type GParamSpec, and the other is an array of corresponding values 
of type GValue.
</notice>

/* These are the GObject Get and Set Property methods. Their signatures are determined 
in gobject.h. */
void    some_object_get_property (GObject *object, guint property_id, GValue *value, 
GParamSpec *pspec)
{
}

void    some_object_set_property (GObject *object, guint property_id, const GValue 
*value, GParamSpec *pspec)
{
}


/* Here is where we override any functions. Since we have no properties or even 
fields, none of the below are needed. */
void    some_object_class_init          (gpointer g_class, gpointer class_data)
{
        GObjectClass    *this_class     = G_OBJECT_CLASS (g_class);
        
        this_class-> constructor        = &some_object_constructor;
        this_class-> dispose            = &some_object_dispose;
        this_class-> finalize           = &some_object_finalize;

        this_class-> set_property       = &some_object_set_property;
        this_class-> get_property       = &some_object_get_property;
}

In order to talk about creating and destroying GObjects, by necessity we had to touch 
on properties and certain other quirks. However I left property code from the example, 
deffering that discussion to the next section. Try not to let the complexity of it all 
overwhelm you. After you've wrestled with the ideas a little more, they will start to 
make more sense. As for above, we have contrained ourselves to making a basic GObject, 
which in the next sections we will actually make function. The important part, is that 
we have learned the tools that we will need to make the rest of the document easier to 
understand.


4. Properties

We have alluded to what a wonderful thing properties are, and how they go about it, 
but to get into any more detail we need to digress yet again.

GValues
-----

C is a strongly type-checked language, meaning the compiler makes you declare the type 
of variable you want to use, and complains any time you use it in a way inconsistent 
with that type. This is a good thing since it makes the resulting code fast, and helps 
us catch type mistakes that could cause crashes or insecure behaviour. This is also a 
bad thing since we programmers live and think in a world that is hardly as strict, and 
we wish to have our type behave polymorphically -- that is change their behaviour in 
different contexts. And as we have seen above in our discussions of inheritance, to 
get a little polymorphism we can to resort to C casts. However when declaring 
functions and passing simple values to them, using raw pointers can become 
troublesome. Lucky, the type system gives us another tool that C doesn't have: the 
GType. 

Lets frame the problem more clearly. I want a data type that implements a list of 
various types of values, and I want to be able to write an interface to that list that 
doesn't depend on the specific type of the value given and doesn't require me specify 
redundant functions for each value type. Such an interface must have some type, so 
we'll call it GValue (for Generic Value). How can we implement such a beast?

We create a structure that encapsulates our value, and has two fields: a union of all 
representable fundamental types (ie: char, int, double, pointer, ...), and the GType 
of the value stored in that union. In this way we can hide the value's type within 
GValue, yet still guarentee type safety by checking any GValue operations against the 
GType. This also downloads the specification of redundant type based operations (such 
as get_int, set_float, ...) into g_value_* which keeps your API free from such 
trouble. 

The wary reader will notice that each GValue takes up at least as much memory as the 
largest fundamental type (usually 8 bytes), plus the size of GType. GValues are not 
space efficient, have a non-trivial overhead, and therefore should not be used in mass 
quantities. The most common usage is for specifying generic APIs. 

Exactly how GValue works outside of this is a little beyond the scope of this 
discussion, but it is helpful to understanding properties.

<example>
/* Lets copy an integer around using GValue! */
#define g_value_new(type) g_value_init (g_new (GValue, 1), type)

GValue *a = g_value_new (G_TYPE_UCHAR);
GValue *b = g_value_new (G_TYPE_INT);
int c = 0;

g_value_set_uchar (a, 'a');
g_value_copy (a, b);

c = g_value_get (b);
g_print ("w00t: %d\n", c);

g_free (a);
g_free (b);
</example>

Design
-----

We have already touched on properties above, so we already have a justification for 
their existance, but lets continue to motivate the rest of their design for 
tradition's sake!

To continue building a general property setting mechanism, we need a way to 
parameterize, and then later find the property name independently of what we might 
have named the variable in our instance struct. Externally we wish to use C strings to 
specify the property from the public API, but internally that is far slower than 
necessary. Therefore we enumerate the properties, and use an index into that 
enumeration to identify the property within our own code.

We have already mentioned the property specification, called GParamSpec in Glib, which 
holds our object's GType, our object's property name, our property enumerated ID, 
default value, boundary values, and more. Thus the GParamSpec is what allows the type 
system to map string names to enumerated property IDs, and is the glue that holds 
everything together.

When we wish to set or get a property, we call g_object_set/get_property on our object 
with the property name, and a GValue that holds the value we wish to set. The 
g_object_set_property function then looks up the GParamSpec associated with the 
property name, looks up our object's class, and calls the object's set_property method 
on the object. This implies that if we introduce any properties, we must override the 
set/get_property method. Also any properties introduced by the parent class are 
handled properly by the parent's set/get_property methods, since that is the class 
where the GParamSpec was installed from. Finally it imples that we must install a 
GParamSpec from our object's class_init! 

Lets add the code to handle properties to our SomeObject! We will assume we already 
have a working skeleton as presented in the previous section.

Code (header)
-----

<step>
AS above, except we added two properties.
</step>

/* Our "Instance struct" defines all the data fields that make our object unique. */
typedef struct _SomeObject SomeObject;
struct _SomeObject
{
        GObject         parent_obj;

        /* Our properties */
        int             a;
        float           b;

        /* Some useful fields may follow. */
};


Code (source)
-----

<step>
Create an enumeration for internally tracking properties.
</step>

enum
{
        OBJECT_PROPERTY_A = 1 << 1;
        OBJECT_PROPERTY_B = 1 << 2;
};

<step>
Implement the handlers for the properties we introduced.
</step>

/* These are the GObject Get and Set Property methods. Their signatures are determined 
in gobject.h. */
void    some_object_get_property (GObject *object, guint property_id, GValue *value, 
GParamSpec *pspec)
{
        SomeObject *self = SOME_OBJECT (object);

        switch (property_id)
        {
                case OBJECT_PROPERTY_A:
                        g_value_set_int (value, self-> a);
                        break;

                case OBJECT_PROPERTY_B:
                        g_value_set_float (value, self-> b);
                        break;

                default: /* No property with that ID!! */
        }
}

void    some_object_set_property (GObject *object, guint property_id, const GValue 
*value, GParamSpec *pspec)
{
        SomeObject *self = SOME_OBJECT (object);

        switch (property_id)
        {
                case OBJECT_PROPERTY_A:
                        self-> a = g_value_get_int (value);
                        break;

                case OBJECT_PROPERTY_B:
                        self-> b = g_value_get_float (value);
                        break;

                default: /* No property with that ID!! */
        }
}

<step>
Override the set/get_property methods inherited from our parent and install 
GParamSpecs.
</step>

/* Here is where we override any functions. */
void    some_object_class_init          (gpointer g_class, gpointer class_data)
{
        GObjectClass    *this_class     = G_OBJECT_CLASS (g_class);
        GParamSpec      *spec;
        
        this_class-> constructor        = &some_object_constructor;
        this_class-> dispose            = &some_object_dispose;
        this_class-> finalize           = &some_object_finalize;

        this_class-> set_property       = &some_object_set_property;
        this_class-> get_property       = &some_object_get_property;

        spec = g_param_spec_int
        (
                "property-a",                           /* property name */
                "a",                                    /* nickname */
                "Mysterty value 1",                     /* description */
                5,                                      /* minimum */
                10,                                     /* maximum */
                5,                                      /* default */
                G_PARAM_READABLE |G_PARAM_WRITABLE      /* GParamSpecFlags */
        );
        g_object_class_install_property (this_class, OBJECT_PROPERTY_A, spec);

        spec = g_param_spec_float
        (
                "property-b",                           /* property name */
                "b",                                    /* nickname */
                "Mysterty value 2                       /* description */
                0.0,                                    /* minimum */
                1.0,                                    /* maximum */
                0.5,                                    /* default */
                G_PARAM_READABLE |G_PARAM_WRITABLE      /* GParamSpecFlags */
        );
        g_object_class_install_property (this_class, OBJECT_PROPERTY_B, spec);
}

5. Signals
-----
_______________________________________________
gtk-list mailing list
[EMAIL PROTECTED]
http://mail.gnome.org/mailman/listinfo/gtk-list

Reply via email to