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:
Here’s some trivial note on using GObject library.
1. Private members
Recall our definition of Base type:
NOTE: PLEASE READ ALL COMMENT CAREFULLY.
C
1
2
3
4
5
6
7
8
9
10
11
12
13
/* Base object struct */
typedefstruct_Base{
GTypeInstance parent;
/* instance variable, should be hidden */
gint base_instance_i;
}Base;
/* Base class struct */
typedefstruct_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:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/* private data of Base object */
typedefstruct_FakeBasePrivate FakeBasePrivate;
/* Base object struct */
typedefstruct_FakeBase{
/* GObject as the first field */
GObject parent;
/* private data */
FakeBasePrivate*priv;
}FakeBase;
/* Base class struct */
typedefstruct_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:
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.
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:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// derived.h
#ifndef DERIVED_H_
#define DERIVED_H_
#include "base.h"
#include <glib-object.h>
/* Derived object struct */
typedefstruct_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 */
typedefstruct_DerivedClass{
/* The TypeInstance structure is still the first member of the instance structure */
BaseClass parent;
}DerivedClass;
/* non-virtual public method for Derived object */
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:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
intmain(){
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
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.
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:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
intmain(){
g_type_init();
my_dump_type(foo_get_type());
/* Use g_type_create_instance if implement a fundamental class */
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.
C
1
2
3
4
5
6
7
8
9
10
11
12
typedefstruct_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.
*/
inti;
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:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
GType foo_get_type(){
staticGType foo_type=0;
if(foo_type==0){
staticconstGTypeInfo 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 */
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:
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.