Up: Readme.md, Prev: Section 8
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:
t_comparable_cmp (self, other)
: It comparesself
andother
. The first argumentself
is an instance on whicht_comparable_cmp
runs. The second argumentother
is another instance. This function needs to be overridden in an object which implements the interface.- If
self
is equal toother
,t_comparable_cmp
returns 0. - If
self
is greater thanother
,t_comparable_cmp
returns 1. - If
self
is less thanother
,t_comparable_cmp
returns -1. - If an error happens,
t_comparable_cmp
returns -2.
- If
t_comparable_eq (self, other)
: It returns TRUE ifself
is equal toother
. 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 ifself
is greater thanother
. Otherwise it returns FALSE.t_comparable_lt (self, other)
: It returns TRUE ifself
is less thanother
. Otherwise it returns FALSE.t_comparable_ge (self, other)
: It returns TRUE ifself
is greater than or equal toother
. Otherwise it returns FALSE.t_comparable_le (self, other)
: It returns TRUE ifself
is less than or equal toother
. 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,
TInt *i1 = t_int_new_with_value (10);
TInt *i2 = t_int_new_with_value (20);
t_comparable_eq (T_COMPARABLE (i1), T_COMPARABLE (i2)); /* => FALSE */
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.
- 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.
Defining interfaces is similar to defining objects.
- Use
G_DECLARE_INTERFACE
instead ofG_DECLARE_FINAL_TYPE
. - Use
G_DEFINE_INTERFACE
instead ofG_DEFINE_TYPE
.
Now let's see the header file.
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);
- 6:
G_DECLARE_INTERFACE
macro. The last parameter is a prerequisite of the interface. The prerequisite of TComparable is GObject. So, any other object than the descendants of GObject, for example GVariant, can't implement TComparable. A prerequisite is the GType of either an interface or a class. This macro expands to:- Declaration of
t_comparable_get_type()
. Typedef struct _TComparableInterface TComparableInterface
T_COMPARABLE ()
macro. It casts an instance to TComparable type.T_IS_COMPARABLE ()
macro. It checks if the type of an instance isT_TYPE_COMPARABLE
.T_COMPARABLE_GET_IFACE ()
macro. It gets the interface of the instance which is given as an argument.
- Declaration of
- 8-14:
TComparableInterface
structure. This is similar to a class structure. The first member is the parent interface. The parent ofTComparableInterface
isGTypeInterface
.GTypeInterface
is a base of all interface types. It is like aGTypeClass
which is a base of all class types.GTypeClass
is the first member of the structureGObjectClass
. (Seegobject.h
. Note thatGObjectClass
is the same asstruct _GObjectClass
.) The next member is a pointerarg_error
to the default signal handler of "arg-error" signal. This signal is emitted when the second argument of the comparison function is inappropriate. For example, ifself
is TInt andother
is TStr, both of them are Comparable instance. But they are not able to compare. This is becauseother
isn't TNumber. The last membercmp
is a pointer to a comparison method. It is a virtual function and is expected to be overridden by a function in the object which implements the interface. - 22-38: Public functions.
C file tcomparable.c
is as follows.
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_HOOKS,
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
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 }
- 5:
G_DEFINE_INTERFACE
macro. The third parameter is the type of the prerequisite. This macro expands to:- Declaration of
t_comparable_default_init ()
. - Definition of
t_comparable_get_type ()
.
- Declaration of
- 7-10:
arg_error_default_cb
is a default signal handler of "arg-error" signal. - 12- 29:
t_comparable_default_init
function. This function is similar to class initialization function. It initializesTComparableInterface
structure. - 15: Assigns NULL to
cmp
. So, the comparison method doesn't work before an implementation class overrides it. - 17: Set the default signal handler of the signal "arg-error".
- 18-28: Creates a signal "arg-error".
- 31-38: The function
t_comparable_cmp
. It checks the type ofself
on the first line. If it isn't comparable, it logs the error message and returns -2 (error). Ififace->cmp
is NULL (it means the class method hasn't been overridden), then it returns -2. Otherwise it calls the class method and returns the value returned by the class method. - 40-65: Public functions.
These five functions are based on
t_comparable_cmp
. Therefore, no overriding is necessary for them. For example,t_comparable_eq
just callst_comparable_cmp
. And it returns TRUE ift_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.
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.
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_comparable_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)
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 *value, 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, GParamSpec *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)); \
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 }
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", "Integer value", G_MININT, G_MAXINT, 0, G_PARAM_READWRITE);
170 g_object_class_install_properties (gobject_class, N_PROPERTIES, int_properties);
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 *
182 t_int_new (void) {
183 TInt *i;
184
185 i = g_object_new (T_TYPE_INT, NULL);
186 return i;
187 }
- 4: It needs to include the header file of TComparable.
- 19: Declaration of
t_comparable_interface_init ()
function. This declaration must be done beforeG_DEFINE_TYPE_WITH_CODE
macro. - 21-22:
G_DEFINE_TYPE_WITH_CODE
macro. The last parameter isG_IMPLEMENT_INTERFACE
macro. The second parameter ofG_IMPLEMENT_INTERFACE
ist_comparable_interface_init
. These two macros expands to:- Declaration of
t_int_class_init ()
. - Declaration of
t_int_init ()
. - Definition of
t_int_parent_class
static variable which points to the parent's class. - Definition of
t_int_get_type ()
. This function includesg_type_register_static_simple ()
andg_type_add_interface_static ()
. The functiong_type_register_static_simple ()
is a convenient version ofg_type_register_static ()
. It registers TInt type to the type system. The functiong_type_add_interface_static ()
adds an interface type to an instance type. There is a good example in GObject Reference Manual, Interfaces. If you want to know how to write codes without the macros, seetint_without_macro.c
.
- Declaration of
- 24-48:
t_int_comparable_cmp
is a function to compare TInt instance to TNumber instance. - 26-29: Checks the type of
other
. If the argument type is not TNumber, it emits "arg-error" signal withg_signal_emit_by_name
. - 34: Converts
self
into double. - 35-39: Gets the value of
other
and if it is TInt then the value is casted to double. - 40-47: compares
s
ando
and returns 1, 0, -1 and -2. - 50-53:
t_comparable_interface_init
. This function is called in the initialization process of TInt. The functiont_int_comparable_cmp
is assigned toiface->cmp
.
tdouble.c
is almost same as tint.c
.
These two objects can be compared because int is casted to double before the comparison.
tstr.c
is as follows.
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_comparable_interface_init))
21
22 static void
23 t_str_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) {
24 TStr *self = T_STR (object);
25
26 /* The returned value of the function g_value_get_string can be NULL. */
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, GParamSpec *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 NULL. */
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;
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_properties);
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 and 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);
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 }
- 16: Declares
t_comparable_interface_init
function. It needs to be declared beforeG_DEFINE_TYPE_WITH_CODE
macro. - 18-20:
G_DEFINE_TYPE_WITH_CODE
macro. Because TStr is derivable type, its private area (TStrPrivate) is needed.G_ADD_PRIVATE
macro makes the private area. Be careful that there's no comma afterG_ADD_PRIVATE
macro. - 68-92:
t_str_comparable_cmp
. - 70-73: Checks the type of
other
. If it is not TStr, "arg-error" signal is emitted. - 78-79: Gets strings
s
ando
from TStr objectsself
andother
. - 81-88: Compares
s
ando
with the C standard functionstrcmp
. - 89-90: Frees
s
ando
. - 91: Returns the result.
- 94-97:
t_comparable_interface_init
function. It overridesiface->comp
witht_str_comparable_cmp
.
TStr can be compared with TStr, but not with TInt nor TDouble. Generally, comparison is available between two same type instances.
TNumStr itself doesn't implement TComparable. But it is a child of TStr, so it is comparable. The comparison is based on the alphabetical order. So, "a" is bigger than "b" and "three" is bigger than "two".
main.c
is a test program.
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) {
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. - 44-58: The function
compare
compares two TComparable objects and callst_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.
Change your current directory to src/tcomparable.
$ cd src/tcomparable
$ meson setup _build
$ ninja -C _build
Then execute it.
$ _build/tcomparable
124 is greater than 123.450000.
"one" is less than "two".
"two" is greater than "three".
TComparable: argument error.
TComparable: argument error.
TComparable: argument error.
TComparable: argument error.
TComparable: argument error.
124 can't compare to "one".
We used macros such as G_DECLARE_INTERFACE
, G_DEFINE_INTERFACE
to build an interface.
And G_DEFINE_TYPE_WITH_CODE
to implement the interface.
We can also build it without macros.
There are three files in the tcomparable
directory.
tcomparable_without_macro.h
tcomparable_without_macro.c
tint_without_macro.c
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.
If you know any object oriented languages, you probably have thought that GObject and the languages are similar. Learning such languages is very useful to know GObject system.