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.
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 */