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.