Up: Readme.md, Prev: Section 5, Next: Section 7
There are two kinds of types, final type and derivable type. Final type doesn't have any child object. Derivable type has child objects.
The main difference between the two objects is their classes. Final type objects doesn't have its own class area. The only member of the class is its parent class.
Derivable object has its own area in the class. The class is open to its descendants.
G_DECLARE_DERIVABLE_TYPE is used to declare derivable type.
It is written in a header file like this:
#define T_TYPE_NUMBER (t_number_get_type ())
G_DECLARE_DERIVABLE_TYPE (TNumber, t_number, T, NUMBER, GObject)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.h is a header file for the TNumber class.
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);- 6:
G_DECLARE_DERIVABLE_TYPEmacro. This is similar toG_DECLARE_FINAL_TYPEmacro. The difference is derivable or final.G_DECLARE_DERIVABLE_TYPEis expanded to:- Declaration of
t_number_get_type ()function. This function must be defined intnumber.cfile. The definition is usually done withG_DEFINE_TYPEor 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) andT_NUMBER_GET_CLASSare defined. g_autoptr()support.
- Declaration of
- 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_sfunction.to_sfunction 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_newas an argument. - 22-38: Functions. They are public.
tnumber.c is as follows.
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_HOOKS,
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);
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) {
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 }- 5:
G_DEFINE_ABSTRACT_TYPEmacro. This macro is used to define an abstract type object. Abstract type isn't instantiated. This macro is expanded to:- Declaration of
t_number_init ()function. - Declaration of
t_number_class_init ()function. - Definition of
t_number_get_type ()function. - Definition of
t_number_parent_classstatic variable that points the parent class.
- Declaration of
- 3, 7-10, 26-35: Defines division-by-zero signal.
The function
div_by_zero_default_cbis a default handler of "div-by-zero" signal. Default handler doesn't have user data parameter. The functiong_signal_newis used instead ofg_signal_new_class_handler. It specifies a handler as the offset from the top of the class to the pointer to the handler. - 12-36: The class initialization function
t_number_class_init. - 16-21: These class methods are virtual functions. They are expected to be overridden in the descendant object of TNumber. NULL is assigned here so that nothing happens when the methods are called.
- 23: Assigns the address of the function
dev_by_zero_default_cbtoclass->div_by_zero. This is the default handler of "div-by-zero" signal. - 38-40:
t_number_initis a initialization function for an instance. But abstract object isn't instantiated. So, nothing is done in this function. But you can't leave out the definition of this function. - 42-98: Public functions. These functions just call the corresponding class methods if the pointer to the class method is not NULL.
tint.h is a header file of the TInt class.
TInt is a child class of TNumber.
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_sare 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.
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 *value, 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, GParamSpec *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; \
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) {
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_MININT, G_MAXINT, 0, G_PARAM_READWRITE);
130 g_object_class_install_property (gobject_class, PROP_INT, int_property);
131 }
132
133 TInt *
134 t_int_new_with_value (int value) {
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_TYPEmacro. This macro expands to:- Declaration of
t_int_init ()function. - Definition of
t_int_get_type ()function. - Definition of
t_int_parent_classstatic variable which points the parent class.
- Declaration of
- 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_subandt_int_mul. This macro is similar tot_int_divfunction. Refer to the explanation below fort_int_div. - 52-71: The functions
t_int_add,t_int_subandt_int_mul. The macrot_int_binary_opis used. - 73-95: The function
t_int_div. The first argumentselfis the object on which the function is called. The second argumentotheris 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 withg_signal_emit_by_nameinstead ofg_signal_emit. The return value oft_int_divis 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 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_addis called on a TInt object, then the function calls the class method*tnumber_class->add. The pointer pointst_int_addfunction. Therefore,t_int_addis finally called. - 133-147: Instance creation functions are the same as before.
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
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
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)) { \
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)->value / (double) i));
86 } else {
87 g_object_get (T_DOUBLE (other), "value", &d, NULL);
88 if (d == 0) {
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)->value / 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_property);
130 }
131
132 TDouble *
133 t_double_new_with_value (double value) {
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 is a simple program to test the objects.
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
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) andd(TDouble). - 33-34: Set "value" properties on
iandd. - 36: Add
dtoi. The answer is TInt object. - 47: Add
itod. 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.
The source files are located under src/tnumber.
The file meson.buld, which controls the compilation process, is as follows.
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)
8 Compilation and execution is done by the usual way.
$ cd src/tnumber
$ meson setup _build
$ ninja -C _build
$ _build/tnumber
Then the following is shown on the display.
Property "value" is set to 100.
Property "value" is set to 12.345000.
100 + 12.345000 is 112.
12.345000 + 100 is 112.345000.
Property "value" is set to 0.000000.
Error: division by zero.
The two answers are different because of the different types.
This section has shown a simple example of derivable and abstract class.
You can define your derivable object like this.
If your object isn't abstract, use G_DEFINE_TYPE instead of G_DEFINE_ABSTRACT_TYPE.
And you need one more thing, how to manage private data in your derivable object.
There is a tutorial in GObject API Reference.
See the tutorial for learning derivable object.
It is also good to see source files in GTK.
Because TNumber is an abstract object, you cannot instantiate it directly.
And you cannot create the TNumber class as well.
But when you create its descendant instance, TNumber class is made and initialized.
First call for g_object_new (T_TYPE_INT, ...) or g_object_new (T_TYPE_DOUBLE, ...) creates and initializes TNumberClass if the class doesn't exist.
After that, TIntClass or TDoubleClass is created and followed by the creation for TInt or TDouble instance respectively.
And the initialization process for the TNumber class is as follows.
- GObjectClass has been initialized before the function
mainstarts. - Memory is allocated for TNumberClass.
- The parent (GObjectClass) part of the class is copied from GObjectClass.
- The class initialization function
t_number_class_initis called. It initializes class methods (Pointers to the class methods) and a default signal handler.
The diagram below shows the process.
- TNumberClass has been initialized before the initialization of TIntClass starts.
- First call for
g_object_new (T_TYPE_INT, ...)initializes TIntClass. And the initialization process is as follows. - Memory is allocated for TIntClass. TIntClass doesn't have its own area. Therefore its structure is the same as its parent class (TNumberClass).
- The parent (TNumberClass) part of the class (This is the same as whole TIntClass) is copied from TNumberClass.
- The class initialization function
t_int_class_initis called. It overrides class methods from TNumber,set_propertyandget_property.
The diagram below shows the process.

