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)comparesselfandother.selfis an instance on whicht_comparable_cmpruns.otheris another instance. This function needs to be overridden in an object which implements the interface.- If
selfis equal toother,t_comparable_cmpreturns 0. - If
selfis greater thanother,t_comparable_cmpreturns 1. - If
selfis less thanother,t_comparable_cmpreturns -1. - If an error happens,
t_comparable_cmpreturns -2.
- If
t_comparable_eq (self, other)returns TRUE ifselfis 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)returns TRUE ifselfis greater thanother. Otherwise it returns FALSE.t_comparable_lt (self, other)returns TRUE ifselfis less thanother. Otherwise it returns FALSE.t_comparable_ge (self, other)returns TRUE ifselfis greater than or equal toother. Otherwise it returns FALSE.t_comparable_le (self, other)returns TRUE ifselfis less than or equal toother. Otherwise it returns FALSE.
Numbers and strings are comparable. TInt, TDouble, TStr and TNumStr implement TComparable interface so that they can use the functions above. 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)); /* It returns FALSE */
t_comparable_lt (T_COMPARABLE (i1), T_COMPARABLE (i2)); /* It returns 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_addis overridden in TIntClass and TDoubleClass. It can't be overridden in TStrClass because TStr isn't a descendant of TNumber. - A function
t_comparable_cmpis overridden in TIntClass, TDoubleClass, TStrClass and TNumStrClass.
Defining interfaces is similar to define objects.
- Use
G_DECLARE_INTERFACEinstead ofG_DEFINE_FINAL_TYPE. - Use
G_DEFINE_INTERFACEinstead ofG_DEFINE_TYPE.
Now let's see the header file.
1 #ifndef __T_COMPARABLE_H__
2 #define __T_COMPARABLE_H__
3
4 #include <glib-object.h>
5
6 #define T_TYPE_COMPARABLE (t_comparable_get_type ())
7 G_DECLARE_INTERFACE (TComparable, t_comparable, T, COMPARABLE, GObject)
8
9 struct _TComparableInterface {
10 GTypeInterface parent;
11 /* signal */
12 void (*arg_error) (TComparable *self);
13 /* virtual function */
14 int (*cmp) (TComparable *self, TComparable *other);
15 };
16
17 /* t_comparable_cmp */
18 /* if self > other, then returns 1 */
19 /* if self = other, then returns 0 */
20 /* if self < other, then returns -1 */
21 /* if error happens, then returns -2 */
22
23 int
24 t_comparable_cmp (TComparable *self, TComparable *other);
25
26 gboolean
27 t_comparable_eq (TComparable *self, TComparable *other);
28
29 gboolean
30 t_comparable_gt (TComparable *self, TComparable *other);
31
32 gboolean
33 t_comparable_lt (TComparable *self, TComparable *other);
34
35 gboolean
36 t_comparable_ge (TComparable *self, TComparable *other);
37
38 gboolean
39 t_comparable_le (TComparable *self, TComparable *other);
40
41 #endif /* __T_COMPARABLE_H__ */
42 - 7:
G_DECLARE_INTERFACEmacro. 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 GVarinat, 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 TComparableInterfaceT_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
- 9-15:
TComparableInterfacestructure. This is similar to a class structure. The first member is the parent interface. The parent ofTComparableInterfaceisGTypeInterface.GTypeInterfaceis a base of all interface types. It is like aGTypeClasswhich is a base of all class types.GTypeClassis the first member of the structureGObjectClass. (Seegobject.h. Note thatGObjecClassis the same asstruct _GObjectClass.) The next member is a pointerarg_errorto the default signal handler of "arg-error" signal. This signal is emitted when an argument of the public functions isn't TComparable. The last member is a pointer to a methodcmp. It is a virtual function and is expected to be overridden by a function in the object which implements the interface. - 23-39: 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 functio */
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 if ((! T_IS_COMPARABLE (self)) || (! T_IS_COMPARABLE (other)))
34 g_signal_emit (self, t_comparable_signal, 0);
35 g_return_val_if_fail (T_IS_COMPARABLE (self), -2);
36 g_return_val_if_fail (T_IS_COMPARABLE (other), -2);
37
38 int result;
39 TComparableInterface *iface = T_COMPARABLE_GET_IFACE (self);
40
41 g_return_val_if_fail (iface->cmp, -2);
42 if ((result = iface->cmp (self, other)) == -2)
43 g_signal_emit (self, t_comparable_signal, 0);
44 return result;
45 }
46
47 gboolean
48 t_comparable_eq (TComparable *self, TComparable *other) {
49 if ((! T_IS_COMPARABLE (self)) || (! T_IS_COMPARABLE (other)))
50 g_signal_emit (self, t_comparable_signal, 0);
51 g_return_val_if_fail (T_IS_COMPARABLE (self), FALSE);
52 g_return_val_if_fail (T_IS_COMPARABLE (other), FALSE);
53
54 if (t_comparable_cmp (self, other) == 0)
55 return TRUE;
56 else
57 return FALSE;
58 }
59
60 gboolean
61 t_comparable_gt (TComparable *self, TComparable *other) {
62 if ((! T_IS_COMPARABLE (self)) || (! T_IS_COMPARABLE (other)))
63 g_signal_emit (self, t_comparable_signal, 0);
64 g_return_val_if_fail (T_IS_COMPARABLE (self), FALSE);
65 g_return_val_if_fail (T_IS_COMPARABLE (other), FALSE);
66
67 if (t_comparable_cmp (self, other) == 1)
68 return TRUE;
69 else
70 return FALSE;
71 }
72
73 gboolean
74 t_comparable_lt (TComparable *self, TComparable *other) {
75 if ((! T_IS_COMPARABLE (self)) || (! T_IS_COMPARABLE (other)))
76 g_signal_emit (self, t_comparable_signal, 0);
77 g_return_val_if_fail (T_IS_COMPARABLE (self), FALSE);
78 g_return_val_if_fail (T_IS_COMPARABLE (other), FALSE);
79
80 if (t_comparable_cmp (self, other) == -1)
81 return TRUE;
82 else
83 return FALSE;
84 }
85
86 gboolean
87 t_comparable_ge (TComparable *self, TComparable *other) {
88 if ((! T_IS_COMPARABLE (self)) || (! T_IS_COMPARABLE (other)))
89 g_signal_emit (self, t_comparable_signal, 0);
90 g_return_val_if_fail (T_IS_COMPARABLE (self), FALSE);
91 g_return_val_if_fail (T_IS_COMPARABLE (other), FALSE);
92
93
94 int result = t_comparable_cmp (self, other);
95 if (result == 1 || result == 0)
96 return TRUE;
97 else
98 return FALSE;
99 }
100
101 gboolean
102 t_comparable_le (TComparable *self, TComparable *other) {
103 if ((! T_IS_COMPARABLE (self)) || (! T_IS_COMPARABLE (other)))
104 g_signal_emit (self, t_comparable_signal, 0);
105 g_return_val_if_fail (T_IS_COMPARABLE (self), FALSE);
106 g_return_val_if_fail (T_IS_COMPARABLE (other), FALSE);
107
108
109 int result = t_comparable_cmp (self, other);
110 if (result == -1 || result == 0)
111 return TRUE;
112 else
113 return FALSE;
114 }
115 - 5:
G_DEFINE_INTERFACEmacro. 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_cbis a default signal handler of "arg-error" signal. - 12- 29:
t_comparable_default_initfunction. This function is similar to class initialization function. It initializesTComparableInterfacestructure. - 15: Assigns NULL to the pointer to
cmpmethod. So, the method doesn't work before an implementation object overrides it. - 17: Set the default signal handler of the signal "arg-error".
- 18-28: Creates a signal "arg-error". This signal is emitted when an argument for a public function is invalid.
- 31-45:
t_comparable_cmpfunction. First four lines (33-36) check the arguments. If one of them is invalid, it emits "arg-error" signal and outputs an error log, then returns -2. (-2 indicates an error.) Ififace->cmpis NULL (it means the class method hasn't been overridden), then outputs an error log and returns -2. Otherwise it calls the class method. Ifresultequals -2, an error has occurred. Then, it emits "arg-error" signal. Returnsresult. - 47-114: Public functions.
These five functions are based on
t_comparable_cmp. Therefore, no overriding is necessary for the functions. For example,t_comparable_eqjust callst_comparable_cmp. And if the return value is zero, then it returns TRUE. 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 an interface.
TInt, TDouble, TStr and TNumStr 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 "tint.h"
2 #include "tdouble.h"
3 #include "tcomparable.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 static void t_comparable_interface_init (TComparableInterface *iface);
14
15 G_DEFINE_TYPE_WITH_CODE (TInt, t_int, T_TYPE_NUMBER,
16 G_IMPLEMENT_INTERFACE (T_TYPE_COMPARABLE, t_comparable_interface_init))
17
18 static int
19 t_int_comparable_cmp (TComparable *self, TComparable *other) {
20 g_return_val_if_fail (T_IS_INT (self), -2);
21 g_return_val_if_fail (T_IS_INT (other) || T_IS_DOUBLE (other), -2);
22
23 int i;
24 double s, o;
25
26 g_object_get (self, "value", &i, NULL);
27 s = (double) i;
28 if (T_IS_INT (other)) {
29 g_object_get (other, "value", &i, NULL);
30 o = (double) i;
31 } else
32 g_object_get (other, "value", &o, NULL);
33 if (s > o)
34 return 1;
35 else if (s == o)
36 return 0;
37 else if (s < o)
38 return -1;
39 else /* This can't happen. */
40 return -2;
41 }
42
43 static void
44 t_comparable_interface_init (TComparableInterface *iface) {
45 iface->cmp = t_int_comparable_cmp;
46 }
47
48 static void
49 t_int_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) {
50 TInt *self = T_INT (object);
51
52 if (property_id == PROP_INT) {
53 self->value = g_value_get_int (value);
54 } else
55 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
56 }
57
58 static void
59 t_int_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) {
60 TInt *self = T_INT (object);
61
62 if (property_id == PROP_INT)
63 g_value_set_int (value, self->value);
64 else
65 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
66 }
67
68 static void
69 t_int_init (TInt *d) {
70 }
71
72 /* arithmetic operator */
73 /* These operators create a new instance and return a pointer to it. */
74 #define t_int_binary_op(op) \
75 int i; \
76 double d; \
77 if (T_IS_INT (other)) { \
78 g_object_get (T_INT (other), "value", &i, NULL); \
79 return T_NUMBER (t_int_new_with_value (T_INT(self)->value op i)); \
80 } else { \
81 g_object_get (T_DOUBLE (other), "value", &d, NULL); \
82 return T_NUMBER (t_int_new_with_value (T_INT(self)->value op (int) d)); \
83 }
84
85 static TNumber *
86 t_int_add (TNumber *self, TNumber *other) {
87 g_return_val_if_fail (T_IS_INT (self), NULL);
88
89 t_int_binary_op (+)
90 }
91
92 static TNumber *
93 t_int_sub (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_mul (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_div (TNumber *self, TNumber *other) {
108 g_return_val_if_fail (T_IS_INT (self), NULL);
109 int i;
110 double d;
111
112 if (T_IS_INT (other)) {
113 g_object_get (T_INT (other), "value", &i, NULL);
114 if (i == 0) {
115 g_signal_emit_by_name (self, "div-by-zero");
116 return NULL;
117 } else
118 return T_NUMBER (t_int_new_with_value (T_INT(self)->value / i));
119 } else {
120 g_object_get (T_DOUBLE (other), "value", &d, NULL);
121 if (d == 0) {
122 g_signal_emit_by_name (self, "div-by-zero");
123 return NULL;
124 } else
125 return T_NUMBER (t_int_new_with_value (T_INT(self)->value / (int) d));
126 }
127 }
128
129 static TNumber *
130 t_int_uminus (TNumber *self) {
131 g_return_val_if_fail (T_IS_INT (self), NULL);
132
133 return T_NUMBER (t_int_new_with_value (- T_INT(self)->value));
134 }
135
136 static char *
137 t_int_to_s (TNumber *self) {
138 g_return_val_if_fail (T_IS_INT (self), NULL);
139 int i;
140
141 g_object_get (T_INT (self), "value", &i, NULL);
142 return g_strdup_printf ("%d", i);
143 }
144
145 static void
146 t_int_class_init (TIntClass *class) {
147 TNumberClass *tnumber_class = T_NUMBER_CLASS (class);
148 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
149
150 /* override virtual functions */
151 tnumber_class->add = t_int_add;
152 tnumber_class->sub = t_int_sub;
153 tnumber_class->mul = t_int_mul;
154 tnumber_class->div = t_int_div;
155 tnumber_class->uminus = t_int_uminus;
156 tnumber_class->to_s = t_int_to_s;
157
158 gobject_class->set_property = t_int_set_property;
159 gobject_class->get_property = t_int_get_property;
160 int_property = g_param_spec_int ("value", "val", "Integer value", G_MININT, G_MAXINT, 0, G_PARAM_READWRITE);
161 g_object_class_install_property (gobject_class, PROP_INT, int_property);
162 }
163
164 TInt *
165 t_int_new_with_value (int value) {
166 TInt *d;
167
168 d = g_object_new (T_TYPE_INT, "value", value, NULL);
169 return d;
170 }
171
172 TInt *
173 t_int_new (void) {
174 TInt *d;
175
176 d = g_object_new (T_TYPE_INT, NULL);
177 return d;
178 }
179 - 3: It needs to include the header file of TComparable.
- 13: Declaration of
t_comparable_interface_init ()function. This declaration is done beforeG_DEFINE_TYPE_WITH_CODEmacro. - 15-16:
G_DEFINE_TYPE_WITH_CODEmacro. The last parameter isG_IMPLEMENT_INTERFACEmacro. The third parameter ofG_IMPLEMENT_INTERFACEist_comparabele_interface_init. These two macros expands to:- Declares
t_int_class_init ()function. - Declares
t_int_init ()function. - Defines
t_int_parent_classstatic variable which points to the parent's class. - Defines
t_int_get_type ()function. This function includesg_type_register_static_simple ()andg_type_add_interface_static ().g_type_register_static_simple ()is a convenient version ofg_type_register_static (). It registers TInt type to the type system.g_type_add_interface_static ()adds an interface type to an instance type. There is a good example in GObject Reference Manual, Interfaces.
- Declares
- 18-41:
t_int_comparable_cmpis a function to compare TInt instances. - 26: Gets the value of
selfand cast it to double. - 27-32: Gets the value of
otherand if it is TInt then the value is casted to double. Ifotheris TDouble, no cast is needed. - 33-40: compares
sandoand returns 1, 0, -1 and -2. - 43-46:
t_comparable_interface_init. This function is called in the initialization process of TInt.t_int_comparable_cmpoverridescmpmethod. - 48-178: The same as before.
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.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 TStrPrivate *priv = t_str_get_instance_private (self);
26
27
28 if (property_id == PROP_STRING) {
29 if (priv->string)
30 g_free (priv->string);
31 priv->string = g_strdup (g_value_get_string (value));
32 } else
33 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
34 }
35
36 static void
37 t_str_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) {
38 TStr *self = T_STR (object);
39 TStrPrivate *priv = t_str_get_instance_private (self);
40
41 if (property_id == PROP_STRING)
42 g_value_set_string (value, g_strdup (priv->string));
43 else
44 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
45 }
46
47 static void
48 t_str_finalize (GObject *object) {
49 TStr *self = T_STR (object);
50 TStrPrivate *priv = t_str_get_instance_private (self);
51
52 if (priv->string)
53 g_free (priv->string);
54 G_OBJECT_CLASS (t_str_parent_class)->finalize (object);
55 }
56
57 static int
58 t_str_comparable_cmp (TComparable *self, TComparable *other) {
59 g_return_val_if_fail (T_IS_STR (self), -2);
60 g_return_val_if_fail (T_IS_STR (other), -2);
61
62 char *s, *o;
63 int result;
64
65 s = t_str_get_string (T_STR (self));
66 o = t_str_get_string (T_STR (other));
67
68 if (strcmp (s, o) > 0)
69 result = 1;
70 else if (strcmp (s, o) == 0)
71 result = 0;
72 else if (strcmp (s, o) < 0)
73 result = -1;
74 else /* This can't happen. */
75 result = -2;
76 g_free (s);
77 g_free (o);
78 return result;
79 }
80
81 static void
82 t_comparable_interface_init (TComparableInterface *iface) {
83 iface->cmp = t_str_comparable_cmp;
84 }
85
86 static void
87 t_str_init (TStr *self) {
88 TStrPrivate *priv = t_str_get_instance_private (self);
89
90 priv->string = NULL;
91 }
92
93 static void
94 t_str_class_init (TStrClass *class) {
95 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
96
97 gobject_class->finalize = t_str_finalize;
98 gobject_class->set_property = t_str_set_property;
99 gobject_class->get_property = t_str_get_property;
100 str_properties[PROP_STRING] = g_param_spec_string ("string", "str", "string", "", G_PARAM_READWRITE);
101 g_object_class_install_properties (gobject_class, N_PROPERTIES, str_properties);
102 }
103
104 /* setter and getter */
105 void
106 t_str_set_string (TStr *self, const char *s) {
107 g_return_if_fail (T_IS_STR (self));
108
109 g_object_set (self, "string", s, NULL);
110 }
111
112 char *
113 t_str_get_string (TStr *self) {
114 g_return_val_if_fail (T_IS_STR (self), NULL);
115
116 char *s;
117
118 g_object_get (self, "string", &s, NULL);
119 return s;
120 }
121
122 TStr *
123 t_str_concat (TStr *self, TStr *other) {
124 g_return_val_if_fail (T_IS_STR (self), NULL);
125 g_return_val_if_fail (T_IS_STR (other), NULL);
126
127 char *s1, *s2, *s3;
128 TStr *str;
129
130 s1 = t_str_get_string (self);
131 s2 = t_str_get_string (other);
132 if (s1 && s2)
133 s3 = g_strconcat (s1, s2, NULL);
134 else if (s1)
135 s3 = g_strdup (s1);
136 else if (s2)
137 s3 = g_strdup (s2);
138 else
139 s3 = NULL;
140 str = t_str_new_with_string (s3);
141 if (s1) g_free (s1);
142 if (s2) g_free (s2);
143 if (s3) g_free (s3);
144 return str;
145 }
146
147 /* create a new TStr instance */
148 TStr *
149 t_str_new_with_string (const char *s) {
150 return T_STR (g_object_new (T_TYPE_STR, "string", s, NULL));
151 }
152
153 TStr *
154 t_str_new (void) {
155 return T_STR (g_object_new (T_TYPE_STR, NULL));
156 }
157 - 16: Declares
t_comparable_interface_initfunction. This needs to be declared beforeG_DEFINE_TYPE_WITH_CODEmacro. - 18-20:
G_DEFINE_TYPE_WITH_CODEmacro. Because TStr is derivable type, its private area (TStrPrivate) is needed.G_ADD_PRIVATEmacro makes the private area. Be careful that there's no comma afterG_ADD_PRIVATEmacro. - 57-79:
t_str_comparable_cmp. First, stringssandoare got from TStr objectsselfandother. Then, the C standard functionstrcmpis used to compare strings. After that,sandoare freed.
TStr can be compared with TStr, but not with TInt nor TDouble. Generally, comparison is available between two same type instances.
TNumStr implements TComparable.
The program tnumstr.c is similar to tint.c.
Its cmp function compares two numeric strings based on their numbers.
Therefore, "100" is bigger than "50".
(If you compare them as TStr, "100" is smaller than "50".)
The source file is in the src/tcomparable directory.
If you want to know the details, see `src/tcomparable/tnumstr.c.
main.c is a test program.
1 #include <glib-object.h>
2 #include "tcomparable.h"
3 #include "tnumber.h"
4 #include "tint.h"
5 #include "tdouble.h"
6 #include "tstr.h"
7 #include "tnumstr.h"
8
9
10 static void
11 notify_cb (GObject *gobject, GParamSpec *pspec, gpointer user_data) {
12 const char *name;
13 int i;
14 double d;
15 char *s;
16
17 name = g_param_spec_get_name (pspec);
18 if (T_IS_INT (gobject) && strcmp (name, "value") == 0) {
19 g_object_get (T_INT (gobject), "value", &i, NULL);
20 g_print ("Property \"%s\" is set to %d.\n", name, i);
21 } else if (T_IS_DOUBLE (gobject) && strcmp (name, "value") == 0) {
22 g_object_get (T_DOUBLE (gobject), "value", &d, NULL);
23 g_print ("Property \"%s\" is set to %lf.\n", name, d);
24 } else if (T_IS_STR (gobject) && strcmp (name, "string") == 0) {
25 s = t_str_get_string (T_STR (gobject));
26 g_print ("Property \"%s\" is set to %s.\n", name, s);
27 }
28 }
29
30 static void
31 t_print (const char *cmp, TComparable *c1, TComparable *c2) {
32 char *s1, *s2;
33
34 if (T_IS_NUMBER (c1))
35 s1 = t_number_to_s (T_NUMBER (c1));
36 else if (T_IS_STR (c1))
37 s1 = t_str_get_string (T_STR (c1));
38 else {
39 g_print ("c1 isn't TInt, TDouble nor TStr.\n");
40 return;
41 }
42 if (T_IS_NUMBER (c2))
43 s2 = t_number_to_s (T_NUMBER (c2));
44 else if (T_IS_STR (c2))
45 s2 = t_str_get_string (T_STR (c2));
46 else {
47 g_print ("c2 isn't TInt, TDouble nor TStr.\n");
48 return;
49 }
50 if (T_IS_STR (c1))
51 g_print ("\"%s\" %s \"%s\".\n", s1, cmp, s2);
52 else
53 g_print ("%s %s %s.\n", s1, cmp, s2);
54 g_free (s1);
55 g_free (s2);
56 }
57
58 static void
59 compare (TComparable *c1, TComparable *c2) {
60 if (t_comparable_eq (c1, c2))
61 t_print ("equals", c1, c2);
62 if (t_comparable_gt (c1, c2))
63 t_print ("is greater than", c1, c2);
64 if (t_comparable_lt (c1, c2))
65 t_print ("is less than", c1, c2);
66 if (t_comparable_ge (c1, c2))
67 t_print ("is greater than or equal to", c1, c2);
68 if (t_comparable_le (c1, c2))
69 t_print ("is less than or equal to", c1, c2);
70 }
71
72 int
73 main (int argc, char **argv) {
74 const char *one = "one";
75 const char *two = "two";
76 TInt *i;
77 TDouble *d;
78 TStr *str1, *str2;
79 TNumStr *numstr1, *numstr2;
80 gpointer obj;
81
82 i = t_int_new ();
83 d = t_double_new ();
84 str1 = t_str_new ();
85 str2 = t_str_new ();
86 numstr1 = t_num_str_new ();
87 numstr2 = t_num_str_new ();
88 obj = g_object_new (G_TYPE_OBJECT, NULL);
89
90 g_signal_connect (G_OBJECT (i), "notify::value", G_CALLBACK (notify_cb), NULL);
91 g_signal_connect (G_OBJECT (d), "notify::value", G_CALLBACK (notify_cb), NULL);
92 g_signal_connect (G_OBJECT (str1), "notify::string", G_CALLBACK (notify_cb), NULL);
93 g_signal_connect (G_OBJECT (str2), "notify::string", G_CALLBACK (notify_cb), NULL);
94 g_signal_connect (G_OBJECT (numstr1), "notify::string", G_CALLBACK (notify_cb), NULL);
95 g_signal_connect (G_OBJECT (numstr2), "notify::string", G_CALLBACK (notify_cb), NULL);
96
97 g_object_set (i, "value", 124, NULL);
98 g_object_set (d, "value", 123.45, NULL);
99 t_str_set_string (str1, one);
100 t_str_set_string (str2, two);
101 t_num_str_set (numstr1, T_NUMBER (i));
102 t_num_str_set (numstr2, T_NUMBER (d));
103
104 compare (T_COMPARABLE (i), T_COMPARABLE (d));
105 compare (T_COMPARABLE (str1), T_COMPARABLE (str2));
106 compare (T_COMPARABLE (numstr1), T_COMPARABLE (numstr2));
107 t_comparable_eq (T_COMPARABLE (d), obj);
108
109 g_print ("\n");
110 t_str_set_string (str1, "100");
111 t_str_set_string (str2, "50");
112 t_str_set_string (T_STR (numstr1), "100");
113 t_str_set_string (T_STR (numstr2), "50");
114 compare (T_COMPARABLE (str1), T_COMPARABLE (str2));
115 compare (T_COMPARABLE (numstr1), T_COMPARABLE (numstr2));
116
117 g_object_unref (i);
118 g_object_unref (d);
119 g_object_unref (str1);
120 g_object_unref (str2);
121 g_object_unref (numstr1);
122 g_object_unref (numstr2);
123 g_object_unref (obj);
124
125 return 0;
126 }
127 - 10-28: "notify" signal handler is extended to support three types of objects: TInt, TDouble and TStr. Because TNumStr is a child of TStr, TNumStr instance is also supported.
- 30-56: This function
t_printusesto_sfunction to convert TInt and/or TDouble to a string. Then, displays the result of the comparison of two TComparable objects. - 58-70:
comparecompares two TComparable objects and callst_printto display the result. - 72-125:
mainfunction. - 82-88: Creates TInt, TDouble, two TStr, two TNumStr and GObject instances.
- 90-95: Connects "notify" signal.
- 97-102: Set values and strings of TInt, TDouble, TStr and TNumStr.
- 104: Compares TInt and TDouble.
- 105: Compares two TStr.
- 106: Compares two TNumStr.
- 107: Check equality between TDouble and GObject. This makes an error because GObject doesn't implement TComparable.
- 109-115: Compare "100" and "50" as TStr first, then as TNumStr. "100" is smaller than "50" if they are TStr. "100" is bigger than "50" if they are TNumStr.
- 117-123: Frees objects.
Change your current directory to src/tcomparable.
$ meson _build
$ ninja -C _build
Then execute it.
$ cd tcomparable; _build/tcomparable
Property "value" is set to 124.
Property "value" is set to 123.450000.
Property "string" is set to one.
Property "string" is set to two.
Property "string" is set to 124.
Property "string" is set to 123.450000.
124 is greater than 123.450000.
124 is greater than or equal to 123.450000.
"one" is less than "two".
"one" is less than or equal to "two".
"124" is greater than "123.450000".
"124" is greater than or equal to "123.450000".
TComparable: argument error.
** (process:7737): CRITICAL **: 13:08:42.673: t_comparable_eq: assertion 'T_IS_COMPARABLE (other)' failed
Property "string" is set to 100.
Property "string" is set to 50.
Property "string" is set to 100.
Property "string" is set to 50.
"100" is less than "50".
"100" is less than or equal to "50".
"100" is greater than "50".
"100" is greater than or equal to "50".
The lines from TComparable: argument error. to ** (process:7737)... show that the comparison with TDouble and GObject fails.
We used macros such as G_DECLARE_INTERFACE, G_DEFINE_INTERFACE to build an interface.
We can also build it without macros.
There are two files in the tcomparable directory.
tcomparable_without_macro.htcomparable_without_macro.c
These two files don't use the macros. Instead, they register 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 language are similar. Learning such languages is very useful to know GObject system.