Recall there are 3 types in GObject type system: Fundamental, static and dynamic. A fundamental type is a top-most type which has no parent type. Most of them are pre-defined. Static types never load/unload its class type (say, their class struct) at runtime, since they are static. On the contrary, dynamic types can be dynamically loaded/unloaded at runtime. They are normally used within a module.
We can call g_type_register_dynamic() to register a dynamic type. When used in a module of GObject library (may be a GTypeModule type), We can also call g_type_module_register_type() to create your dynamic types. g_type_register_dynamic() is invoked for you in that function. Let’s go through the code:
The implementation structure may be a little different with the stuff when creating a static type. An additional parameter GTypeModule is passed in. It represents the module your dynamic type belongs to. So, when the module is unloaded, all dynamic types in it are unaccessible.
Also note the bar_type_class_finalize() function. We use it to override the finalize() virtual function in GObjectClass. Now you can do un-initialiation in this function. It is like the destructor in a C++ class.
Let’s move on to the module type. This type inherits GTypeModule:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// fakemodule.h
#ifndef FAKE_MODULE_H_
#define FAKE_MODULE_H_
#include <glib-object.h>
/* module object struct */
typedefstruct_FakeModule{
GTypeModule parent;
}FakeModule;
/* module class struct */
typedefstruct_FakeModuleClass{
GTypeModuleClass parent;
}FakeModuleClass;
/* type method */
GType fake_module_get_type();
#endif /* FAKE_MODULE_H_ */
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// fakemodule.c
#include "fakemodule.h"
/*
* If you implement a real shared library module, you
* can init module variables, assign virtual function here.
*/
gboolean fake_module_load(GTypeModule*module){
g_print("Invoking fake_module_load()\n");
/* successfully loaded */
returnTRUE;
}
/*
* If you implement a real shared library module, you
* can uninit module variables, and make all cleanups here.
Then we register the interface using g_type_register_static() with G_TYPE_INTERFACE as first parameter. For interfaces, we only need to assign base_init() and base_finalize()callbacks.
As described in the official document, we should allocate dynamic memebers of class struct in base_init(). Otherwise, all copies of the class struct share only one copy of dynamic members. This leads to problems.
Let’s define the type which implements the interface:
Note the naming convention I used here. Our FakeDesktop class will implement the FakeIServer interface and another FakeIClient interface. This time do not use corresponding interface struct as the first members of FakeDesktop and FakeDesktopClass. Interface info will be added dynamically when initialize a real instance of FakeDesktop. Let’s move to the *.c code:
Note the g_type_add_interface_static() function call to add interface info. The interface info is defined in a GInterfaceInfo struct. We just make use of the interface_init() callback. In it, we assign function pointers of corresponding interface to our implementation function. We can add multiple interface infos to implement them.
Signals in GObject are used to support a event-driven programming. Signals can be connected to callback handlers. When they are emitted, these handlers are invoked. To add signals to a type, notice following lines of code:
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.