GObject 03: An Instantiatable Class

9835 ワード

Most of the time classes are instantiatable.  It can create "instances", which have their own data and share common methods of the class.
So a CLASS is shared by many INSTANCEs.
In GObject, we need two structs.  One for the class and one for the instance.
/* A fundamental type.  Instantiatable. */

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

typedef struct {
    GTypeClass something_as_boilerplate;
} myclass_t;

int a_class_member_of_myclass;

typedef struct {
    GTypeInstance something_as_boilerplate; int an_instance_member;
} myinstance_t;

void my_class_method() {
    printf("The class member is %d
", a_class_member_of_myclass); } void my_instance_method(myinstance_t *instance, int a_parameter) { printf("The member is %d
",instance->an_instance_member); printf("The parameter is %d
",a_parameter); } void my_class_init_func(myclass_t* klass, gpointer data) { printf("my_class_init_func called!
"); a_class_member_of_myclass = 42; } void my_instance_init_func(myinstance_t *instance, gpointer data) { printf("my_instance_init_func called!
"); instance->an_instance_member = 65; } int main() { g_type_init(); GTypeInfo my_type_info = { sizeof(myclass_t), //guint16 class_size; NULL, //GBaseInitFunc base_init; NULL, //GBaseFinalizeFunc base_finalize; /* classed types, instantiated types */ (GClassInitFunc)my_class_init_func, //GClassInitFunc class_init; NULL, //GClassFinalizeFunc class_finalize; NULL, //gconstpointer class_data; /* instantiated types */ sizeof(myinstance_t),//guint16 instance_size; 0, //guint16 n_preallocs; (GInstanceInitFunc)my_instance_init_func, //GInstanceInitFunc instance_init; /* value handling */ NULL, //const GTypeValueTable *value_table; }; GTypeFundamentalInfo my_fundamental_info = { G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_INSTANTIATABLE }; GType my_type_id = g_type_register_fundamental( g_type_fundamental_next(), "MyInstantiatableFundamentalType", &my_type_info, &my_fundamental_info, 0 ); printf("%d
",my_type_id); printf("%s
",g_type_name(my_type_id)); myinstance_t *instance = (myinstance_t*)g_type_create_instance(my_type_id); my_instance_method(instance,78); my_class_method(); printf("Is instance of class? %s
", G_TYPE_CHECK_INSTANCE_TYPE(instance, my_type_id)?"yes":"no"); printf("Is instance of class? %s
", G_TYPE_CHECK_INSTANCE_TYPE(instance, G_TYPE_INT)?"yes":"no"); return 0; }

And focus in here:
typedef struct {
    GTypeClass something_as_boilerplate;
} myclass_t;

int a_class_member_of_myclass;

typedef struct {
    GTypeInstance something_as_boilerplate;
    int an_instance_member;
} myinstance_t;

Q: What are the something_as_boilerplate things?
A: As every other classes in GObject, all class structs start with a GTypeClass field and all instance structs start with a GTypeInstance field.
Q: What do they contain?
A: GTypeClass contains a GType which shows its type.  GTypeInstance contains a pointer to its class struct so every instance knows its class.  See the source code to make sure.
Q: Where are their members/fields?
A: Non-static members (such as an_instance_member) are in the instance struct, after the GTypeInstace.  Static members (such as a_class_member_of_myclass) are usually global and are usually not in the class struct(see the previous article).
Q: How do you figure out if it is a member or a global variable?
A: You say "let it be a member"and it is a member.  The concepts "object-oriented programming", "class", "instance"and "member"are all man-made so it is a member if you treat it like a member.
And the functions:
void my_class_method() {
    printf("The class member is %d
", a_class_member_of_myclass); } void my_instance_method(myinstance_t *instance, int a_parameter) { printf("The member is %d
",instance->an_instance_member); printf("The parameter is %d
",a_parameter); }

Q: What are methods like?
A: Methods are simply functions.  Non-static methods always take a pointer to an instance struct as their first parameter.  Static methods are ordinary functions.
Q: How do you figure out if it is a member or a global variable?
A: You say "let it be a method"and it is a method.  The concept "method"is man-made so it is a method if you treat it like a method.
Now we ask GLib to recognize my class as a type.
Code from the main() function:
    GTypeInfo my_type_info = {
        sizeof(myclass_t),  //guint16                class_size;

        NULL,               //GBaseInitFunc          base_init;
        NULL,               //GBaseFinalizeFunc      base_finalize;

        /* classed types, instantiated types */
        (GClassInitFunc)my_class_init_func, //GClassInitFunc         class_init;
        NULL,               //GClassFinalizeFunc     class_finalize;
        NULL,               //gconstpointer          class_data;

        /* instantiated types */
        sizeof(myinstance_t),//guint16               instance_size;
        0,                  //guint16                n_preallocs;
        (GInstanceInitFunc)my_instance_init_func, //GInstanceInitFunc      instance_init;

        /* value handling */
        NULL,               //const GTypeValueTable *value_table;
    };

    GTypeFundamentalInfo my_fundamental_info = {
        G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_INSTANTIATABLE
    };

    GType my_type_id = g_type_register_fundamental(
            g_type_fundamental_next(),
            "MyInstantiatableFundamentalType",
            &my_type_info,
            &my_fundamental_info,
            0
            );

Again we use the g_type_register_fundamental() function to make the library think myclass is a class whose instance is myinstance.
We provide one GTypeInfo struct.  class_size and instance_size are filled so that the library can automatically malloc a myclass_t struct and some myinstance_t structs.  class_init and instance_init are also provided so that the library can help me initialize the class/instance struct when they are created.
In GTypeFundamentalInfo, mark my class as CLASSED and INSTANTIATABLE.
Register it with g_type_register_fundamental and then it becomes a type.
And the init functions:
void my_class_init_func(myclass_t* klass, gpointer data) {
    printf("my_class_init_func called!
"); a_class_member_of_myclass = 42; } void my_instance_init_func(myinstance_t *instance, gpointer data) { printf("my_instance_init_func called!
"); instance->an_instance_member = 65; }

They initialize the class and the instance, respectively on creating.  You can omit them if they are not necessary.
Now the main() function:
    printf("%d
",my_type_id); printf("%s
",g_type_name(my_type_id)); myinstance_t *instance = (myinstance_t*)g_type_create_instance(my_type_id); my_instance_method(instance,78); my_class_method(); printf("Is instance of class? %s
", G_TYPE_CHECK_INSTANCE_TYPE(instance, my_type_id)?"yes":"no"); printf("Is instance of class? %s
", G_TYPE_CHECK_INSTANCE_TYPE(instance, G_TYPE_INT)?"yes":"no");

g_type_name querys the string representation of the name of the type.
When first creating an instance using g_type_create_instance, the class struct is automatically allocated and initialized.
In order to call a instance method, just call the function with the first argument filled as the instance pointer.
G_TYPE_CHECK_INSTANCE_TYPE checks the type of an instance.
Q: How can it know the type of the instance by merely a pointer to the instance?
A: Recall that the first field of myinstance_t (that is GTypeInstance) contains a pointer which points to its class struct (myclass_t) which begins with a GTypeClass which has a GType field which identifies its type.
This means GObject provides runtime type information (RTTI) support which C++ classes do not usually have.  Note that you need to #include in C++ in order to use RTTI.