GObject 03: An Instantiatable Class

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\n", a_class_member_of_myclass);
}

void my_instance_method(myinstance_t *instance, int a_parameter) {
    printf("The member is %d\n",instance->an_instance_member);
    printf("The parameter is %d\n",a_parameter);
}

void my_class_init_func(myclass_t* klass, gpointer data) {
    printf("my_class_init_func called!\n");
    a_class_member_of_myclass = 42;
}

void my_instance_init_func(myinstance_t *instance, gpointer data) {
    printf("my_instance_init_func called!\n");
    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\n",my_type_id);
    printf("%s\n",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\n",
            G_TYPE_CHECK_INSTANCE_TYPE(instance, my_type_id)?"yes":"no");
    printf("Is instance of class? %s\n",
            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\n", a_class_member_of_myclass);
}

void my_instance_method(myinstance_t *instance, int a_parameter) {
    printf("The member is %d\n",instance->an_instance_member);
    printf("The parameter is %d\n",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!\n");
    a_class_member_of_myclass = 42;
}

void my_instance_init_func(myinstance_t *instance, gpointer data) {
    printf("my_instance_init_func called!\n");
    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\n",my_type_id);
    printf("%s\n",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\n",
            G_TYPE_CHECK_INSTANCE_TYPE(instance, my_type_id)?"yes":"no");
    printf("Is instance of class? %s\n",
            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<typeinfo> in C++ in order to use RTTI.

你可能感兴趣的:(C++,c,C#)