Gobject Tutorial
Gobject Tutorial
GObject
Class and instance
Reference count
Initialization and destruction process
Type system and registration process
Name convention
Define TDoubleClass and TDouble
Creation process of a child of GObject
Registration
G_DEFINE_TYPE macro
G_DECLARE_FINAL_TYPE macro
Separate the file into main.c, tdouble.h and tdouble.c
Functions
Exercise
Signals
Signals
Signal registration
Signal handler
Signal connection
Signal emission
Example
Default signal handler
Signal flag
Properties
GParamSpec
Overriding set_property and get_property class methods
GValue
t_double_set_property and t_double_get_property
1
Notify signal
Define more than one property
Exercise
Derivable type and abstract type
Derivable type
Abstract type
TNumber class
TInt object.
TDouble object.
main.c
Compilation and execution
Class initialization process
Initialization process of TNumberClass
Initialization process of TIntClass
Derivable and non-abstract type
String and memory management
String and memory
Copying string
const qualifier
Header file
C file
Chaining up to its parent
How to write a derivable type
Child class extends parent’s function
Verification of a numeric string
Header file
C file
Child class extends parent’s function.
Compilation and execution
Interface
TComparable interface
Implementing interface
2
Test program.
Compilation and execution
Build an interface without macros
GObject system and object oriented languages
GObject
Class and instance
GObject instance is created with g_object_new function. GObject has not only instances
but also classes.
● A class of GObject is created at the first call of g_object_new. And there exists only
one GObject class.
● GObject instance is created whenever g_object_new is called. So, two or more
GObject instances can exist.
In a broad sense, GObject means the object which includes its class and instances. In a
narrow sense, GObject is a definition of a C structure.
C
1 typedef struct _GObject GObject;
2 struct _GObject
3 {
4 GTypeInstance g_type_instance;
5
6 /*< private >*/
7 guint ref_count; /* (atomic) */
8 GData *qdata;
9 };
The g_object_new function allocates GObject-sized memory, initializes the memory and
returns the pointer to the memory. The memory is a GObject instance.
In the same way, the class of GObject is memory allocated by g_object_new and its
structure is defined with GObjectClass. The following is extracted from gobject.h. But you
don’t need to know the details of the structure now.
3
4
C
1 struct _GObjectClass
2 {
3 GTypeClass g_type_class;
4
5 /*< private >*/
6 GSList *construct_properties;
7
8 /*< public >*/
9 /* seldom overridden */
10 GObject* (*constructor) (GType type,
11 guint n_construct_proper
ties,
12 GObjectConstructParam *construct_properti
es);
13 /* overridable methods */
14 void (*set_property) (GObject *object,
15 guint property_id,
16 const GValue *value,
17 GParamSpec *pspec);
18 void (*get_property) (GObject *object,
19 guint property_id,
20 GValue *value,
21 GParamSpec *pspec);
22 void (*dispose) (GObject *object);
23 void (*finalize) (GObject *object);
24 /* seldom overridden */
25 void (*dispatch_properties_changed) (GObject *object,
26 guint n_pspecs,
27 GParamSpec **pspecs);
28 /* signals */
29 void (*notify) (GObject *object,
30 GParamSpec *pspec);
31
32 /* called when done constructing */
33 void (*constructed) (GObject *object);
34
35 /*< private >*/
36 gsize flags;
37
38 gsize n_construct_properties;
39
40 gpointer pspecs;
41 gsize n_pspecs;
42
43 /* padding */
5
44 gpointer pdummy[3];
45
};
The programs for GObject are included in GLib source files. You can download the GLib
source files from GNOME download page.
There are sample programs in src/misc directory in the GObject tutorial repository. You
can compile them by:
C
1 $ cd src/misc
2 $ meson setup _build
3 $ ninja -C _build
C
1 #include <glib-object.h>
2
3 int
4 main (int argc, char **argv) {
5 GObject *instance1, *instance2;
6 GObjectClass *class1, *class2;
7
8 instance1 = g_object_new (G_TYPE_OBJECT, NULL);
9 instance2 = g_object_new (G_TYPE_OBJECT, NULL);
10 g_print ("The address of instance1 is %p\n", instance1);
11 g_print ("The address of instance2 is %p\n", instance2);
12
13 class1 = G_OBJECT_GET_CLASS (instance1);
14 class2 = G_OBJECT_GET_CLASS (instance2);
15 g_print ("The address of the class of instance1 is %p\n", class1);
16 g_print ("The address of the class of instance2 is %p\n", class2);
17
18 g_object_unref (instance1);
19 g_object_unref (instance2);
20
21 return 0;
22 }
● 5-6: instance1 and instance2 are pointers that points GObject instances. class1 and
class2 points a class of the instances.
6
● 8-11: A function g_object_new creates a GObject instance. GObject instance is a
chunk of memory which has GObject structure (struct _GObject). The argument
G_TYPE_OBJECT is the type of GObject. This type is different from C language type
like char or int. There is Type System which is a base system of GObject system. Every
data type such as GObject must be registered to the type system. The type system has
series of functions for the registration. If one of the functions is called, then the type
system determines GType type value for the object and returns it to the caller. GType
is an unsigned long integer on my computer but it depends on the hardware.
g_object_new allocates GObject-sized memory and returns the pointer to the top
address of the memory. After the creation, this program displays the addresses of
instances.
● 13-16: A macro G_OBJECT_GET_CLASS returns the pointer to the class of the
argument. Therefore, class1 points the class of instance1 and class2 points the class of
instance2 respectively. The addresses of the two classes are displayed.
● 18-19: g_object_unref will be explained in the next subsection. It destroys the instances
and the memory is freed.
Now, execute it.
C
1 $ cd src/misc; _build/example1
2 The address of instance1 is 0x55895eaf7ad0
3 The address of instance2 is 0x55895eaf7af0
4 The address of the class of instance1 is 0x55895eaf7880
5 The address of the class of instance2 is 0x55895eaf7880
The locations of two instances instance1 and instance2 are different. Each instance has its
own memory. The locations of two classes class1 and class2 are the same. Two GObject
instances share the same class.
7
Class and Instance
Reference count
GObject instance has its own memory. They are allocated by the system when it is created.
If it becomes useless, the memory must be freed. However, how can we determine whether
it is useless? GObject system provides reference count to solve the problem.
An instance is created and used by other instance or the main program. That is to say, the
instance is referred. If the instance is referred by A and B, then the number of the
reference is two. This number is called reference count. Let’s think about a scenario like
this:
● A calls g_object_new and owns an instance G. A refers G, so the reference count of G
is 1.
● B wants to use G too. B calls g_object_ref and increases the reference count by 1.
Now the reference count is 2.
8
● A no longer uses G. A calls g_object_unref and decreases the reference count by 1.
Now the reference count is 1.
● B no longer uses G. B calls g_object_unref and decreases the reference count by 1.
Now the reference count is 0.
● Because the reference count is zero, G knows that no one refers to it. G begins
finalizing process by itself. G disappears and the memory is freed.
A program example2.c is based on the scenario above.
9
C
1 #include <glib-object.h>
2
3 static void
4 show_ref_count (GObject *instance) {
5 if (G_IS_OBJECT (instance))
6 /* Users should not use ref_count member in their program. */
7 /* This is only for demonstration. */
8 g_print ("Reference count is %d.\n", instance->ref_count);
9 else
10 g_print ("Instance is not GObject.\n");
11 }
12
13 int
14 main (int argc, char **argv) {
15 GObject *instance;
16
17 instance = g_object_new (G_TYPE_OBJECT, NULL);
18 g_print ("Call g_object_new.\n");
19 show_ref_count (instance);
20 g_object_ref (instance);
21 g_print ("Call g_object_ref.\n");
22 show_ref_count (instance);
23 g_object_unref (instance);
24 g_print ("Call g_object_unref.\n");
25 show_ref_count (instance);
26 g_object_unref (instance);
27 g_print ("Call g_object_unref.\n");
28 g_print ("Now the reference count is zero and the instance is destroye
d.\n");
29 g_print ("The instance memories are possibly returned to the syste
m.\n");
30 g_print ("Therefore, the access to the same address may cause a segmen
tation error.\n");
31
32 return 0;
33 }
10
C
1 $ cd src/misc; _build/example2
2 bash: cd: src/misc: No such file or directory
3 Call g_object_new.
4 Reference count is 1.
5 Call g_object_ref.
6 Reference count is 2.
7 Call g_object_unref.
8 Reference count is 1.
9 Call g_object_unref.
10 Now the reference count is zero and the instance is destroyed.
11 The instance memories are possibly returned to the system.
12 Therefore, the access to the same address may cause a segmentation err
or.
example2 shows:
● g_object_new creates a new GObject instance and sets its reference count to 1.
● g_object_ref increases the reference count by 1.
● g_object_unref decreases the reference count by 1. If the reference count drops to
zero, the instance destroys itself.
This initialization process is carried out when g_object_new function is called for the first
time. At the second and subsequent call for g_object_new, it performs only two processes:
(1) memory allocation for GObject structure (2) initialization for the memory. g_object_new
11
returns the pointer that points the instance (the memory allocated for the GObject
structure).
Destruction
1. Destroys GObject instance. The memory for the instance is freed.
GObject type is a static type. Static type never destroys its class. So, even if the destroyed
instance is the last instance, the class still remains.
When you write code to define a child object of GObject, It is important to understand the
process above. The detailed process will be explained in the later sections.
Name convention
An example of this section is an object represents a real number. It is not so useful
because we have already had double type in C language to represent real numbers.
However, I think this example is not so bad to know the technique how to define a child
object.
First, you need to know the naming convention. An object name consists of name space
and name. For example, “GObject” consists of a name space “G” and a name “Object”.
“GtkWidget” consists of a name space “Gtk” and a name “Widget”. Let the name space
be “T” and the name be “Double” of the new object. In this tutorial, we use “T” as a name
space for all the objects we make.
TDouble is the object name. It is a child object of GObject. It represents a real number and
the type of the number is double. It has some useful functions.
C
1 typedef struct _TDoubleClass TDoubleClass;
2 struct _TDoubleClass {
3 GObjectClass parent_class;
4 };
C
1 typedef struct _TDouble TDouble;
2 struct _TDouble {
3 GObject parent;
4 double value;
5 };
TDouble has its own member, “value”. It is the value of TDouble instance.
The coding convention above needs to be kept all the time.
13
Creation process of a child of GObject
The creation process of TDouble type is similar to the one of GObject.
1. Registers TDouble type to the type system.
2. The type system allocates memory for TDoubleClass and TDouble.
3. Initializes TDoubleClass.
4. Initializes TDouble.
Registration
Usually registration is done by convenient macro such as G_DECLARE_FINAL_TYPE and
G_DEFINE_TYPE. You can use G_DEFINE_FINAL_TYPE for a final type class instead of
G_DEFINE_TYPE since GLib version 2.70. So you don’t need to care about registration
details. But, in this tutorial, it is important to understand GObject type system, so I want to
show you the registration without macro, first.
There are two kinds of types, static and dynamic. Static type doesn’t destroy its class even
after all the instances have been destroyed. Dynamic type destroys its class when the last
instance has been destroyed. The type of GObject is static and its descendant objects’
type is also static. The function g_type_register_static registers a type of a static object.
The following code is extracted from gtype.h in the Glib source files.
C
1 GType
2 g_type_register_static (GType parent_type,
3 const gchar *type_name,
4 const GTypeInfo *info,
5 GTypeFlags flags);
14
Otherwise, set it to zero.
Because the type system maintains the parent-child relationship of the type,
g_type_register_static has a parent type parameter. And the type system also keeps the
information of the type. After the registration, g_type_register_static returns the type of
the new object.
GTypeInfo structure is defined as follows.
C
1 typedef struct _GTypeInfo GTypeInfo;
2
3 struct _GTypeInfo
4 {
5 /* interface types, classed types, instantiated types */
6 guint16 class_size;
7
8 GBaseInitFunc base_init;
9 GBaseFinalizeFunc base_finalize;
10
11 /* interface types, classed types, instantiated types */
12 GClassInitFunc class_init;
13 GClassFinalizeFunc class_finalize;
14 gconstpointer class_data;
15
16 /* instantiated types */
17 guint16 instance_size;
18 guint16 n_preallocs;
19 GInstanceInitFunc instance_init;
20
21 /* value handling */
22 const GTypeValueTable *value_table;
23 };
16
C
1 #include <glib-object.h>
2
3 #define T_TYPE_DOUBLE (t_double_get_type ())
4
5 typedef struct _TDouble TDouble;
6 struct _TDouble {
7 GObject parent;
8 double value;
9 };
10
11 typedef struct _TDoubleClass TDoubleClass;
12 struct _TDoubleClass {
13 GObjectClass parent_class;
14 };
15
16 static void
17 t_double_class_init (TDoubleClass *class) {
18 }
19
20 static void
21 t_double_init (TDouble *self) {
22 }
23
24 GType
25 t_double_get_type (void) {
26 static GType type = 0;
27 GTypeInfo info;
28
29 if (type == 0) {
30 info.class_size = sizeof (TDoubleClass);
31 info.base_init = NULL;
32 info.base_finalize = NULL;
33 info.class_init = (GClassInitFunc) t_double_class_init;
34 info.class_finalize = NULL;
35 info.class_data = NULL;
36 info.instance_size = sizeof (TDouble);
37 info.n_preallocs = 0;
38 info.instance_init = (GInstanceInitFunc) t_double_init;
39 info.value_table = NULL;
40 type = g_type_register_static (G_TYPE_OBJECT, "TDouble", &info, 0
);
41 }
42 return type;
43 }
44
17
45 int
46
main (int argc, char **argv) {
47
GType dtype;
48
TDouble *d;
49
50
dtype = t_double_get_type (); /* or dtype = T_TYPE_DOUBLE */
51
if (dtype)
52
g_print ("Registration was a success. The type is %lx.\n", dtype);
53
else
54
g_print ("Registration failed.\n");
55
56
d = g_object_new (T_TYPE_DOUBLE, NULL);
57
if (d)
58
g_print ("Instantiation was a success. The instance address is %
p.\n", d);
59
else
60
g_print ("Instantiation failed.\n");
61
g_object_unref (d); /* Releases the object d. */
62
63
return 0;
64
}
G_DEFINE_TYPE macro
The registration above is always done with the same algorithm. Therefore, it can be
defined as a macro such as G_DEFINE_TYPE.
G_DEFINE_TYPE does the following:
● Declares a class initialization function. Its name is <name space>_<name>_class_init.
For example, if the object name is TDouble, it is t_double_class_init. This is a
declaration, not a definition. You need to define it.
● Declares a instance initialization function. Its name is <name space>_<name>_init. For
example, if the object name is TDouble, it is t_double_init. This is a declaration, not a
definition. You need to define it.
● Defines a static variable pointing to the parent class. Its name is <name
space>_<name>_parent_class. For example, if the object name is TDouble, it is
t_double_parent_class.
● Defines a <name space>_<name>_get_type () function. For example, if the object
name is TDouble, it is t_double_get_type. The registration is done in this function like
the previous subsection.
Using this macro reduces lines of the program. See the following sample example4.c which
works the same as example3.c.
19
C
1 #include <glib-object.h>
2
3 #define T_TYPE_DOUBLE (t_double_get_type ())
4
5 typedef struct _TDouble TDouble;
6 struct _TDouble {
7 GObject parent;
8 double value;
9 };
10
11 typedef struct _TDoubleClass TDoubleClass;
12 struct _TDoubleClass {
13 GObjectClass parent_class;
14 };
15
16 G_DEFINE_TYPE (TDouble, t_double, G_TYPE_OBJECT)
17
18 static void
19 t_double_class_init (TDoubleClass *class) {
20 }
21
22 static void
23 t_double_init (TDouble *self) {
24 }
25
26 int
27 main (int argc, char **argv) {
28 GType dtype;
29 TDouble *d;
30
31 dtype = t_double_get_type (); /* or dtype = T_TYPE_DOUBLE */
32 if (dtype)
33 g_print ("Registration was a success. The type is %lx.\n", dtype);
34 else
35 g_print ("Registration failed.\n");
36
37 d = g_object_new (T_TYPE_DOUBLE, NULL);
38 if (d)
39 g_print ("Instantiation was a success. The instance address is %
p.\n", d);
40 else
41 g_print ("Instantiation failed.\n");
42 g_object_unref (d);
43
44 return 0;
20
45 }
Thanks to G_DEFINE_TYPE, we are freed from writing bothersome code like GTypeInfo and
g_type_register_static. One important thing to be careful is to follow the convention of the
naming of init functions.
Execute it.
Plain Text
1 $ cd src/misc; _build/example4
2 Registration was a success. The type is 564b4ff708a0.
3 Instantiation was a success. The instance address is 0x564b4ff71400.
You can use G_DEFINE_FINAL_TYPE instead of G_DEFINE_TYPE for final type classes
since GLib version 2.70.
G_DECLARE_FINAL_TYPE macro
Another useful macro is G_DECLARE_FINAL_TYPE macro. This macro can be used for a
final type. A final type doesn’t have any children. If a type has children, it is a derivable
type. If you want to define a derivable type object, use G_DECLARE_DERIVABLE_TYPE
instead. However, you probably want to write final type objects in most cases.
G_DECLARE_FINAL_TYPE does the following:
● Declares <name space>_<name>_get_type () function. This is only declaration. You
need to define it. But you can use G_DEFINE_TYPE, its expansion includes the
definition of the function. So, you actually don’t need to write the definition by yourself.
● The C type of the object is defined as a typedef of structure. For example, if the
object name is TDouble, then typedef struct _TDouble TDouble is included in the
expansion. But you need to define the structure struct _TDouble by yourself before
G_DEFINE_TYPE.
● <NAME SPACE>_<NAME> macro is defined. For example, if the object is TDouble the
macro is T_DOUBLE. It will be expanded to a function which casts the argument to the
pointer to the object. For example, T_DOUBLE (obj) casts the type of obj to TDouble *.
● <NAME SPACE>_IS_<NAME> macro is defined. For example, if the object is TDouble
the macro is T_IS_DOUBLE. It will be expanded to a function which checks if the
21
argument points the instance of TDouble. It returns true if the argument points a
descendant of TDouble.
● The class structure is defined. A final type object doesn’t need to have its own
member of class structure. The definition is like the line 11 to 14 in the example4.c.
You need to write the macro definition of the type of the object before
G_DECLARE_FINAL_TYPE. For example, if the object is TDouble, then
C
1 #define T_TYPE_DOUBLE (t_double_get_type ())
22
C
1 #include <glib-object.h>
2
3 #define T_TYPE_DOUBLE (t_double_get_type ())
4 G_DECLARE_FINAL_TYPE (TDouble, t_double, T, DOUBLE, GObject)
5
6 struct _TDouble {
7 GObject parent;
8 double value;
9 };
10
11 G_DEFINE_TYPE (TDouble, t_double, G_TYPE_OBJECT)
12
13 static void
14 t_double_class_init (TDoubleClass *class) {
15 }
16
17 static void
18 t_double_init (TDouble *self) {
19 }
20
21 int main (int argc, char **argv) {
22 GType dtype;
23 TDouble *d;
24
25 dtype = t_double_get_type (); /* or dtype = T_TYPE_DOUBLE */
26 if (dtype)
27 g_print ("Registration was a success. The type is %lx.\n", dtype);
28 else
29 g_print ("Registration failed.\n");
30
31 d = g_object_new (T_TYPE_DOUBLE, NULL);
32 if (d)
33 g_print ("Instantiation was a success. The instance address is %
p.\n", d);
34 else
35 g_print ("Instantiation failed.\n");
36
37 if (T_IS_DOUBLE (d))
38 g_print ("d is TDouble instance.\n");
39 else
40 g_print ("d is not TDouble instance.\n");
41
42 if (G_IS_OBJECT (d))
43 g_print ("d is GObject instance.\n");
44 else
23
45 g_print ("d is not GObject instance.\n");
46
g_object_unref (d);
47
48
return 0;
49
}
Execute it.
Plain Text
1 $ cd src/misc; _build/example5
2 Registration was a success. The type is 5560b4cf58a0.
3 Instantiation was a success. The instance address is 0x5560b4cf6400.
4 d is TDouble instance.
5 d is GObject instance.
C
1 #pragma once
2
3 #include <glib-object.h>
4
5 #define T_TYPE_DOUBLE (t_double_get_type ())
6 G_DECLARE_FINAL_TYPE (TDouble, t_double, T, DOUBLE, GObject)
7
8 gboolean
9 t_double_get_value (TDouble *self, double *value);
10
11 void
12 t_double_set_value (TDouble *self, double value);
13
14 TDouble *
15 t_double_new (double value);
● Header files are public, i.e. it is open to any files. Header files include macros, which
gives type information, cast and type check, and public functions.
24
● 1: The directive #pragma once prevent the compiler from reading the header file two
times or more. It is not officially defined but is supported widely in many compilers.
● 5-6: T_TYPE_DOUBLE is public. G_DECLARE_FINAL_TYPE is expanded to public
definitions.
● 8-12: Public function declarations. They are getter and setter of the value of the
object. They are called “instance methods”, which are used in object-oriented
languages.
● 14-15: Object instantiation function.
tdouble.c
25
C
1 #include "tdouble.h"
2
3 struct _TDouble {
4 GObject parent;
5 double value;
6 };
7
8 G_DEFINE_TYPE (TDouble, t_double, G_TYPE_OBJECT)
9
10 static void
11 t_double_class_init (TDoubleClass *class) {
12 }
13
14 static void
15 t_double_init (TDouble *self) {
16 }
17
18 gboolean
19 t_double_get_value (TDouble *self, double *value) {
20 g_return_val_if_fail (T_IS_DOUBLE (self), FALSE);
21
22 *value = self->value;
23 return TRUE;
24 }
25
26 void
27 t_double_set_value (TDouble *self, double value) {
28 g_return_if_fail (T_IS_DOUBLE (self));
29
30 self->value = value;
31 }
32
33 TDouble *
34 t_double_new (double value) {
35 TDouble *d;
36
37 d = g_object_new (T_TYPE_DOUBLE, NULL);
38 d->value = value;
39 return d;
40 }
27
C
1 #include <glib-object.h>
2 #include "tdouble.h"
3
4 int
5 main (int argc, char **argv) {
6 TDouble *d;
7 double value;
8
9 d = t_double_new (10.0);
10 if (t_double_get_value (d, &value))
11 g_print ("t_double_get_value succesfully assigned %lf to value.\n"
, value);
12 else
13 g_print ("t_double_get_value failed.\n");
14
15 t_double_set_value (d, -20.0);
16 g_print ("Now, set d (tDouble object) with %lf.\n", -20.0);
17 if (t_double_get_value (d, &value))
18 g_print ("t_double_get_value succesfully assigned %lf to value.\n"
, value);
19 else
20 g_print ("t_double_get_value failed.\n");
21 g_object_unref (d);
22
23 return 0;
24 }
The source files are located in src/tdouble1. Change your current directory to the directory
above and type the following.
Plain Text
1 $ cd src/tdouble1
2 $ meson setup _build
3 $ ninja -C _build
28
Then, execute the program.
Plain Text
1 $ cd src/tdouble1; _build/example6
2 t_double_get_value succesfully assigned 10.000000 to value.
3 Now, set d (tDouble object) with -20.000000.
4 t_double_get_value succesfully assigned -20.000000 to value.
This example is very simple. But any object has header file and C source file like this. And
they follow the convention. You probably aware of the importance of the convention. For
the further information refer to GObject API Reference – Conventions.
Functions
Functions of objects are open to other objects. They are like public methods in object
oriented languages. They are actually called “instance method” in the GObject API
Reference.
It is natural to add calculation operators to TDouble objects because they represent real
numbers. For example, t_double_add adds the value of the instance and another instance.
Then it creates a new TDouble instance which has a value of the sum of them.
C
1 TDouble *
2 t_double_add (TDouble *self, TDouble *other) {
3 g_return_val_if_fail (T_IS_DOUBLE (self), NULL);
4 g_return_val_if_fail (T_IS_DOUBLE (other), NULL);
5 double value;
6
7 if (! t_double_get_value (other, &value))
8 return NULL;
9 return t_double_new (self->value + value);
10 }
The first argument self is the instance the function belongs to. The second argument other
is another TDouble instance.
The value of self can be accessed by self->value, but don’t use other->value to get the
value of other. Use a function t_double_get_value instead. Because self is an instance out
29
of other. Generally, the structure of an object isn’t open to other objects. When an object
A access to another object B, A must use a public function provided by B.
Exercise
Write functions of TDouble object for subtraction, multiplication, division and sign
changing (unary minus). Compare your program to tdouble.c in src/tdouble2 directory.
Signals
Signals
Signals provide a means of communication between objects. Signals are emitted when
something happens or completes.
The steps to program a signal is shown below.
1. Register a signal. A signal belongs to an object, so the registration is done in the class
initialization function of the object.
2. Write a signal handler. A signal handler is a function that is invoked when the signal is
emitted.
3. Connect the signal and handler. Signals are connected to handlers with
g_connect_signal or its family functions.
4. Emit the signal.
Step one and Four are done on the object to which the signal belongs. Step three is usually
done outside the object.
The process of signals is complicated and it takes long to explain all the features. The
contents of this section is limited to the minimum things to write a simple signal and not
necessarily accurate. If you need an accurate information, refer to GObject API reference.
There are four parts which describe signals.
● Type System Concepts – signals
● Funcions (g_signal_XXX series)
● Funcions Macros (g_signal_XXX series)
● GObject Tutorial – How to create and use signals
30
Signal registration
An example in this section is a signal emitted when division-by-zero happens. First, we
need to determine the name of the signal. Signal name consists of letters, digits, dash (-)
and underscore (_). The first character of the name must be a letter. So, a string “div-by-
zero” is appropriate for the signal name.
There are four functions to register a signal. We will use g_signal_new for “div-by-zero”
signal.
C
1 guint
2 g_signal_new (const gchar *signal_name,
3 GType itype,
4 GSignalFlags signal_flags,
5 guint class_offset,
6 GSignalAccumulator accumulator,
7 gpointer accu_data,
8 GSignalCMarshaller c_marshaller,
9 GType return_type,
10 guint n_params,
11 ...);
It needs a lot to explain each parameter. At present I just show you g_signal_new function
call extracted from tdouble.c.
31
C
1 t_double_signal =
2 g_signal_new ("div-by-zero",
3 G_TYPE_FROM_CLASS (class),
4 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
5 0 /* class offset.Subclass cannot override the class handle
r (default handler). */,
6 NULL /* accumulator */,
7 NULL /* accumulator data */,
8 NULL /* C marshaller. g_cclosure_marshal_generic() will be u
sed */,
9 G_TYPE_NONE /* return_type */,
10 0 /* n_params */
11 );
● t_double_signal is a static guint variable. The type guint is the same as unsigned int. It
is set to the signal id returned by the function g_signal_new.
● The second parameter is the type (GType) of the object the signal belongs to.
G_TYPE_FROM_CLASS (class) returns the type corresponds to the class (class is a
pointer to the class of the object).
● The third parameter is a signal flag. Lots of pages are necessary to explain this flag.
So, I want leave them out now. The argument above can be used in many cases. The
definition is described in the GObject API Reference – SignalFlags.
● The return type is G_TYPE_NONE which means no value is returned by the signal
handler.
● n_params is a number of parameters. This signal doesn’t have parameters, so it is zero.
Signal handler
Signal handler is a function that is called when the signal is emitted. The handler has two
parameters.
● The instance to which the signal belongs
● A pointer to a user data which is given in the signal connection.
The first argument self is the instance on which the signal is emitted. You can leave out
the second parameter.
C
1 void div_by_zero_cb (TDouble *self) { ... ... ...}
If a signal has parameters, the parameters are between the instance and the user data. For
example, the handler of “window-added” signal on GtkApplication is:
C
1 void window_added (GtkApplication* self, GtkWindow* window, gpointer user_d
ata);
The second argument window is the parameter of the signal. The “window-added” signal
is emitted when a new window is added to the application. The parameter window points a
newly added window. See GTK API reference for further information.
The handler of “div-by-zero” signal just shows an error message.
C
1 static void
2 div_by_zero_cb (TDouble *self, gpointer user_data) {
3 g_print ("\nError: division by zero.\n\n");
4 }
Signal connection
A signal and a handler are connected with the function g_signal_connect.
33
C
1 g_signal_connect (self, "div-by-zero", G_CALLBACK (div_by_zero_cb), NULL);
Signal emission
Signals are emitted on the object. The following is a part of tdouble.c.
C
1 TDouble *
2 t_double_div (TDouble *self, TDouble *other) {
3 ... ... ...
4 if ((! t_double_get_value (other, &value)))
5 return NULL;
6 else if (value == 0) {
7 g_signal_emit (self, t_double_signal, 0);
8 return NULL;
9 }
10 return t_double_new (self->value / value);
11 }
If the divisor is zero, the signal is emitted. g_signal_emit has three parameters.
● The first parameter is the instance that emits the signal.
● The second parameter is the signal id. Signal id is the value returned by the function
g_signal_new.
● The third parameter is a detail. “div-by-zero” signal doesn’t have a detail, so the
argument is zero. Detail isn’t explained in this section but usually you can put zero as a
third argument. If you want to know the details, refer to GObject API Reference –
Signal Detail.
If a signal has parameters, they are fourth and subsequent arguments.
34
Example
A sample program is in src/tdouble3.
tdouble.h
C
1 #pragma once
2
3 #include <glib-object.h>
4
5 #define T_TYPE_DOUBLE (t_double_get_type ())
6 G_DECLARE_FINAL_TYPE (TDouble, t_double, T, DOUBLE, GObject)
7
8 /* getter and setter */
9 gboolean
10 t_double_get_value (TDouble *self, double *value);
11
12 void
13 t_double_set_value (TDouble *self, double value);
14
15 /* arithmetic operator */
16 /* These operators create a new instance and return a pointer to it. */
17 TDouble *
18 t_double_add (TDouble *self, TDouble *other);
19
20 TDouble *
21 t_double_sub (TDouble *self, TDouble *other);
22
23 TDouble *
24 t_double_mul (TDouble *self, TDouble *other);
25
26 TDouble *
27 t_double_div (TDouble *self, TDouble *other);
28
29 TDouble *
30 t_double_uminus (TDouble *self);
31
32 /* create a new TDouble instance */
33 TDouble *
34 t_double_new (double value);
tdouble.c
35
36
C
1 #include "tdouble.h"
2
3 static guint t_double_signal;
4
5 struct _TDouble {
6 GObject parent;
7 double value;
8 };
9
10 G_DEFINE_TYPE (TDouble, t_double, G_TYPE_OBJECT)
11
12 static void
13 t_double_class_init (TDoubleClass *class) {
14 t_double_signal = g_signal_new ("div-by-zero",
15 G_TYPE_FROM_CLASS (class),
16 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE
| G_SIGNAL_NO_HOOKS,
17 0 /* class offset.Subclass cannot overr
ide the class handler (default handler). */,
18 NULL /* accumulator */,
19 NULL /* accumulator data */,
20 NULL /* C marshaller. g_cclosure_marsha
l_generic() will be used */,
21 G_TYPE_NONE /* return_type */,
22 0 /* n_params */
23 );
24 }
25
26 static void
27 t_double_init (TDouble *self) {
28 }
29
30 /* getter and setter */
31 gboolean
32 t_double_get_value (TDouble *self, double *value) {
33 g_return_val_if_fail (T_IS_DOUBLE (self), FALSE);
34
35 *value = self->value;
36 return TRUE;
37 }
38
39 void
40 t_double_set_value (TDouble *self, double value) {
41 g_return_if_fail (T_IS_DOUBLE (self));
42
37
43 self->value = value;
44
}
45
46
/* arithmetic operator */
47
/* These operators create a new instance and return a pointer to it. */
48
#define t_double_binary_op(op) \
49
if (! t_double_get_value (other, &value)) \
50
return NULL; \
51
return t_double_new (self->value op value);
52
53
TDouble *
54
t_double_add (TDouble *self, TDouble *other) {
55
g_return_val_if_fail (T_IS_DOUBLE (self), NULL);
56
g_return_val_if_fail (T_IS_DOUBLE (other), NULL);
57
double value;
58
59
t_double_binary_op (+)
60
}
61
62
TDouble *
63
t_double_sub (TDouble *self, TDouble *other) {
64
g_return_val_if_fail (T_IS_DOUBLE (self), NULL);
65
g_return_val_if_fail (T_IS_DOUBLE (other), NULL);
66
double value;
67
68
t_double_binary_op (-)
69
}
70
71
TDouble *
72
t_double_mul (TDouble *self, TDouble *other) {
73
g_return_val_if_fail (T_IS_DOUBLE (self), NULL);
74
g_return_val_if_fail (T_IS_DOUBLE (other), NULL);
75
double value;
76
77
t_double_binary_op (*)
78
}
79
80
TDouble *
81
t_double_div (TDouble *self, TDouble *other) {
82
g_return_val_if_fail (T_IS_DOUBLE (self), NULL);
83
g_return_val_if_fail (T_IS_DOUBLE (other), NULL);
84
double value;
85
86
if ((! t_double_get_value (other, &value)))
87
return NULL;
88
else if (value == 0) {
89
g_signal_emit (self, t_double_signal, 0);
90
return NULL;
38
91 }
92
return t_double_new (self->value / value);
93
}
94
TDouble *
95
t_double_uminus (TDouble *self) {
96
g_return_val_if_fail (T_IS_DOUBLE (self), NULL);
97
98
return t_double_new (-self->value);
99
}
100
101
TDouble *
102
t_double_new (double value) {
103
TDouble *d;
104
105
d = g_object_new (T_TYPE_DOUBLE, NULL);
106
d->value = value;
107
return d;
108
}
main.c
39
C
1 #include <glib-object.h>
2 #include "tdouble.h"
3
4 static void
5 div_by_zero_cb (TDouble *self, gpointer user_data) {
6 g_printerr ("\nError: division by zero.\n\n");
7 }
8
9 static void
10 t_print (char *op, TDouble *d1, TDouble *d2, TDouble *d3) {
11 double v1, v2, v3;
12
13 if (! t_double_get_value (d1, &v1))
14 return;
15 if (! t_double_get_value (d2, &v2))
16 return;
17 if (! t_double_get_value (d3, &v3))
18 return;
19
20 g_print ("%lf %s %lf = %lf\n", v1, op, v2, v3);
21 }
22
23 int
24 main (int argc, char **argv) {
25 TDouble *d1, *d2, *d3;
26 double v1, v3;
27
28 d1 = t_double_new (10.0);
29 d2 = t_double_new (20.0);
30 if ((d3 = t_double_add (d1, d2)) != NULL) {
31 t_print ("+", d1, d2, d3);
32 g_object_unref (d3);
33 }
34
35 if ((d3 = t_double_sub (d1, d2)) != NULL) {
36 t_print ("-", d1, d2, d3);
37 g_object_unref (d3);
38 }
39
40 if ((d3 = t_double_mul (d1, d2)) != NULL) {
41 t_print ("*", d1, d2, d3);
42 g_object_unref (d3);
43 }
44
45 if ((d3 = t_double_div (d1, d2)) != NULL) {
40
46 t_print ("/", d1, d2, d3);
47
g_object_unref (d3);
48
}
49
50
g_signal_connect (d1, "div-by-zero", G_CALLBACK (div_by_zero_cb), NULL
);
51
t_double_set_value (d2, 0.0);
52
if ((d3 = t_double_div (d1, d2)) != NULL) {
53
t_print ("/", d1, d2, d3);
54
g_object_unref (d3);
55
}
56
57
if ((d3 = t_double_uminus (d1)) != NULL && (t_double_get_value (d1, &v
1)) && (t_double_get_value (d3, &v3))) {
58
g_print ("-%lf = %lf\n", v1, v3);
59
g_object_unref (d3);
60
}
61
62
g_object_unref (d1);
63
g_object_unref (d2);
64
65
return 0;
66
}
Then, Executable file tdouble is created in the _build directory. Execute it.
Plain Text
1 $ _build/tdouble
2 10.000000 + 20.000000 = 30.000000
3 10.000000 - 20.000000 = -10.000000
4 10.000000 * 20.000000 = 200.000000
5 10.000000 / 20.000000 = 0.500000
6
7 Error: division by zero.
8
9 -10.000000 = -10.000000
41
Default signal handler
You may have thought that it was strange that the error message was set in main.c.
Indeed, the error happens in tdouble.c so the message should been managed by tdouble.c
itself. GObject system has a default signal handler that is set in the object itself. A default
signal handler is also called “default handler” or “object method handler”.
You can set a default handler with g_signal_new_class_handler.
C
1 guint
2 g_signal_new_class_handler (const gchar *signal_name,
3 GType itype,
4 GSignalFlags signal_flags,
5 GCallback class_handler, /*default signal hand
ler */
6 GSignalAccumulator accumulator,
7 gpointer accu_data,
8 GSignalCMarshaller c_marshaller,
9 GType return_type,
10 guint n_params,
11 ...);
The difference from g_signal_new is the fourth parameter. g_signal_new sets a default
handler with the offset of the function pointer in the class structure. If an object is
derivable, it has its own class area, so you can set a default handler with g_signal_new. But
a final type object doesn’t have its own class area, so it’s impossible to set a default
handler with g_signal_new. That’s the reason why we use g_signal_new_class_handler.
The C file tdouble.c is changed like this. The function div_by_zero_default_cb is added and
g_signal_new_class_handler replaces g_signal_new. Default signal handler doesn’t have
user_data parameter. A user_data parameter is set in the g_signal_connect family
functions when a user connects their own signal handler to the signal. Default signal
handler is managed by the instance, not a user. So no user data is given as an argument.
42
C
1 static void
2 div_by_zero_default_cb (TDouble *self) {
3 g_printerr ("\nError: division by zero.\n\n");
4 }
5
6 static void
7 t_double_class_init (TDoubleClass *class) {
8 t_double_signal =
9 g_signal_new_class_handler ("div-by-zero",
10 G_TYPE_FROM_CLASS (class),
11 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_
SIGNAL_NO_HOOKS,
12 G_CALLBACK (div_by_zero_default_cb),
13 NULL /* accumulator */,
14 NULL /* accumulator data */,
15 NULL /* C marshaller */,
16 G_TYPE_NONE /* return_type */,
17 0 /* n_params */
18 );
19 }
43
C
1 static void
2 div_by_zero_cb (TDouble *self, gpointer user_data) {
3 g_print ("\nError happens in main.c.\n");
4 }
5
6 int
7 main (int argc, char **argv) {
8 ... ... ...
9 g_signal_connect (d1, "div-by-zero", G_CALLBACK (div_by_zero_cb),
NULL);
10 ... ... ...
11 }
Then, both the user-provided handler and default handler are called when the signal is
emitted. Compile and execute it, then the following is shown on your display.
Plain Text
1 10.000000 + 20.000000 = 30.000000
2 10.000000 - 20.000000 = -10.000000
3 10.000000 * 20.000000 = 200.000000
4 10.000000 / 20.000000 = 0.500000
5
6 Error happens in main.c.
7
8 Error: division by zero.
9
10 -10.000000 = -10.000000
This tells us that the user-provided handler is called first, then the default handler is called.
If you want your handler called after the default handler, then you can use
g_signal_connect_after. Add the lines below to main.c again.
44
C
1 static void
2 div_by_zero_after_cb (TDouble *self, gpointer user_data) {
3 g_print ("\nError has happened in main.c and an error message has bee
n displayed.\n");
4 }
5
6 int
7 main (int argc, char **argv) {
8 ... ... ...
9 g_signal_connect_after (d1, "div-by-zero", G_CALLBACK (div_by_zero
_after_cb), NULL);
10 ... ... ...
11 }
Signal flag
The order that handlers are called is described in GObject API Reference – Sigmal
emission.
The order depends on the signal flag which is set in g_signal_new or
g_signal_new_class_handler. There are three flags which relate to the order of handlers’
invocation.
45
● G_SIGNAL_RUN_FIRST: the default handler is called before any user provided handler.
● G_SIGNAL_RUN_LAST: the default handler is called after the normal user provided
handler (not connected with g_signal_connect_after).
● G_SIGNAL_RUN_CLEANUP: the default handler is called after any user provided
handler.
G_SIGNAL_RUN_LAST is the most appropriate in many cases.
Other signal flags are described in GObject API Reference.
Properties
GObject system provides properties. Properties are values kept by instances, which is a
descendant of GObject, and they are open to other instances. They can be accessed with
their names.
For example, GtkWindow has “title”, “default-width”, “default-height” and other
properties. The string “title” is the name of the property. The name of a property is a
string that begins with a letter followed by letters, digits, dash (‘-’) or underscore (’_’).
Dash and underscore is used as separators but they cannot be mixed. Using dash is more
efficient than underscore. For example, “value”, “double” and “double-value” are correct
property names. “_value” or “-value” are incorrect.
Properties have various types of values. The type of “title” property is string. The type of
“default-width” and “default-height” is integer.
Properties are set and got with functions defined in GObject.
● Properties can be set with several GObject functions. g_object_new and g_object_set
are often used.
● Properties can be get with several GObject functions. g_object_get is often used.
The functions above belongs to GObject, but they can be used for any descendant object
of GObject. The following is an example of GtkWindow, which is a descendant object of
GObject.
An instance is created and its properties are set with g_object_new.
46
C
1 GtkWindow *win;
2 win = g_object_new (GTK_TYPE_WINDOW, "title", "Hello", "default-width", 800
, "default-height", 600, NULL);
The example above creates an instance of GtkWindow and sets the properties.
● The “title” property is set to “Hello”.
● The “default-width” property is set to 800.
● The “default-height” property is set to 600.
The last parameter of g_object_new is NULL which is the end of the list of properties.
If you have already created a GtkWindow instance and you want to set its title, you can use
g_object_set.
C
1 GtkWindow *win;
2 win = g_object_new (GTK_TYPE_WINDOW, NULL);
3 g_object_set (win, "title", "Good bye", NULL);
C
1 GtkWindow *win;
2 char *title;
3 int width, height;
4
5 win = g_object_new (GTK_TYPE_WINDOW, "title", "Hello", "default-width", 800
, "default-height", 600, NULL);
6 g_object_get (win, "title", &title, "default-width", &width, "default-heigh
t", &height, NULL);
7 g_print ("%s, %d, %d\n", title, width, height);
8 g_free (title);
47
● Define set_property and get_property class method to complement g_object_set and
g_object_get.
GParamSpec
GParamSpec is a fundamental object. GParamSpec and GObject don’t have parent-child
relationship. GParamSpec has information of parameters. “ParamSpec” is short for
“Parameter specification”.
For example,
C
1 double_property = g_param_spec_double ("value", "val", "Double value",
2 -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
3 G_PARAM_READWRITE);
48
For further information, refer to the GObject API reference.
● GParamSpec and its subclasses
● g_param_spec_double and similar functions
● GValue
GParamSpec is used for the registration for GObject properties. This is extracted from
tdouble.c in src/tdouble6.
C
1 #define PROP_DOUBLE 1
2 static GParamSpec *double_property = NULL;
3
4 static void
5 t_double_class_init (TDoubleClass *class) {
6 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
7
8 gobject_class->set_property = t_double_set_property;
9 gobject_class->get_property = t_double_get_property;
10 double_property = g_param_spec_double ("value", "val", "Double value"
, -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, G_PARAM_READWRITE);
11 g_object_class_install_property (gobject_class, PROP_DOUBLE, double_pr
operty);
12 }
49
The function g_object_set is given a value as an argument and stores the value. But how
does g_object_set know the instance to store? It is compiled before the object is made.
So, it doesn’t know where to store the value at all. That part needs to be programmed by
the writer of the object with overriding.
The function g_object_set first checks the property and value, then it creates GValue
(generic value) from the value. And it calls a function pointed by set_property in the class.
Look at the diagram below.
GValue
GValue is generic value. GValue consists of type and value.
The type is any Gtype. The table below shows some GType, but not all.
GType C type type name notes
G_TYPE_CHAR char gchar
G_TYPE_BOOLEAN int gboolean
G_TYPE_INT int gint
G_TYPE_FLOAT float gfloat
G_TYPE_DOUBLE double gdouble
G_TYPE_STRING gchararray null-terminated
Cstring
G_TYPE_PARAM GParam GParamSpec
G_TYPE_OBJECT GObject
G_TYPE_VARIANT GVariant
If the type of a GValue value is G_TYPE_DOUBLE, value can be get with
g_value_get_double function.
Plain Text
1 GValue value;
2 value = ... ... ... (a GValue object is assigned. Its type is double.)
3 double v;
4 v = g_value_get_double (&value);
51
Plain Text
1 g_value_set_double (value, 123.45);
C
1 struct _GObjectClass
2 {
3 ... ... ...
4 ... ... ...
5 /* overridable methods */
6 void (*set_property) (GObject *object,
7 guint property_id,
8 const GValue *value,
9 GParamSpec *pspec);
10 void (*get_property) (GObject *object,
11 guint property_id,
12 GValue *value,
13 GParamSpec *pspec);
14 ... ... ...
15 ... ... ...
16 };
t_double_set_property just get the value from GValue value and store it to the TDouble
instance.
52
C
1 static void
2 t_double_set_property (GObject *object, guint property_id, const GValue *va
lue, GParamSpec *pspec) {
3 TDouble *self = T_DOUBLE (object);
4
5 if (property_id == PROP_DOUBLE)
6 self->value = g_value_get_double (value);
7 else
8 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
9 }
C
1 static void
2 t_double_get_property (GObject *object, guint property_id, GValue *value,
GParamSpec *pspec) {
3 TDouble *self = T_DOUBLE (object);
4
5 if (property_id == PROP_DOUBLE)
6 g_value_set_double (value, self->value);
7 else
8 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
9
10 }
Notify signal
GObject emits “notify” signal when a property is set. When you connect “notify” signal to
your handler, you can specify a detail which is the name of the property. The detail is
added to the signal name with the delimiter “::”.
53
Plain Text
1 g_signal_connect (G_OBJECT (d1), "notify::value", G_CALLBACK (notify_cb), N
ULL);
If you don’t specify details, the handler is called whenever any properties are set. So,
usually the detail is set.
Notify signal doesn’t mean that the value of the property is changed. It is emitted even if
the same value is set. You might want to emit the notify signal only when the property is
actually changed. In that case, you define the GPramSpec with
G_PARAM_EXPLICIT_NOTIFY flag. Then, the notify signal isn’t emitted automatically.
Instead you call g_object_notify_by_pspec function to emit “notify” signal explicitly when
the value of the property is actually changed.
It is possible to make setter and getter for the property. But if you just set the instance
member in your setter, notify signal isn’t emitted.
C
1 void
2 t_double_set_value (TDouble *self, double value) {
3 g_return_if_fail (T_IS_DOUBLE (self));
4
5 self->value = value; /* Just set d->value. No "notify" signal is emitte
d. */
6 }
Users must be confused if they want to catch the “notify” signal. One solution is use
g_object_set in your setter. Then, notify signal will be emitted even if a user uses the setter
function.
54
C
1 void
2 t_double_set_value (TDouble *d, double value) {
3 g_return_if_fail (T_IS_DOUBLE (d));
4
5 g_object_set (d, "value", value, NULL); /* Use g_object_set. "notify" s
ignal will be emitted. */
6 }
The other solution is use g_object_notify_by_pspec to emit the signal explicitly. Anyway, if
you make a setter for your property, be careful about notify signal.
Exercise
Make TInt object. It is like TDouble but the value type is int. Define “div-by-zero” signal
and “value” property.
Compare your answer to the files in src/tint directory.
55
G_DECLARE_DERIVABLE_TYPE is used to declare derivable type. It is written in a header
file like this:
C
1 #define T_TYPE_NUMBER (t_number_get_type ())
2 G_DECLARE_DERIVABLE_TYPE (TNumber, t_number, T, NUMBER, GObject)
Abstract type
Abstract type doesn’t have any instance. This type of object is derivable and its children
can use functions and signals of the abstract object.
The examples of this section are TNumber, TInt and TDouble object. TInt and TDouble
have already been made in the previous section. They represent integer and floating point
respectively. Numbers are more abstract than integer and floating point.
TNumber is an abstract object which represents numbers. TNumber is a parent object of
TInt and TDouble. TNumber isn’t instantiated because its type is abstract. When an
instance has TInt or TDouble type, it is an instance of TNumber as well.
TInt and TDouble have five operations: addition, subtraction, multiplication, division and
unary minus operation. Those operations can be defined on TNumber object.
In this section we will define TNumber object and five functions above. In addition, to_s
function will be added. It converts the value of TNumber into a string. It is like sprintf
function. And we will rewrite TInt and TDouble to implement the functions.
TNumber class
tnumber.h is a header file for the TNumber class.
56
C
1 #pragma once
2
3 #include <glib-object.h>
4
5 #define T_TYPE_NUMBER (t_number_get_type ())
6 G_DECLARE_DERIVABLE_TYPE (TNumber, t_number, T, NUMBER, GObject)
7
8 struct _TNumberClass {
9 GObjectClass parent_class;
10 TNumber* (*add) (TNumber *self, TNumber *other);
11 TNumber* (*sub) (TNumber *self, TNumber *other);
12 TNumber* (*mul) (TNumber *self, TNumber *other);
13 TNumber* (*div) (TNumber *self, TNumber *other);
14 TNumber* (*uminus) (TNumber *self);
15 char * (*to_s) (TNumber *self);
16 /* signal */
17 void (*div_by_zero) (TNumber *self);
18 };
19
20 /* arithmetic operator */
21 /* These operators create a new instance and return a pointer to it. */
22 TNumber *
23 t_number_add (TNumber *self, TNumber *other);
24
25 TNumber *
26 t_number_sub (TNumber *self, TNumber *other);
27
28 TNumber *
29 t_number_mul (TNumber *self, TNumber *other);
30
31 TNumber *
32 t_number_div (TNumber *self, TNumber *other);
33
34 TNumber *
35 t_number_uminus (TNumber *self);
36
37 char *
38 t_number_to_s (TNumber *self);
57
tnumber.c file. The definition is usually done with G_DEFINE_TYPE or its family
macros.
○ Definition of TNumber instance, whose member is its parent only.
○ Declaration of TNumberClass. It should be defined later in the header file.
○ Convenience macros T_NUMBER (cast to instance), T_NUMBER_CLASS (cast to
class), T_IS_NUMBER (instance check), T_IS_NUMBER_CLASS (class check) and
T_NUMBER_GET_CLASS are defined.
○ g_autoptr() support.
● 8-18: Definition of the structure of TNumberClass.
● 10-15: These are pointers to functions. They are called class methods or virtual
functions. They are expected to be overridden in the descendant object. The methods
are five arithmetic operators and to_s function. to_s function is similar to sprintf
function.
● 17: A pointer to the default signal handler of “div-by-zero” signal. The offset of this
pointer is given to g_signal_new as an argument.
● 22-38: Functions. They are public.
tnumber.c is as follows.
58
C
1 #include "tnumber.h"
2
3 static guint t_number_signal;
4
5 G_DEFINE_ABSTRACT_TYPE (TNumber, t_number, G_TYPE_OBJECT)
6
7 static void
8 div_by_zero_default_cb (TNumber *self) {
9 g_printerr ("\nError: division by zero.\n\n");
10 }
11
12 static void
13 t_number_class_init (TNumberClass *class) {
14
15 /* virtual functions */
16 class->add = NULL;
17 class->sub = NULL;
18 class->mul = NULL;
19 class->div = NULL;
20 class->uminus = NULL;
21 class->to_s = NULL;
22 /* default signal handler */
23 class->div_by_zero = div_by_zero_default_cb;
24 /* signal */
25 t_number_signal =
26 g_signal_new ("div-by-zero",
27 G_TYPE_FROM_CLASS (class),
28 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOK
S,
29 G_STRUCT_OFFSET (TNumberClass, div_by_zero),
30 NULL /* accumulator */,
31 NULL /* accumulator data */,
32 NULL /* C marshaller */,
33 G_TYPE_NONE /* return_type */,
34 0 /* n_params */
35 );
36 }
37
38 static void
39 t_number_init (TNumber *self) {
40 }
41
42 TNumber *
43 t_number_add (TNumber *self, TNumber *other) {
44 g_return_val_if_fail (T_IS_NUMBER (self), NULL);
59
45 g_return_val_if_fail (T_IS_NUMBER (other), NULL);
46
47
TNumberClass *class = T_NUMBER_GET_CLASS(self);
48
49
return class->add ? class->add (self, other) : NULL;
50
}
51
52
TNumber *
53
t_number_sub (TNumber *self, TNumber *other) {
54
g_return_val_if_fail (T_IS_NUMBER (self), NULL);
55
g_return_val_if_fail (T_IS_NUMBER (other), NULL);
56
57
TNumberClass *class = T_NUMBER_GET_CLASS(self);
58
59
return class->sub ? class->sub (self, other) : NULL;
60
}
61
62
TNumber *
63
t_number_mul (TNumber *self, TNumber *other) {
64
g_return_val_if_fail (T_IS_NUMBER (self), NULL);
65
g_return_val_if_fail (T_IS_NUMBER (other), NULL);
66
67
TNumberClass *class = T_NUMBER_GET_CLASS(self);
68
69
return class->mul ? class->mul (self, other) : NULL;
70
}
71
72
TNumber *
73
t_number_div (TNumber *self, TNumber *other) {
74
g_return_val_if_fail (T_IS_NUMBER (self), NULL);
75
g_return_val_if_fail (T_IS_NUMBER (other), NULL);
76
77
TNumberClass *class = T_NUMBER_GET_CLASS(self);
78
79
return class->div ? class->div (self, other) : NULL;
80
}
81
82
TNumber *
83
t_number_uminus (TNumber *self) {
84
g_return_val_if_fail (T_IS_NUMBER (self), NULL);
85
86
TNumberClass *class = T_NUMBER_GET_CLASS(self);
87
88
return class->uminus ? class->uminus (self) : NULL;
89
}
90
91
char *
92
t_number_to_s (TNumber *self) {
60
93 g_return_val_if_fail (T_IS_NUMBER (self), NULL);
94
95
TNumberClass *class = T_NUMBER_GET_CLASS(self);
96
97
return class->to_s ? class->to_s (self) : NULL;
98
}
TInt object.
tint.h is a header file of the TInt class. TInt is a child class of TNumber.
61
C
1 #pragma once
2
3 #include <glib-object.h>
4
5 #define T_TYPE_INT (t_int_get_type ())
6 G_DECLARE_FINAL_TYPE (TInt, t_int, T, INT, TNumber)
7
8 /* create a new TInt instance */
9 TInt *
10 t_int_new_with_value (int value);
11
12 TInt *
13 t_int_new (void);
● 9-13:Declares public functions. Arithmetic functions and to_s are declared in TNumber,
so TInt doesn’t declare those functions. Only instance creation functions are declared.
The C file tint.c implements virtual functions (class methods). And the pointers of the
methods in TNumberClass are rewritten here.
62
C
1 #include "tnumber.h"
2 #include "tint.h"
3 #include "tdouble.h"
4
5 #define PROP_INT 1
6 static GParamSpec *int_property = NULL;
7
8 struct _TInt {
9 TNumber parent;
10 int value;
11 };
12
13 G_DEFINE_TYPE (TInt, t_int, T_TYPE_NUMBER)
14
15 static void
16 t_int_set_property (GObject *object, guint property_id, const GValue *val
ue, GParamSpec *pspec) {
17 TInt *self = T_INT (object);
18
19 if (property_id == PROP_INT)
20 self->value = g_value_get_int (value);
21 else
22 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
23 }
24
25 static void
26 t_int_get_property (GObject *object, guint property_id, GValue *value, GP
aramSpec *pspec) {
27 TInt *self = T_INT (object);
28
29 if (property_id == PROP_INT)
30 g_value_set_int (value, self->value);
31 else
32 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
33 }
34
35 static void
36 t_int_init (TInt *self) {
37 }
38
39 /* arithmetic operator */
40 /* These operators create a new instance and return a pointer to it. */
41 #define t_int_binary_op(op) \
42 int i; \
43 double d; \
63
44 if (T_IS_INT (other)) { \
45
g_object_get (T_INT (other), "value", &i, NULL); \
46
return T_NUMBER (t_int_new_with_value (T_INT(self)->value op i)); \
47
} else { \
48
g_object_get (T_DOUBLE (other), "value", &d, NULL); \
49
return T_NUMBER (t_int_new_with_value (T_INT(self)->value op (int)
d)); \
50
}
51
52
static TNumber *
53
t_int_add (TNumber *self, TNumber *other) {
54
g_return_val_if_fail (T_IS_INT (self), NULL);
55
56
t_int_binary_op (+)
57
}
58
59
static TNumber *
60
t_int_sub (TNumber *self, TNumber *other) {
61
g_return_val_if_fail (T_IS_INT (self), NULL);
62
63
t_int_binary_op (-)
64
}
65
66
static TNumber *
67
t_int_mul (TNumber *self, TNumber *other) {
68
g_return_val_if_fail (T_IS_INT (self), NULL);
69
70
t_int_binary_op (*)
71
}
72
73
static TNumber *
74
t_int_div (TNumber *self, TNumber *other) {
75
g_return_val_if_fail (T_IS_INT (self), NULL);
76
77
int i;
78
double d;
79
80
if (T_IS_INT (other)) {
81
g_object_get (T_INT (other), "value", &i, NULL);
82
if (i == 0) {
83
g_signal_emit_by_name (self, "div-by-zero");
84
return NULL;
85
} else
86
return T_NUMBER (t_int_new_with_value (T_INT(self)->value /
i));
87
} else {
88
g_object_get (T_DOUBLE (other), "value", &d, NULL);
89
if (d == 0) {
64
90 g_signal_emit_by_name (self, "div-by-zero");
91
return NULL;
92
} else
93
return T_NUMBER (t_int_new_with_value (T_INT(self)->value /
(int) d));
94
}
95
}
96
97
static TNumber *
98
t_int_uminus (TNumber *self) {
99
g_return_val_if_fail (T_IS_INT (self), NULL);
100
101
return T_NUMBER (t_int_new_with_value (- T_INT(self)->value));
102
}
103
104
static char *
105
t_int_to_s (TNumber *self) {
106
g_return_val_if_fail (T_IS_INT (self), NULL);
107
108
int i;
109
110
g_object_get (T_INT (self), "value", &i, NULL);
111
return g_strdup_printf ("%d", i);
112
}
113
114
static void
115
t_int_class_init (TIntClass *class) {
116
TNumberClass *tnumber_class = T_NUMBER_CLASS (class);
117
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
118
119
/* override virtual functions */
120
tnumber_class->add = t_int_add;
121
tnumber_class->sub = t_int_sub;
122
tnumber_class->mul = t_int_mul;
123
tnumber_class->div = t_int_div;
124
tnumber_class->uminus = t_int_uminus;
125
tnumber_class->to_s = t_int_to_s;
126
127
gobject_class->set_property = t_int_set_property;
128
gobject_class->get_property = t_int_get_property;
129
int_property = g_param_spec_int ("value", "val", "Integer value", G_M
ININT, G_MAXINT, 0, G_PARAM_READWRITE);
130
g_object_class_install_property (gobject_class, PROP_INT, int_propert
y);
131
}
132
133
TInt *
134
t_int_new_with_value (int value) {
65
135 TInt *i;
136
137
i = g_object_new (T_TYPE_INT, "value", value, NULL);
138
return i;
139
}
140
141
TInt *
142
t_int_new (void) {
143
TInt *i;
144
145
i = g_object_new (T_TYPE_INT, NULL);
146
return i;
147
}
● 5-6, 15-33, 127-130: Definition of the property “value”. This is the same as before.
● 8-11: Definition of the structure of TInt. This must be defined before G_DEFINE_TYPE.
● 13: G_DEFINE_TYPE macro. This macro expands to:
○ Declaration of t_int_init () function.
○ Definition of t_int_get_type () function.
○ Definition of t_int_parent_class static variable which points the parent class.
● 35-37: t_int_init.
● 41-112: These functions are connected to the class method pointers in TIntClass. They
are the implementation of the virtual functions defined in tnumber.c.
● 41-50: Defines a macro used in t_int_add, t_int_sub and t_int_mul. This macro is
similar to t_int_div function. Refer to the explanation below for t_int_div.
● 52-71: The functions t_int_add, t_int_sub and t_int_mul. The macro t_int_binary_op is
used.
● 73-95: The function t_int_div. The first argument self is the object on which the
function is called. The second argument other is another TNumber object. It can be
TInt or TDouble. If it is TDouble, its value is casted to int before the division is
performed. If the divisor (other) is zero, “div-by-zero” signal is emitted. The signal is
defined in TNumber, so TInt doesn’t know the signal id. Therefore, the emission is
done with g_signal_emit_by_name instead of g_signal_emit. The return value of t_int_div
is TNumber type object However, because TNumber is abstract, the actual type of the
object is TInt.
● 97-102: A function for unary minus operator.
● 104-112: The function to_s. This function converts int to string. For example, if the
66
value of the object is 123, then the result is a string “123”. The caller should free the
string if it becomes useless.
● 114- 131: The class initialization function t_int_class_init.
● 120-125: The class methods are overridden. For example, if t_number_add is called on
a TInt object, then the function calls the class method *tnumber_class->add. The
pointer points t_int_add function. Therefore, t_int_add is finally called.
● 133-147: Instance creation functions are the same as before.
TDouble object.
TDouble object is defined with tdouble.h and tdouble.c. The definition is very similar to
TInt. So, this subsection just shows the contents of the files.
tdouble.h
Plain Text
1 #pragma once
2
3 #include <glib-object.h>
4
5 #define T_TYPE_DOUBLE (t_double_get_type ())
6 G_DECLARE_FINAL_TYPE (TDouble, t_double, T, DOUBLE, TNumber)
7
8 /* create a new TDouble instance */
9 TDouble *
10 t_double_new_with_value (double value);
11
12 TDouble *
13 t_double_new (void);
tdouble.c
67
C
1 #include "tnumber.h"
2 #include "tdouble.h"
3 #include "tint.h"
4
5 #define PROP_DOUBLE 1
6 static GParamSpec *double_property = NULL;
7
8 struct _TDouble {
9 TNumber parent;
10 double value;
11 };
12
13 G_DEFINE_TYPE (TDouble, t_double, T_TYPE_NUMBER)
14
15 static void
16 t_double_set_property (GObject *object, guint property_id, const GValue *
value, GParamSpec *pspec) {
17 TDouble *self = T_DOUBLE (object);
18 if (property_id == PROP_DOUBLE) {
19 self->value = g_value_get_double (value);
20 } else
21 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
22 }
23
24 static void
25 t_double_get_property (GObject *object, guint property_id, GValue *value
, GParamSpec *pspec) {
26 TDouble *self = T_DOUBLE (object);
27
28 if (property_id == PROP_DOUBLE)
29 g_value_set_double (value, self->value);
30 else
31 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
32 }
33
34 static void
35 t_double_init (TDouble *self) {
36 }
37
38 /* arithmetic operator */
39 /* These operators create a new instance and return a pointer to it. */
40 #define t_double_binary_op(op) \
41 int i; \
42 double d; \
43 if (T_IS_INT (other)) { \
68
44 g_object_get (T_INT (other), "value", &i, NULL); \
45
return T_NUMBER (t_double_new_with_value (T_DOUBLE(self)->value op
(double) i)); \
46
} else { \
47
g_object_get (T_DOUBLE (other), "value", &d, NULL); \
48
return T_NUMBER (t_double_new_with_value (T_DOUBLE(self)->value op
d)); \
49
}
50
51
static TNumber *
52
t_double_add (TNumber *self, TNumber *other) {
53
g_return_val_if_fail (T_IS_DOUBLE (self), NULL);
54
55
t_double_binary_op (+)
56
}
57
58
static TNumber *
59
t_double_sub (TNumber *self, TNumber *other) {
60
g_return_val_if_fail (T_IS_DOUBLE (self), NULL);
61
62
t_double_binary_op (-)
63
}
64
65
static TNumber *
66
t_double_mul (TNumber *self, TNumber *other) {
67
g_return_val_if_fail (T_IS_DOUBLE (self), NULL);
68
69
t_double_binary_op (*)
70
}
71
72
static TNumber *
73
t_double_div (TNumber *self, TNumber *other) {
74
g_return_val_if_fail (T_IS_DOUBLE (self), NULL);
75
76
int i;
77
double d;
78
79
if (T_IS_INT (other)) {
80
g_object_get (T_INT (other), "value", &i, NULL);
81
if (i == 0) {
82
g_signal_emit_by_name (self, "div-by-zero");
83
return NULL;
84
} else
85
return T_NUMBER (t_double_new_with_value (T_DOUBLE(self)->va
lue / (double) i));
86
} else {
87
g_object_get (T_DOUBLE (other), "value", &d, NULL);
88
if (d == 0) {
69
89 g_signal_emit_by_name (self, "div-by-zero");
90
return NULL;
91
} else
92
return T_NUMBER (t_double_new_with_value (T_DOUBLE(self)->va
lue / d));
93
}
94
}
95
96
static TNumber *
97
t_double_uminus (TNumber *self) {
98
g_return_val_if_fail (T_IS_DOUBLE (self), NULL);
99
100
return T_NUMBER (t_double_new_with_value (- T_DOUBLE(self)->value));
101
}
102
103
static char *
104
t_double_to_s (TNumber *self) {
105
g_return_val_if_fail (T_IS_DOUBLE (self), NULL);
106
107
double d;
108
109
g_object_get (T_DOUBLE (self), "value", &d, NULL);
110
return g_strdup_printf ("%lf", d);
111
}
112
113
static void
114
t_double_class_init (TDoubleClass *class) {
115
TNumberClass *tnumber_class = T_NUMBER_CLASS (class);
116
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
117
118
/* override virtual functions */
119
tnumber_class->add = t_double_add;
120
tnumber_class->sub = t_double_sub;
121
tnumber_class->mul = t_double_mul;
122
tnumber_class->div = t_double_div;
123
tnumber_class->uminus = t_double_uminus;
124
tnumber_class->to_s = t_double_to_s;
125
126
gobject_class->set_property = t_double_set_property;
127
gobject_class->get_property = t_double_get_property;
128
double_property = g_param_spec_double ("value", "val", "Double value"
, -G_MAXDOUBLE, G_MAXDOUBLE, 0, G_PARAM_READWRITE);
129
g_object_class_install_property (gobject_class, PROP_DOUBLE, double_p
roperty);
130
}
131
132
TDouble *
133
t_double_new_with_value (double value) {
70
134 TDouble *d;
135
136
d = g_object_new (T_TYPE_DOUBLE, "value", value, NULL);
137
return d;
138
}
139
140
TDouble *
141
t_double_new (void) {
142
TDouble *d;
143
144
d = g_object_new (T_TYPE_DOUBLE, NULL);
145
return d;
146
}
main.c
main.c is a simple program to test the objects.
71
C
1 #include <glib-object.h>
2 #include "tnumber.h"
3 #include "tint.h"
4 #include "tdouble.h"
5
6 static void
7 notify_cb (GObject *gobject, GParamSpec *pspec, gpointer user_data) {
8 const char *name;
9 int i;
10 double d;
11
12 name = g_param_spec_get_name (pspec);
13 if (T_IS_INT (gobject) && strcmp (name, "value") == 0) {
14 g_object_get (T_INT (gobject), "value", &i, NULL);
15 g_print ("Property \"%s\" is set to %d.\n", name, i);
16 } else if (T_IS_DOUBLE (gobject) && strcmp (name, "value") == 0) {
17 g_object_get (T_DOUBLE (gobject), "value", &d, NULL);
18 g_print ("Property \"%s\" is set to %lf.\n", name, d);
19 }
20 }
21
22 int
23 main (int argc, char **argv) {
24 TNumber *i, *d, *num;
25 char *si, *sd, *snum;
26
27 i = T_NUMBER (t_int_new ());
28 d = T_NUMBER (t_double_new ());
29
30 g_signal_connect (G_OBJECT (i), "notify::value", G_CALLBACK (notify_cb
), NULL);
31 g_signal_connect (G_OBJECT (d), "notify::value", G_CALLBACK (notify_cb
), NULL);
32
33 g_object_set (T_INT (i), "value", 100, NULL);
34 g_object_set (T_DOUBLE (d), "value", 12.345, NULL);
35
36 num = t_number_add (i, d);
37
38 si = t_number_to_s (i);
39 sd = t_number_to_s (d);
40 snum = t_number_to_s (num);
41
42 g_print ("%s + %s is %s.\n", si, sd, snum);
43
72
44 g_object_unref (num);
45
g_free (snum);
46
47
num = t_number_add (d, i);
48
snum = t_number_to_s (num);
49
50
g_print ("%s + %s is %s.\n", sd, si, snum);
51
52
g_object_unref (num);
53
g_free (sd);
54
g_free (snum);
55
56
g_object_set (T_DOUBLE (d), "value", 0.0, NULL);
57
sd = t_number_to_s (d);
58
if ((num = t_number_div(i, d)) != NULL) {
59
snum = t_number_to_s (num);
60
g_print ("%s / %s is %s.\n", si, sd, snum);
61
g_object_unref (num);
62
g_free (snum);
63
}
64
65
g_object_unref (i);
66
g_object_unref (d);
67
g_free (si);
68
g_free (sd);
69
70
return 0;
71
}
● 6-20: “notify” handler. This handler is upgraded to support both TInt and TDouble.
● 22-71: The function main.
● 30-31: Connects the notify signals on i (TInt) and d (TDouble).
● 33-34: Set “value” properties on i and d.
● 36: Add d to i. The answer is TInt object.
● 47: Add i to d. The answer is TDouble object. The addition of two TNumber objects
isn’t commutative because the type of the result will be different if the two objects are
exchanged.
● 56-63: Tests division by zero signal.
Plain Text
1 project ('tnumber', 'c')
2
3 gobjdep = dependency('gobject-2.0')
4
5 sourcefiles = files('main.c', 'tnumber.c', 'tint.c', 'tdouble.c')
6
7 executable('tnumber', sourcefiles, dependencies: gobjdep, install: false)
75
TNumberClass initialization
Initialization process of TIntClass
1. TNumberClass has been initialized before the initialization of TIntClass starts.
2. First call for g_object_new (T_TYPE_INT, ...) initializes TIntClass. And the initialization
process is as follows.
3. Memory is allocated for TIntClass. TIntClass doesn’t have its own area. Therefore its
structure is the same as its parent class (TNumberClass).
4. The parent (TNumberClass) part of the class (This is the same as whole TIntClass) is
copied from TNumberClass.
5. The class initialization function t_int_class_init is called. It overrides class methods
from TNumber, set_property and get_property.
The diagram below shows the process.
76
TIntClass initialization
Plain Text
1 char *s;
2 s = g_strdup ("Hello.");
3 ... ... ... do something with s
4 g_free (s);
If the string s is no longer in use, s must be freed, which means the allocated 7 bytes must
be returned to the system. g_free frees the memory.
Strings bounded by double quotes like “Hello.” are string literals. They are an array of
characters, but the contents of the array are not allowed to change. And they mustn’t be
78
freed. If you write a character in a string literal or free a string literal, the result is
undefined. Maybe bad things will happen, for example, a segmentation fault error.
There’s a difference between arrays and pointers when you initialize them with a string
literal. If an array is initialized with a string literal, the array can be changed.
Plain Text
1 char a[]="Hello!";
2 a[1]='a';
3 g_print ("%s\n", a); /* Hallo will appear on your display. */
The first line initializes an array a. The initialization is not simple. First, the compiler
calculates the length of “Hello!”. It is seven because the string literal has ‘\0’ at the end of
it. Then seven bytes memory is allocated in static memory or stack memory. It depends on
the class of the array, whether static or auto. The memory is initialized with “Hello!”. So,
the string in the array can be changed. This program successfully displays `Hallo!.
The first line of the program above is the same as follows.
Plain Text
1 char a[] = {'H', 'e', 'l', 'l', 'o', '!', '\0'};
If you define a pointer with string literal, you can’t change the string pointed by the pointer.
Plain Text
1 char *a = "Hello";
2 *(a+1) = 'a'; /* This is illegal. */
3 g_print ("%s\n", a);
The first line just assigns the address of the string literal to the variable a. String literal is
an array of characters but it’s read-only. It might be in the program code area or some
other non-writable area. It depends on the implementation of your compiler. Therefore, the
second line tries to write a char ‘a’ to the read-only memory and the result is undefined,
for example, a segmentation error happens. Anyway, don’t write a program like this.
In conclusion, a string is an array of characters and it is placed in one of the following.
79
● Read-only memory. A string literal is read-only.
● Stack. If the class of an array is auto, then the array is placed in the stack. stack is
writable. If the array is defined in a function, its default class is auto. The stack area
will disappear when the function returns to the caller.
● Static area. If the class of an array is static, then the array is placed in the static area.
It keeps its value and remains for the life of the program. This area is writable.
● Heap. You can allocate and free memory for a string. For allocation, g_new or g_new0
is used. For freeing, g_free is used.
Copying string
There are two ways to copy a string. First way is just copying the pointer.
Plain Text
1 char *s = "Hello";
2 char *t = s;
Two pointers s and t points the same address. Therefore, you can’t modify t because t
points a string literal, which is read-only.
Second way is creating memory and copying the string to the memory.
Plain Text
1 char *s = "Hello";
2 char *t = g_strdup (s);
The function g_strdup allocates memory and initializes it with “Hello”, then returns the
pointer to the memory. The function g_strdup is almost same as the function string_dup
below.
80
Plain Text
1 #include <glib-object.h>
2 #include <string.h>
3
4 char *
5 string_dup (char *s) {
6 int length;
7 char *t;
8
9 if (s == NULL)
10 return NULL;
11 length = strlen (s) + 1;
12 t = g_new (char, length);
13 strcpy (t, s);
14 return t;
15 }
If g_strdup is used, the two pointers s and t point different memories. You can modify t
because it is placed in the memory allocated from the heap area.
It is important to know the difference between assigning pointers and duplicating strings.
const qualifier
The qualifier const makes a variable won’t change its value. It can also be applied to an
array. Then, the elements of the array won’t be changed.
Plain Text
1 const double pi = 3.1415926536;
2 const char a[] = "read only string";
An array parameter in a function can be qualified with const to indicate that the function
does not change the array. In the same way, the return value (a pointer to an array or
string) of a function can be qualified with const. The caller mustn’t modify or free the
returned array or string.
81
Plain Text
1 char *
2 string_dup (const char *s) {
3 ... ...
4 }
5
6 const char *
7 g_value_get_string (const GValue *value);
The qualifier const indicates who is the owner of the string when it is used in the function
of objects. “Owner” is an object or a caller of the function that has the right to modify or
free the string.
For example, g_value_get_string is given const GValue *value as an argument. The GValue
pointed by value is owned by the caller and the function doesn’t change or free it. The
function returns a string qualified with const. The returned string is owned by the object
and the caller mustn’t change or free the string. It is possible that the string will be
changed or freed by the object later.
Header file
The rest of this section is about TStr. TStr is a child of GObject and it holds a string. The
string is a pointer and an array of characters. The pointer points the array. The pointer can
be NULL. If it is NULL, TStr has no array. The memory of the array comes from the heap
area. TStr owns the memory and is responsible to free it when it becomes useless. TStr
has a string type property.
The header file tstr.h is as follows.
82
C
1 #pragma once
2
3 #include <glib-object.h>
4
5 #define T_TYPE_STR (t_str_get_type ())
6 G_DECLARE_DERIVABLE_TYPE (TStr, t_str, T, STR, GObject)
7
8 struct _TStrClass {
9 GObjectClass parent_class;
10 /* expect that descendants will override the setter */
11 void (*set_string) (TStr *self, const char *s);
12 };
13
14 TStr *
15 t_str_concat (TStr *self, TStr *other);
16
17 /* setter and getter */
18 void
19 t_str_set_string (TStr *self, const char *s);
20
21 char *
22 t_str_get_string (TStr *self);
23
24 /* create a new TStr instance */
25 TStr *
26 t_str_new_with_string (const char *s);
27
28 TStr *
29 t_str_new (void);
● 6: Uses G_DECLARE_DERIVABLE_TYPE. The TStr class is derivable and its child class
will be defined later.
● 8-12: TStrClass has one class method. It is set_string member of the TStrClass
structure. This will be overridden by the child class TNumStr. Therefore, Both TStr and
TNumStr has set_string member in their classes but they point different functions.
● 14-15: The public function t_str_concat connects two strings of TStr instances and
returns a new TStr instance.
● 18-22: Setter and getter.
● 25-29: Functions to create a TStr object.
83
C file
The C file tstr.c for TStr is as follows.
84
C
1 #include "tstr.h"
2
3 enum {
4 PROP_0,
5 PROP_STRING,
6 N_PROPERTIES
7 };
8
9 static GParamSpec *str_properties[N_PROPERTIES] = {NULL, };
10
11 typedef struct {
12 char *string;
13 } TStrPrivate;
14
15 G_DEFINE_TYPE_WITH_PRIVATE(TStr, t_str, G_TYPE_OBJECT)
16
17 static void
18 t_str_set_property (GObject *object, guint property_id, const GValue *val
ue, GParamSpec *pspec) {
19 TStr *self = T_STR (object);
20
21 /* The returned value of the function g_value_get_string can be NUL
L. */
22 /* The function t_str_set_string calls a class method, */
23 /* which is expected to rewrite in the descendant object. */
24 if (property_id == PROP_STRING)
25 t_str_set_string (self, g_value_get_string (value));
26 else
27 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
28 }
29
30 static void
31 t_str_get_property (GObject *object, guint property_id, GValue *value, GP
aramSpec *pspec) {
32 TStr *self = T_STR (object);
33 TStrPrivate *priv = t_str_get_instance_private (self);
34
35 /* The second argument of the function g_value_set_string can be NUL
L. */
36 if (property_id == PROP_STRING)
37 g_value_set_string (value, priv->string);
38 else
39 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
40 }
41
85
42 /* This function just set the string. */
43
/* So, no notify signal is emitted. */
44
static void
45
t_str_real_set_string (TStr *self, const char *s) {
46
TStrPrivate *priv = t_str_get_instance_private (self);
47
48
if (priv->string)
49
g_free (priv->string);
50
priv->string = g_strdup (s);
51
}
52
53
static void
54
t_str_finalize (GObject *object) {
55
TStr *self = T_STR (object);
56
TStrPrivate *priv = t_str_get_instance_private (self);
57
58
if (priv->string)
59
g_free (priv->string);
60
G_OBJECT_CLASS (t_str_parent_class)->finalize (object);
61
}
62
63
static void
64
t_str_init (TStr *self) {
65
TStrPrivate *priv = t_str_get_instance_private (self);
66
67
priv->string = NULL;
68
}
69
70
static void
71
t_str_class_init (TStrClass *class) {
72
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
73
74
gobject_class->finalize = t_str_finalize;
75
gobject_class->set_property = t_str_set_property;
76
gobject_class->get_property = t_str_get_property;
77
str_properties[PROP_STRING] = g_param_spec_string ("string", "str",
"string", "", G_PARAM_READWRITE);
78
g_object_class_install_properties (gobject_class, N_PROPERTIES, str_p
roperties);
79
80
class->set_string = t_str_real_set_string;
81
}
82
83
/* setter and getter */
84
void
85
t_str_set_string (TStr *self, const char *s) {
86
g_return_if_fail (T_IS_STR (self));
87
TStrClass *class = T_STR_GET_CLASS (self);
86
88
89
/* The setter calls the class method 'set_string', */
90
/* which is expected to be overridden by the descendant TNumStr. */
91
/* Therefore, the behavior of the setter is different between TStr an
d TNumStr. */
92
class->set_string (self, s);
93
}
94
95
char *
96
t_str_get_string (TStr *self) {
97
g_return_val_if_fail (T_IS_STR (self), NULL);
98
TStrPrivate *priv = t_str_get_instance_private (self);
99
100
return g_strdup (priv->string);
101
}
102
103
TStr *
104
t_str_concat (TStr *self, TStr *other) {
105
g_return_val_if_fail (T_IS_STR (self), NULL);
106
g_return_val_if_fail (T_IS_STR (other), NULL);
107
108
char *s1, *s2, *s3;
109
TStr *str;
110
111
s1 = t_str_get_string (self);
112
s2 = t_str_get_string (other);
113
if (s1 && s2)
114
s3 = g_strconcat (s1, s2, NULL);
115
else if (s1)
116
s3 = g_strdup (s1);
117
else if (s2)
118
s3 = g_strdup (s2);
119
else
120
s3 = NULL;
121
str = t_str_new_with_string (s3);
122
if (s1) g_free (s1);
123
if (s2) g_free (s2);
124
if (s3) g_free (s3);
125
return str;
126
}
127
128
/* create a new TStr instance */
129
TStr *
130
t_str_new_with_string (const char *s) {
131
return T_STR (g_object_new (T_TYPE_STR, "string", s, NULL));
132
}
133
134
TStr *
87
135 t_str_new (void) {
136
return T_STR (g_object_new (T_TYPE_STR, NULL));
137
}
● 3-9: enum defines a property id. The member PROP_STRING is the id of the “string”
property. Only one property is defined here, so it is possible to define it without enum.
However, enum can be applied to define two or more properties and it is more
common. The last member N_PROPERTIES is two because enum is zero-based. An
array str_properties has two elements since N_PROPERTIES is two. The first element
isn’t used and it is assigned with NULL. The second element will be assigned a pointer
to a GParamSpec instance in the class initialization function.
● 11-13: TStrPrivate is a C structure. It is a private data area for TStr. If TStr were a final
type, then no descendant would exist and TStr instance could be a private data area.
But TStr is derivable so you can’t store such private data in TStr instance that is open
to the descendants. The name of this structure is “<object name>Private” like
TStrPrivate. The structure must be defined before G_DEFINE_TYPE_WITH_PRIVATE.
● 15: G_DEFINE_TYPE_WITH_PRIVATE macro. It is similar to G_DEFINE_TYPE macro but
it adds the private data area for the derivable instance. This macro expands to:
○ Declaration of t_str_class_init which is a class initialization function.
○ Declaration of t_str_init which is an instance initialization function.
○ Definition of t_str_parent_class static variable. It points to the parent class of TStr.
○ The function call that adds private instance data to the type. It is a C structure and
its name is TStrPrivate.
○ Definition of t_str_get_type () function. This function registers the type in its first
call.
○ Definition of the private instance getter t_str_get_instance_private ().
● 17-28: The function t_str_set_property sets the “string” property and it is used by
g_object_set family functions. It uses t_str_set_string function to set the private data
with the copy of the string from the GValue. It is important because t_str_set_string
calls the class method set_string, which will be overridden by the child class.
Therefore, the behavior of t_str_set_property function is different between TStr and
TNumStr, which is a child of TStr. The function g_value_get_string returns the pointer
to the string that GValue owns. So you need to duplicate the string and it is done in
the function t_str_set_string.
● 30-40: The function t_str_get_property gets the “string” property and it is used by
88
g_object_get family functions. It just gives priv->string to the function
g_value_set_string. The variable priv points the private data area. The second argument
priv->string is owned by the TStr instance and the function g_value_set_string
duplicates it to store in the GValue structure.
● 44-51 The function t_str_real_set_string is the body of the class method and pointed by
set_string member in the class. First, it gets the pointer to the private area with
t_str_get_instance_private function. If the instance holds a string, free it before setting
it to a new string. It copies the given string and assigns it to priv->string. The
duplication is important. Thanks to that, the address of the string is hidden from the
out of the instance.
● 53-61: The finalize function t_str_finalize is called when TStr instance is destroyed.
The destruction process has two phases, “dispose” and “finalize”. In the disposal
phase, the instance releases instances. In the finalization phase, the instance does the
rest of all the finalization like freeing memories. This function frees the string priv-
>string if necessary. After that, it calls the parent’s finalize method. This is called
“chain up to its parent” and it will be explained later in this section.
● 63-68: The instance initialization function t_str_init assigns NULL to priv->string.
● 70-81: The class initialization function t_str_class_init overrides finalize, set_property
and get_property members. It creates the GParamSpec instance with
g_param_spec_string and installs the property with g_object_class_install_properties. It
assigns t_str_real_set_string to the member set_string. It is a class method and is
expected to be replaced in the child class.
● 84-101: Setter and getter. The setter method t_str_set_string just calls the class
method. So, it is expected to be replaced in the child class. It is used by property set
function t_str_set_property. So the behavior of property setting will change in the child
class. The getter method t_str_get_string just duplicates the string priv->string and
return the copy.
● 103-126: The public function t_str_concat concatenates the string of self and other,
and creates a new TStr.
● 129-137: Two functions t_str_new_with_string and t_str_new create a new TStr
instances.
90
Chaining up process
91
Child class extends parent’s function
The example in this section is TNumStr object. TNumStr is a child of TStr object. TNumStr
holds a string and the string type, which is one of t_int, t_double or t_none.
● t_int: the string expresses an integer
● t_double: the string expresses a double (floating point)
● t_none: the string doesn’t express the two numbers above.
A t_int or t_double type string is called a numeric string. It is not a common terminology
and it is used only in this tutorial. In short, a numeric string is a string that expresses a
number. For example, “0”, “-100” and “123.456” are numeric strings. They are strings and
express numbers.
● “0” is a string. It is a character array and its elements are ‘0’ and ‘\0’. It expresses 0,
which is an integer zero.
● “-100” is a string that consists of ‘-’, ‘1’, ‘0’, ‘0’ and ‘\0’. It expresses an integer -100.
● “123.456” consists of ‘1’, ‘2’, ‘3’, ‘.’, ‘4’, ‘5’, ‘6’ and ‘\0’. It expresses a real number
(double type) 123.456.
A numeric string is such a specific string.
We need to be careful that “0” and “0.0” are different. Because their type are different.
The type of “0” is integer and the type of “0.0” is double. In the same way, “1” is an
integer and “1.” is a double.
“.5” and “0.5” are the same. Both are double and their values are 0.5.
Verification of a numeric string is a kind of lexical analysis. A state diagram and state
matrix is often used for lexical analysis.
A numeric string is a sequence of characters that satisfies:
92
1. ‘+’ or ‘-’ can be the first character. It can be left out.
2. followed by a sequence of digits.
3. followed by a period.
4. followed by a sequence of digits.
The second part can be left out. For example, “.56” or “-.56” are correct.
The third and fourth parts can be left out. For example, “12” or “-23” are correct.
The fourth part can be left out. For example, “100.” is correct.
There are six states.
● 0 is the start point.
● 1 is the state after ‘+’ or ‘-’.
● 2 is at the middle of the first sequence of digits (integer part).
● 3 is the state after the decimal point.
● 4 is the end of the string and the string is int.
● 5 is the end of the string and the string is double.
● 6 is error. The string doesn’t express a number.
93
state diagram of a numeric string
The state matrix is:
state\input 0 1 2 3 4
0 1 2 3 6 6
1 6 2 3 6 6
2 6 2 3 4 6
3 6 3 6 5 6
This state diagram doesn’t support “1.23e5” style double (decimal floating point). If it is
supported, the state diagram will be more complicated. (However, it will be a good practice
for your programming skill.)
Header file
94
The header file of TNumStr is tnumstr.h. It is in the src/tstr directory.
Plain Text
1 #pragma once
2
3 #include <glib-object.h>
4 #include "tstr.h"
5 #include "../tnumber/tnumber.h"
6
7 #define T_TYPE_NUM_STR (t_num_str_get_type ())
8 G_DECLARE_FINAL_TYPE (TNumStr, t_num_str, T, NUM_STR, TStr)
9
10 /* type of the numeric string */
11 enum {
12 t_none,
13 t_int,
14 t_double
15 };
16
17 /* get the type of the TNumStr object */
18 int
19 t_num_str_get_string_type (TNumStr *self);
20
21 /* setter and getter */
22 void
23 t_num_str_set_from_t_number (TNumStr *self, TNumber *num);
24
25 // TNumStr can have any string, which is t_none, t_int or t_double type.
26 // If the type is t_none, t_num_str_get_t_number returns NULL.
27 // It is good idea to call t_num_str_get_string_type and check the type i
n advance.
28
29 TNumber *
30 t_num_str_get_t_number (TNumStr *self);
31
32 /* create a new TNumStr instance */
33 TNumStr *
34 t_num_str_new_with_tnumber (TNumber *num);
35
36 TNumStr *
37 t_num_str_new (void);
C file
The C file of TNumStr is tnumstr.c. It is in the src/tstr directory.
96
C
1 #include <stdlib.h>
2 #include <ctype.h>
3 #include "tnumstr.h"
4 #include "tstr.h"
5 #include "../tnumber/tnumber.h"
6 #include "../tnumber/tint.h"
7 #include "../tnumber/tdouble.h"
8
9 struct _TNumStr {
10 TStr parent;
11 int type;
12 };
13
14 G_DEFINE_TYPE(TNumStr, t_num_str, T_TYPE_STR)
15
16 static int
17 t_num_str_string_type (const char *string) {
18 const char *t;
19 int stat, input;
20 /* state matrix */
21 int m[4][5] = {
22 {1, 2, 3, 6, 6},
23 {6, 2, 3, 6, 6},
24 {6, 2, 3, 4, 6},
25 {6, 3, 6, 5, 6}
26 };
27
28 if (string == NULL)
29 return t_none;
30 stat = 0;
31 for (t = string; ; ++t) {
32 if (*t == '+' || *t == '-')
33 input = 0;
34 else if (isdigit (*t))
35 input = 1;
36 else if (*t == '.')
37 input = 2;
38 else if (*t == '\0')
39 input = 3;
40 else
41 input = 4;
42
43 stat = m[stat][input];
44
45 if (stat >= 4 || *t == '\0')
97
46 break;
47
}
48
if (stat == 4)
49
return t_int;
50
else if (stat == 5)
51
return t_double;
52
else
53
return t_none;
54
}
55
56
/* This function overrides t_str_set_string. */
57
/* And it also changes the behavior of setting the "string" property. */
58
/* On TStr => just set the "string" property */
59
/* On TNumStr => set the "string" property and set the type of the strin
g. */
60
static void
61
t_num_str_real_set_string (TStr *self, const char *s) {
62
T_STR_CLASS (t_num_str_parent_class)->set_string (self, s);
63
T_NUM_STR (self)->type = t_num_str_string_type(s);
64
}
65
66
static void
67
t_num_str_init (TNumStr *self) {
68
self->type = t_none;
69
}
70
71
static void
72
t_num_str_class_init (TNumStrClass *class) {
73
TStrClass *t_str_class = T_STR_CLASS (class);
74
75
t_str_class->set_string = t_num_str_real_set_string;
76
}
77
78
int
79
t_num_str_get_string_type (TNumStr *self) {
80
g_return_val_if_fail (T_IS_NUM_STR (self), -1);
81
82
return self->type;
83
}
84
85
/* setter and getter */
86
void
87
t_num_str_set_from_t_number (TNumStr *self, TNumber *num) {
88
g_return_if_fail (T_IS_NUM_STR (self));
89
g_return_if_fail (T_IS_NUMBER (num));
90
91
char *s;
92
98
93 s = t_number_to_s (T_NUMBER (num));
94
t_str_set_string (T_STR (self), s);
95
g_free (s);
96
}
97
98
TNumber *
99
t_num_str_get_t_number (TNumStr *self) {
100
g_return_val_if_fail (T_IS_NUM_STR (self), NULL);
101
102
char *s = t_str_get_string(T_STR (self));
103
TNumber *tnum;
104
105
if (self->type == t_int)
106
tnum = T_NUMBER (t_int_new_with_value (atoi (s)));
107
else if (self->type == t_double)
108
tnum = T_NUMBER (t_double_new_with_value (atof (s)));
109
else
110
tnum = NULL;
111
g_free (s);
112
return tnum;
113
}
114
115
/* create a new TNumStr instance */
116
117
TNumStr *
118
t_num_str_new_with_tnumber (TNumber *num) {
119
g_return_val_if_fail (T_IS_NUMBER (num), NULL);
120
121
TNumStr *numstr;
122
123
numstr = t_num_str_new ();
124
t_num_str_set_from_t_number (numstr, num);
125
return numstr;
126
}
127
128
TNumStr *
129
t_num_str_new (void) {
130
return T_NUM_STR (g_object_new (T_TYPE_NUM_STR, NULL));
131
}
● 9-12: TNumStr structure has its parent “TStr” and int type “type” members. So,
TNumStr instance holds a string, which is placed in the parent’s private area, and a
type.
● 14: G_DEFINE_TYPE macro.
● 16- 54: The function t_num_str_string_type checks the given string and returns t_int,
99
t_double or t_none. If the string is NULL or an non-numeric string, t_none will be
returned. The check algorithm is explained in the first subsection “Verification of a
numeric string”.
● 60-64: The function t_num_str_real_set_string sets TNumStr’s string and its type. This
is a body of the class method pointed by set_string member of the class structure. The
class method is initialized in the class initialization function t_num_str_class_init.
● 66-69: The instance initialization function t_num_str_init sets the type to t_none
because its parent initialization function set the pointer priv->string to NULL.
● 71-76: The class initialization function t_num_str_class_init assigns
t_num_str_real_set_string to the member set_string. Therefore, the function
t_str_set_string calls t_num_str_real_set_string, which sets not only the string but also
the type. The function g_object_set also calls it and sets both the string and type.
● 78-83: The public function t_num_str_get_string_type returns the type of the string.
● 86-113: Setter and getter. The setter sets the numeric string from a TNumber object.
And the getter returns a TNumber object.
● 117-131: These two functions create TNumStr instances.
When you want to set a string to a TNumStr instance, you can use t_str_set_string function.
Plain Text
1 TNumStr *ns = t_num_str_new ();
2 t_str_set_string (T_STR (ns), "123.456");
TNumStr extends the function t_str_set_string and it sets not only a string but also the
type for a TNumStr instance.
100
Plain Text
1 int t;
2 t = t_num_str_get_string_type (ns);
3 if (t == t_none) g_print ("t_none\n");
4 if (t == t_int) g_print ("t_int\n");
5 if (t == t_double) g_print ("t_double\n");
6 // => t_double appears on your display
A child class extends the parent class. So, a child is more specific and richer than the
parent.
101
Plain Text
1 $ cd src/tstr
2 $ meson setup _build
3 $ ninja -C _build
4 $ _build/test1
5 $ _build/test2
6 $ _build/tnumstr
7 String property is set to one.
8 "one" and "two" is "onetwo".
9 123 + 456 + 789 = 1368
10 TNumStr => TNumber => TNumStr
11 123 => 123 => 123
12 -45 => -45 => -45
13 +0 => 0 => 0
14 123.456 => 123.456000 => 123.456000
15 +123.456 => 123.456000 => 123.456000
16 -123.456 => -123.456000 => -123.456000
17 .456 => 0.456000 => 0.456000
18 123. => 123.000000 => 123.000000
19 0.0 => 0.000000 => 0.000000
20 123.4567890123456789 => 123.456789 => 123.456789
21 abc => (null) => abc
22 (null) => (null) => (null)
The last part of main.c is conversion between TNumStr and TNumber. There are some
difference between them because of the following two reasons.
● floating point is rounded. It is not an exact value when the value has long figures.
TNumStr “123.4567890123456789” is converted to TNumber 123.456789.
● There are two or more string expression in floating points. TNumStr “.456” is
converted to TNumber x, and x is converted to TNumStr “0.456000”.
It is difficult to compare two TNumStr instances. The best way is to compare TNumber
instances converted from TNumStr.
Interface
Interface is similar to abstract class. Interface defines virtual functions which are expected
to be overridden by a function in another instantiable object.
This section provides a simple example, TComparable. TComparable is an interface. It
defines functions to compare. They are:
102
● t_comparable_cmp (self, other): It compares self and other. The first argument self is
an instance on which t_comparable_cmp runs. The second argument other is another
instance. This function needs to be overridden in an object which implements the
interface.
○ If self is equal to other, t_comparable_cmp returns 0.
○ If self is greater than other, t_comparable_cmp returns 1.
○ If self is less than other, t_comparable_cmp returns -1.
○ If an error happens, t_comparable_cmp returns -2.
● t_comparable_eq (self, other): It returns TRUE if self is equal to other. Otherwise it
returns FALSE. You need to be careful that FALSE is returned even if an error occurs.
● t_comparable_gt (self, other): It returns TRUE if self is greater than other. Otherwise it
returns FALSE.
● t_comparable_lt (self, other): It returns TRUE if self is less than other. Otherwise it
returns FALSE.
● t_comparable_ge (self, other): It returns TRUE if self is greater than or equal to other.
Otherwise it returns FALSE.
● t_comparable_le (self, other): It returns TRUE if self is less than or equal to other.
Otherwise it returns FALSE.
Numbers and strings are comparable. TInt, TDouble and TStr implement TComparable
interface so that they can use the functions above. In addition, TNumStr can use the
functions because it is a child class of TStr.
For example,
Plain Text
1 TInt *i1 = t_int_new_with_value (10);
2 TInt *i2 = t_int_new_with_value (20);
3 t_comparable_eq (T_COMPARABLE (i1), T_COMPARABLE (i2)); /* => FALSE */
4 t_comparable_lt (T_COMPARABLE (i1), T_COMPARABLE (i2)); /* => TRUE */
What’s the difference between interface and abstract class? Virtual functions in an
abstract class are overridden by a function in its descendant classes. Virtual functions in
an interface are overridden by a function in any classes. Compare TNumber and
TComparable.
103
● A function t_number_add is overridden in TIntClass and TDoubleClass. It can’t be
overridden in TStrClass because TStr isn’t a descendant of TNumber.
● A function t_comparable_cmp is overridden in TIntClass, TDoubleClass and TStrClass.
TComparable interface
Defining interfaces is similar to defining objects.
● Use G_DECLARE_INTERFACE instead of G_DECLARE_FINAL_TYPE.
● Use G_DEFINE_INTERFACE instead of G_DEFINE_TYPE.
104
C
1 #pragma once
2
3 #include <glib-object.h>
4
5 #define T_TYPE_COMPARABLE (t_comparable_get_type ())
6 G_DECLARE_INTERFACE (TComparable, t_comparable, T, COMPARABLE, GObject)
7
8 struct _TComparableInterface {
9 GTypeInterface parent;
10 /* signal */
11 void (*arg_error) (TComparable *self);
12 /* virtual function */
13 int (*cmp) (TComparable *self, TComparable *other);
14 };
15
16 /* t_comparable_cmp */
17 /* if self > other, then returns 1 */
18 /* if self = other, then returns 0 */
19 /* if self < other, then returns -1 */
20 /* if error happens, then returns -2 */
21
22 int
23 t_comparable_cmp (TComparable *self, TComparable *other);
24
25 gboolean
26 t_comparable_eq (TComparable *self, TComparable *other);
27
28 gboolean
29 t_comparable_gt (TComparable *self, TComparable *other);
30
31 gboolean
32 t_comparable_lt (TComparable *self, TComparable *other);
33
34 gboolean
35 t_comparable_ge (TComparable *self, TComparable *other);
36
37 gboolean
38 t_comparable_le (TComparable *self, TComparable *other);
106
C
1 #include "tcomparable.h"
2
3 static guint t_comparable_signal;
4
5 G_DEFINE_INTERFACE (TComparable, t_comparable, G_TYPE_OBJECT)
6
7 static void
8 arg_error_default_cb (TComparable *self) {
9 g_printerr ("\nTComparable: argument error.\n");
10 }
11
12 static void
13 t_comparable_default_init (TComparableInterface *iface) {
14 /* virtual function */
15 iface->cmp = NULL;
16 /* argument error signal */
17 iface->arg_error = arg_error_default_cb;
18 t_comparable_signal =
19 g_signal_new ("arg-error",
20 T_TYPE_COMPARABLE,
21 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOK
S,
22 G_STRUCT_OFFSET (TComparableInterface, arg_error),
23 NULL /* accumulator */,
24 NULL /* accumulator data */,
25 NULL /* C marshaller */,
26 G_TYPE_NONE /* return_type */,
27 0 /* n_params */
28 );
29 }
30
31 int
32 t_comparable_cmp (TComparable *self, TComparable *other) {
33 g_return_val_if_fail (T_IS_COMPARABLE (self), -2);
34
35 TComparableInterface *iface = T_COMPARABLE_GET_IFACE (self);
36
37 return (iface->cmp == NULL ? -2 : iface->cmp (self, other));
38 }
39
40 gboolean
41 t_comparable_eq (TComparable *self, TComparable *other) {
42 return (t_comparable_cmp (self, other) == 0);
43 }
44
107
45 gboolean
46
t_comparable_gt (TComparable *self, TComparable *other) {
47
return (t_comparable_cmp (self, other) == 1);
48
}
49
50
gboolean
51
t_comparable_lt (TComparable *self, TComparable *other) {
52
return (t_comparable_cmp (self, other) == -1);
53
}
54
55
gboolean
56
t_comparable_ge (TComparable *self, TComparable *other) {
57
int result = t_comparable_cmp (self, other);
58
return (result == 1 || result == 0);
59
}
60
61
gboolean
62
t_comparable_le (TComparable *self, TComparable *other) {
63
int result = t_comparable_cmp (self, other);
64
return (result == -1 || result == 0);
65
}
108
t_comparable_cmp. And it returns TRUE if t_comparable_cmp returns zero. Otherwise
it returns FALSE.
This program uses a signal to give the argument type error information to a user. This
error is usually a program error rather than a run-time error. Using a signal to report a
program error is not a good way. The best way is using g_return_if_fail. The reason why I
made this signal is just I wanted to show how to implement a signal in interfaces.
Implementing interface
TInt, TDouble and TStr implement TComparable. First, look at TInt. The header file is the
same as before. The implementation is written in C file.
tint.c is as follows.
109
C
1 #include "../tnumber/tnumber.h"
2 #include "../tnumber/tint.h"
3 #include "../tnumber/tdouble.h"
4 #include "tcomparable.h"
5
6 enum {
7 PROP_0,
8 PROP_INT,
9 N_PROPERTIES
10 };
11
12 static GParamSpec *int_properties[N_PROPERTIES] = {NULL, };
13
14 struct _TInt {
15 TNumber parent;
16 int value;
17 };
18
19 static void t_comparable_interface_init (TComparableInterface *iface);
20
21 G_DEFINE_TYPE_WITH_CODE (TInt, t_int, T_TYPE_NUMBER,
22 G_IMPLEMENT_INTERFACE (T_TYPE_COMPARABLE, t_comp
arable_interface_init))
23
24 static int
25 t_int_comparable_cmp (TComparable *self, TComparable *other) {
26 if (! T_IS_NUMBER (other)) {
27 g_signal_emit_by_name (self, "arg-error");
28 return -2;
29 }
30
31 int i;
32 double s, o;
33
34 s = (double) T_INT (self)->value;
35 if (T_IS_INT (other)) {
36 g_object_get (other, "value", &i, NULL);
37 o = (double) i;
38 } else
39 g_object_get (other, "value", &o, NULL);
40 if (s > o)
41 return 1;
42 else if (s == o)
43 return 0;
44 else if (s < o)
110
45 return -1;
46
else /* This can't happen. */
47
return -2;
48
}
49
50
static void
51
t_comparable_interface_init (TComparableInterface *iface) {
52
iface->cmp = t_int_comparable_cmp;
53
}
54
55
static void
56
t_int_set_property (GObject *object, guint property_id, const GValue *val
ue, GParamSpec *pspec) {
57
TInt *self = T_INT (object);
58
59
if (property_id == PROP_INT)
60
self->value = g_value_get_int (value);
61
else
62
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
63
}
64
65
static void
66
t_int_get_property (GObject *object, guint property_id, GValue *value, GP
aramSpec *pspec) {
67
TInt *self = T_INT (object);
68
69
if (property_id == PROP_INT)
70
g_value_set_int (value, self->value);
71
else
72
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
73
}
74
75
static void
76
t_int_init (TInt *self) {
77
}
78
79
/* arithmetic operator */
80
/* These operators create a new instance and return a pointer to it. */
81
#define t_int_binary_op(op) \
82
int i; \
83
double d; \
84
if (T_IS_INT (other)) { \
85
g_object_get (T_INT (other), "value", &i, NULL); \
86
return T_NUMBER (t_int_new_with_value (T_INT(self)->value op i)); \
87
} else { \
88
g_object_get (T_DOUBLE (other), "value", &d, NULL); \
89
return T_NUMBER (t_int_new_with_value (T_INT(self)->value op (int)
d)); \
111
90 }
91
92
static TNumber *
93
t_int_add (TNumber *self, TNumber *other) {
94
g_return_val_if_fail (T_IS_INT (self), NULL);
95
96
t_int_binary_op (+)
97
}
98
99
static TNumber *
100
t_int_sub (TNumber *self, TNumber *other) {
101
g_return_val_if_fail (T_IS_INT (self), NULL);
102
103
t_int_binary_op (-)
104
}
105
106
static TNumber *
107
t_int_mul (TNumber *self, TNumber *other) {
108
g_return_val_if_fail (T_IS_INT (self), NULL);
109
110
t_int_binary_op (*)
111
}
112
113
static TNumber *
114
t_int_div (TNumber *self, TNumber *other) {
115
g_return_val_if_fail (T_IS_INT (self), NULL);
116
117
int i;
118
double d;
119
120
if (T_IS_INT (other)) {
121
g_object_get (T_INT (other), "value", &i, NULL);
122
if (i == 0) {
123
g_signal_emit_by_name (self, "div-by-zero");
124
return NULL;
125
} else
126
return T_NUMBER (t_int_new_with_value (T_INT(self)->value /
i));
127
} else {
128
g_object_get (T_DOUBLE (other), "value", &d, NULL);
129
if (d == 0) {
130
g_signal_emit_by_name (self, "div-by-zero");
131
return NULL;
132
} else
133
return T_NUMBER (t_int_new_with_value (T_INT(self)->value /
(int) d));
134
}
135
}
112
136
137
static TNumber *
138
t_int_uminus (TNumber *self) {
139
g_return_val_if_fail (T_IS_INT (self), NULL);
140
141
return T_NUMBER (t_int_new_with_value (- T_INT(self)->value));
142
}
143
144
static char *
145
t_int_to_s (TNumber *self) {
146
g_return_val_if_fail (T_IS_INT (self), NULL);
147
148
int i;
149
150
g_object_get (T_INT (self), "value", &i, NULL);
151
return g_strdup_printf ("%d", i);
152
}
153
154
static void
155
t_int_class_init (TIntClass *class) {
156
TNumberClass *tnumber_class = T_NUMBER_CLASS (class);
157
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
158
159
/* override virtual functions */
160
tnumber_class->add = t_int_add;
161
tnumber_class->sub = t_int_sub;
162
tnumber_class->mul = t_int_mul;
163
tnumber_class->div = t_int_div;
164
tnumber_class->uminus = t_int_uminus;
165
tnumber_class->to_s = t_int_to_s;
166
167
gobject_class->set_property = t_int_set_property;
168
gobject_class->get_property = t_int_get_property;
169
int_properties[PROP_INT] = g_param_spec_int ("value", "val", "Intege
r value", G_MININT, G_MAXINT, 0, G_PARAM_READWRITE);
170
g_object_class_install_properties (gobject_class, N_PROPERTIES, int_p
roperties);
171
}
172
173
TInt *
174
t_int_new_with_value (int value) {
175
TInt *i;
176
177
i = g_object_new (T_TYPE_INT, "value", value, NULL);
178
return i;
179
}
180
181
TInt *
113
182 t_int_new (void) {
183
TInt *i;
184
185
i = g_object_new (T_TYPE_INT, NULL);
186
return i;
187
}
115
C
1 #include "../tstr/tstr.h"
2 #include "tcomparable.h"
3
4 enum {
5 PROP_0,
6 PROP_STRING,
7 N_PROPERTIES
8 };
9
10 static GParamSpec *str_properties[N_PROPERTIES] = {NULL, };
11
12 typedef struct {
13 char *string;
14 } TStrPrivate;
15
16 static void t_comparable_interface_init (TComparableInterface *iface);
17
18 G_DEFINE_TYPE_WITH_CODE (TStr, t_str, G_TYPE_OBJECT,
19 G_ADD_PRIVATE (TStr)
20 G_IMPLEMENT_INTERFACE (T_TYPE_COMPARABLE, t_comp
arable_interface_init))
21
22 static void
23 t_str_set_property (GObject *object, guint property_id, const GValue *val
ue, GParamSpec *pspec) {
24 TStr *self = T_STR (object);
25
26 /* The returned value of the function g_value_get_string can be NUL
L. */
27 /* The function t_str_set_string calls a class method, */
28 /* which is expected to rewrite in the descendant object. */
29 if (property_id == PROP_STRING)
30 t_str_set_string (self, g_value_get_string (value));
31 else
32 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
33 }
34
35 static void
36 t_str_get_property (GObject *object, guint property_id, GValue *value, GP
aramSpec *pspec) {
37 TStr *self = T_STR (object);
38 TStrPrivate *priv = t_str_get_instance_private (self);
39
40 /* The second argument of the function g_value_set_string can be NUL
L. */
116
41 if (property_id == PROP_STRING)
42
g_value_set_string (value, priv->string);
43
else
44
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
45
}
46
47
/* This function just set the string. */
48
/* So, no notify signal is emitted. */
49
static void
50
t_str_real_set_string (TStr *self, const char *s) {
51
TStrPrivate *priv = t_str_get_instance_private (self);
52
53
if (priv->string)
54
g_free (priv->string);
55
priv->string = g_strdup (s);
56
}
57
58
static void
59
t_str_finalize (GObject *object) {
60
TStr *self = T_STR (object);
61
TStrPrivate *priv = t_str_get_instance_private (self);
62
63
if (priv->string)
64
g_free (priv->string);
65
G_OBJECT_CLASS (t_str_parent_class)->finalize (object);
66
}
67
68
static int
69
t_str_comparable_cmp (TComparable *self, TComparable *other) {
70
if (! T_IS_STR (other)) {
71
g_signal_emit_by_name (self, "arg-error");
72
return -2;
73
}
74
75
char *s, *o;
76
int result;
77
78
s = t_str_get_string (T_STR (self));
79
o = t_str_get_string (T_STR (other));
80
81
if (strcmp (s, o) > 0)
82
result = 1;
83
else if (strcmp (s, o) == 0)
84
result = 0;
85
else if (strcmp (s, o) < 0)
86
result = -1;
87
else /* This can't happen. */
88
result = -2;
117
89 g_free (s);
90
g_free (o);
91
return result;
92
}
93
94
static void
95
t_comparable_interface_init (TComparableInterface *iface) {
96
iface->cmp = t_str_comparable_cmp;
97
}
98
99
static void
100
t_str_init (TStr *self) {
101
TStrPrivate *priv = t_str_get_instance_private (self);
102
103
priv->string = NULL;
104
}
105
106
static void
107
t_str_class_init (TStrClass *class) {
108
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
109
110
gobject_class->finalize = t_str_finalize;
111
gobject_class->set_property = t_str_set_property;
112
gobject_class->get_property = t_str_get_property;
113
str_properties[PROP_STRING] = g_param_spec_string ("string", "str",
"string", "", G_PARAM_READWRITE);
114
g_object_class_install_properties (gobject_class, N_PROPERTIES, str_p
roperties);
115
116
class->set_string = t_str_real_set_string;
117
}
118
119
/* setter and getter */
120
void
121
t_str_set_string (TStr *self, const char *s) {
122
g_return_if_fail (T_IS_STR (self));
123
TStrClass *class = T_STR_GET_CLASS (self);
124
125
/* The setter calls the class method 'set_string', */
126
/* which is expected to be overridden by the descendant TNumStr. */
127
/* Therefore, the behavior of the setter is different between TStr an
d TNumStr. */
128
class->set_string (self, s);
129
}
130
131
char *
132
t_str_get_string (TStr *self) {
133
g_return_val_if_fail (T_IS_STR (self), NULL);
118
134 TStrPrivate *priv = t_str_get_instance_private (self);
135
136
return g_strdup (priv->string);
137
}
138
139
TStr *
140
t_str_concat (TStr *self, TStr *other) {
141
g_return_val_if_fail (T_IS_STR (self), NULL);
142
g_return_val_if_fail (T_IS_STR (other), NULL);
143
144
char *s1, *s2, *s3;
145
TStr *str;
146
147
s1 = t_str_get_string (self);
148
s2 = t_str_get_string (other);
149
if (s1 && s2)
150
s3 = g_strconcat (s1, s2, NULL);
151
else if (s1)
152
s3 = g_strdup (s1);
153
else if (s2)
154
s3 = g_strdup (s2);
155
else
156
s3 = NULL;
157
str = t_str_new_with_string (s3);
158
if (s1) g_free (s1);
159
if (s2) g_free (s2);
160
if (s3) g_free (s3);
161
return str;
162
}
163
164
/* create a new TStr instance */
165
TStr *
166
t_str_new_with_string (const char *s) {
167
return T_STR (g_object_new (T_TYPE_STR, "string", s, NULL));
168
}
169
170
TStr *
171
t_str_new (void) {
172
return T_STR (g_object_new (T_TYPE_STR, NULL));
173
}
Test program.
main.c is a test program.
120
C
1 #include <glib-object.h>
2 #include "tcomparable.h"
3 #include "../tnumber/tnumber.h"
4 #include "../tnumber/tint.h"
5 #include "../tnumber/tdouble.h"
6 #include "../tstr/tstr.h"
7
8 static void
9 t_print (const char *cmp, TComparable *c1, TComparable *c2) {
10 char *s1, *s2;
11 TStr *ts1, *ts2, *ts3;
12
13 ts1 = t_str_new_with_string("\"");
14 if (T_IS_NUMBER (c1))
15 s1 = t_number_to_s (T_NUMBER (c1));
16 else if (T_IS_STR (c1)) {
17 ts2 = t_str_concat (ts1, T_STR (c1));
18 ts3 = t_str_concat (ts2, ts1);
19 s1 = t_str_get_string (T_STR (ts3));
20 g_object_unref (ts2);
21 g_object_unref (ts3);
22 } else {
23 g_print ("c1 isn't TInt, TDouble nor TStr.\n");
24 return;
25 }
26 if (T_IS_NUMBER (c2))
27 s2 = t_number_to_s (T_NUMBER (c2));
28 else if (T_IS_STR (c2)) {
29 ts2 = t_str_concat (ts1, T_STR (c2));
30 ts3 = t_str_concat (ts2, ts1);
31 s2 = t_str_get_string (T_STR (ts3));
32 g_object_unref (ts2);
33 g_object_unref (ts3);
34 } else {
35 g_print ("c2 isn't TInt, TDouble nor TStr.\n");
36 return;
37 }
38 g_print ("%s %s %s.\n", s1, cmp, s2);
39 g_object_unref (ts1);
40 g_free (s1);
41 g_free (s2);
42 }
43
44 static void
45 compare (TComparable *c1, TComparable *c2) {
121
46 if (t_comparable_eq (c1, c2))
47
t_print ("equals", c1, c2);
48
else if (t_comparable_gt (c1, c2))
49
t_print ("is greater than", c1, c2);
50
else if (t_comparable_lt (c1, c2))
51
t_print ("is less than", c1, c2);
52
else if (t_comparable_ge (c1, c2))
53
t_print ("is greater than or equal to", c1, c2);
54
else if (t_comparable_le (c1, c2))
55
t_print ("is less than or equal to", c1, c2);
56
else
57
t_print ("can't compare to", c1, c2);
58
}
59
60
int
61
main (int argc, char **argv) {
62
const char *one = "one";
63
const char *two = "two";
64
const char *three = "three";
65
TInt *i;
66
TDouble *d;
67
TStr *str1, *str2, *str3;
68
69
i = t_int_new_with_value (124);
70
d = t_double_new_with_value (123.45);
71
str1 = t_str_new_with_string (one);
72
str2 = t_str_new_with_string (two);
73
str3 = t_str_new_with_string (three);
74
75
compare (T_COMPARABLE (i), T_COMPARABLE (d));
76
compare (T_COMPARABLE (str1), T_COMPARABLE (str2));
77
compare (T_COMPARABLE (str2), T_COMPARABLE (str3));
78
compare (T_COMPARABLE (i), T_COMPARABLE (str1));
79
80
g_object_unref (i);
81
g_object_unref (d);
82
g_object_unref (str1);
83
g_object_unref (str2);
84
g_object_unref (str3);
85
86
return 0;
87
}
● 8-42: The function t_print has three parameters and builds an output string, then
shows it to the display. When it builds the output, strings are surrounded with double
quotes.
122
● 44-58: The function compare compares two TComparable objects and calls t_print to
display the result.
● 60-87: main function.
● 69-73: Creates TInt, TDouble and three TStr instances. They are given values.
● 75: Compares TInt and TDouble.
● 76-77: Compares two TStr.
● 78: Compare TInt to TStr. This makes an “arg-error”.
● 80-84 Frees objects.
They don’t use macros. Instead, they register the interface or implementation of the
interface to the type system directly. If you want to know that, see the source files in
src/tcomparable.
124