九、接口
接口类似于抽象类。接口定义了虚拟函数,这些函数预期会被另一个可实例化对象中的函数覆盖。
本节提供了一个简单的例子,TComparable。TComparable 是一个接口。它定义了比较的函数,这些函数包括:
-
t_comparable_cmp(self, other):它比较 self 和 other。第一个参数 self 是运行 t_comparable_cmp 的实例。第二个参数 other 是另一个实例。这个函数需要在实现了该接口的对象中被覆盖。
-
如果 self 等于 other,t_comparable_cmp 返回 0。
-
如果 self 大于 other,t_comparable_cmp 返回 1。
-
如果 self 小于 other,t_comparable_cmp 返回 -1。
-
如果发生错误,t_comparable_cmp 返回 -2。
-
-
t_comparable_eq(self, other):如果 self 等于 other,则返回 TRUE。否则返回 FALSE。需要注意的是,即使发生错误也会返回 FALSE。
-
t_comparable_gt(self, other):如果 self 大于 other,则返回 TRUE。否则返回 FALSE。
-
t_comparable_lt(self, other):如果 self 小于 other,则返回 TRUE。否则返回 FALSE。
-
t_comparable_ge(self, other):如果 self 大于或等于 other,则返回 TRUE。否则返回 FALSE。
-
t_comparable_le(self, other):如果 self 小于或等于 other,则返回 TRUE。否则返回 FALSE。
数字和字符串是可比较的。TInt、TDouble 和 TStr 实现了 TComparable 接口,因此它们可以使用上述函数。此外,TNumStr 也可以使用这些函数,因为它是 TStr 的子类。
例如,
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 */
接口和抽象类之间的区别是什么?在抽象类中的虚拟函数由其后代类中的函数覆盖。在接口中的虚拟函数可以由任何类中的函数覆盖。比较一下 TNumber 和 TComparable。
-
函数 t_number_add 在 TIntClass 和 TDoubleClass 中被覆盖。它不能在 TStrClass 中被覆盖,因为 TStr 不是 TNumber 的后代。
-
函数 t_comparable_cmp 在 TIntClass、TDoubleClass 和 TStrClass 中被覆盖。
1 TComparable 接口
定义接口与定义对象类似。
-
使用 G_DECLARE_INTERFACE 代替 G_DECLARE_FINAL_TYPE。
-
使用 G_DEFINE_INTERFACE 代替 G_DEFINE_TYPE。
1.1 头文件 tcomparable.h
现在让我们看看头文件。
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; /* 注意:第一个成员是 GTypeInterface parent */
10 /* 信号 */
11 void (*arg_error) (TComparable *self);
12 /* 虚函数 */
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宏。最后一个参数是接口的先决条件。TComparable的先决条件是GObject。因此,除了GObject的派生类之外,例如GVariant,不能实现TComparable。先决条件是接口或类的GType。此宏扩展为:
/************************************G_DECLARE_INTERFACE宏展开*********************START**********************/ GType t_comparable_get_type (void); # 4 "/home/lieryang/Desktop/gobject-study/section-two/sec9/interface.c" #pragma GCC diagnostic push # 4 "/home/lieryang/Desktop/gobject-study/section-two/sec9/interface.c" # 4 "/home/lieryang/Desktop/gobject-study/section-two/sec9/interface.c" #pragma GCC diagnostic ignored "-Wdeprecated-declarations" # 4 "/home/lieryang/Desktop/gobject-study/section-two/sec9/interface.c" typedef struct _TComparable TComparable; typedef struct _TComparableInterface TComparableInterface; typedef TComparable *TComparable_autoptr; typedef GList *TComparable_listautoptr; typedef GSList *TComparable_slistautoptr; typedef GQueue *TComparable_queueautoptr; # 4 "/home/lieryang/Desktop/gobject-study/section-two/sec9/interface.c" #pragma GCC diagnostic push # 4 "/home/lieryang/Desktop/gobject-study/section-two/sec9/interface.c" # 4 "/home/lieryang/Desktop/gobject-study/section-two/sec9/interface.c" #pragma GCC diagnostic ignored "-Wdeprecated-declarations" # 4 "/home/lieryang/Desktop/gobject-study/section-two/sec9/interface.c" static __attribute__ ((__unused__)) inline void glib_autoptr_clear_TComparable (TComparable *_ptr) { if (_ptr) (glib_autoptr_clear_GObject) ((GObject *) _ptr); } static __attribute__ ((__unused__)) inline void glib_autoptr_cleanup_TComparable (TComparable **_ptr) { glib_autoptr_clear_TComparable (*_ptr); } static __attribute__ ((__unused__)) inline void glib_listautoptr_cleanup_TComparable (GList **_l) { g_list_free_full (*_l, (GDestroyNotify) (void(*)(void)) glib_autoptr_clear_GObject); } static __attribute__ ((__unused__)) inline void glib_slistautoptr_cleanup_TComparable (GSList **_l) { g_slist_free_full (*_l, (GDestroyNotify) (void(*)(void)) glib_autoptr_clear_GObject); } static __attribute__ ((__unused__)) inline void glib_queueautoptr_cleanup_TComparable (GQueue **_q) { if (*_q) g_queue_free_full (*_q, (GDestroyNotify) (void(*)(void)) glib_autoptr_clear_GObject); } # 4 "/home/lieryang/Desktop/gobject-study/section-two/sec9/interface.c" #pragma GCC diagnostic pop # 4 "/home/lieryang/Desktop/gobject-study/section-two/sec9/interface.c" __attribute__ ((__unused__)) static inline TComparable * T_COMPARABLE (gpointer ptr) { return (((TComparable*) (void *) g_type_check_instance_cast ((GTypeInstance*) (ptr), (t_comparable_get_type ())))); } __attribute__ ((__unused__)) static inline gboolean T_IS_COMPARABLE (gpointer ptr) { return ((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (ptr); GType __t = (t_comparable_get_type ()); gboolean __r; if (!__inst) __r = (0); else if (__inst->g_class && __inst->g_class->g_type == __t) __r = (!(0)); else __r = g_type_check_instance_is_a (__inst, __t); __r; }))); } __attribute__ ((__unused__)) static inline TComparableInterface * T_COMPARABLE_GET_IFACE (gpointer ptr) { return (((TComparableInterface*) g_type_interface_peek (((GTypeInstance*) (ptr))->g_class, (t_comparable_get_type ())))); } # 4 "/home/lieryang/Desktop/gobject-study/section-two/sec9/interface.c" #pragma GCC diagnostic pop # 4 "/home/lieryang/Desktop/gobject-study/section-two/sec9/interface.c" /************************************G_DECLARE_INTERFACE宏展开*********************END**********************/
-
声明 GType t_comparable_get_type (void);
-
定义 typedef struct _TComparableInterface TComparableInterface
-
定义 T_COMPARABLE()宏。它将一个实例转换为TComparable类型。
-
定义 T_IS_COMPARABLE()宏。它检查实例的类型是否为T_TYPE_COMPARABLE。
-
定义 T_COMPARABLE_GET_IFACE()宏。它获取给定作为参数的实例的接口。
-
-
8-14: TComparableInterface结构。这类似于类结构。第一个成员是父接口 。TComparableInterface的父接口是GTypeInterface。GTypeInterface是所有接口类型的基类。它类似于是GTypeClass,它是所有类类型的基类。GTypeClass是结构GObjectClass的第一个成员。 请参阅gobject.h。请注意,GObjectClass与struct _GObjectClass相同)。
/* GTypeInterface有两个成员,一个是接口类型id,另一个是实例 */ struct _GTypeInterface { /*< private >*/ GType g_type; /* iface type */ GType g_instance_type; };
下一个成员是指向“arg-error”信号的默认信号处理程序arg_error的指针。 当比较函数的第二个参数不合适时,将触发此信号。例如,如果self是TInt,other是TStr,它们都是Comparable实例。但它们不能进行比较。这是因为other不是TNumber。最后一个成员cmp是指向比较方法的指针。这是一个虚函数,预计将由实现接口的对象中的函数覆盖。
-
22-38: 公共函数。
1.2 源文件 tcomparable.c
C文件tcomparable.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_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宏。第三个参数是前提条件的类型。此宏扩展为:
/************************************G_DEFINE_INTERFACE宏展开*********************START**********************/ static void t_comparable_default_init (TComparableInterface *klass); GType t_comparable_get_type (void) { static gsize static_g_define_type_id = 0; if ((__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id) == sizeof (gpointer), "Expression evaluates to false"); (void) (0 ? (gpointer) *(&static_g_define_type_id) : ((void *)0)); (!(__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id) == sizeof (gpointer), "Expression evaluates to false"); __typeof__ (*(&static_g_define_type_id)) gapg_temp_newval; __typeof__ ((&static_g_define_type_id)) gapg_temp_atomic = (&static_g_define_type_id); __atomic_load (gapg_temp_atomic, &gapg_temp_newval, 5); gapg_temp_newval; })) && g_once_init_enter (&static_g_define_type_id)); }))) { GType g_define_type_id = g_type_register_static_simple (((GType) ((2) << (2))), g_intern_static_string ("TComparable"), sizeof (TComparableInterface), (GClassInitFunc)(void (*)(void)) t_comparable_default_init, 0, (GInstanceInitFunc) ((void *)0), (GTypeFlags) 0); if (((GType) ((20) << (2))) != ((GType) ((0) << (2)))) g_type_interface_add_prerequisite (g_define_type_id, ((GType) ((20) << (2)))); { {;;} } (__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id) == sizeof (gpointer), "Expression evaluates to false"); 0 ? (void) (*(&static_g_define_type_id) = (g_define_type_id)) : (void) 0; g_once_init_leave ((&static_g_define_type_id), (gsize) (g_define_type_id)); })); } return static_g_define_type_id; } /************************************G_DEFINE_INTERFACE宏展开*********************END**********************/
-
声明 t_comparable_default_init()。
-
定义 t_comparable_get_type()。
-
-
7-10: arg_error_default_cb是”arg-error”信号的默认信号处理程序。
-
12-29: t_comparable_default_init函数。此函数类似于类初始化函数。它初始化TComparableInterface结构。
-
15: 将cmp赋值为NULL。因此,在实现类覆盖它之前,比较方法不起作用。
-
17: 设置”arg-error”信号的默认信号处理程序。
-
18-28: 创建”arg-error”信号。
-
31-38: 函数t_comparable_cmp。它首先检查self的类型。如果它不可比较,它会记录错误消息并返回-2(错误)。如果iface->cmp为NULL(这意味着类方法还没有被覆盖),则返回-2。否则,它调用类方法并返回类方法返回的值。
- 40-65: 公共函数。这五个函数都基于
t_comparable_cmp
。因此,它们不需要被覆盖。例如,t_comparable_eq只是调用t_comparable_cmp。如果t_comparable_cmp返回零,它返回TRUE。否则,它返回FALSE。
这个程序使用信号向用户提供参数类型错误信息。这种错误通常是程序错误,而不是运行时错误。使用信号报告程序错误并不是一个好方法。最好的方法是使用g_return_if_fail。我之所以使用这个信号,只是想演示如何在接口中实现信号。
2 Implementing interface
TInt、TDouble 和 TStr 实现了 TComparable。首先看一下 TInt。头文件与之前相同。实现是在 C 文件中编写的。
2.1 源文件 tint.c
tint.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_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:需要包含 TComparable 的头文件。
-
19:声明 t_comparable_interface_init() 函数。这个声明必须在 G_DEFINE_TYPE_WITH_CODE 宏之前完成。
-
21-22:G_DEFINE_TYPE_WITH_CODE 宏。最后一个参数是 G_IMPLEMENT_INTERFACE 宏。G_IMPLEMENT_INTERFACE 的第二个参数是 t_comparable_interface_init。这两个宏展开为:
-
声明 t_int_class_init()。
-
声明 t_int_init()。
-
定义静态变量 t_int_parent_class,它指向父类的类结构。
-
定义 t_int_get_type()。此函数包含 g_type_register_static_simple() 和 g_type_add_interface_static()。g_type_register_static_simple() 是 g_type_register_static() 的便捷版本,它将 TInt 类型注册到类型系统中。
g_type_add_interface_static()
将接口类型添加到实例类型中。在 GObject 参考手册的 Interfaces 一节中有一个很好的例子。如果你想知道如何不使用宏来编写代码,请参见 tint_without_macro.c。
-
-
24-48:t_int_comparable_cmp 是一个比较 TInt 实例和 TNumber 实例的函数。
-
26-29:检查 other 的类型。如果参数类型不是 TNumber,则使用 g_signal_emit_by_name 发出 “arg-error” 信号。
-
34:将 self 转换为 double 类型。
-
35-39:获取 other 的值,如果它是 TInt,则将值转换为 double。
-
40-47:比较 s 和 o,返回 1、0、-1 和 -2。
-
50-53:t_comparable_interface_init。这个函数在 TInt 的初始化过程中被调用。函数 t_int_comparable_cmp 被分配给 iface->cmp。
tdouble.c 与 tint.c 几乎相同。这两个对象可以比较,因为在比较前 int 被转换为 double。
2.2 源文件 tstr.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_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:声明了 t_comparable_interface_init 函数。在 G_DEFINE_TYPE_WITH_CODE 宏之前需要声明该函数。
-
18-20:G_DEFINE_TYPE_WITH_CODE 宏。因为 TStr 是可派生类型,所以需要其私有区域(TStrPrivate)。G_ADD_PRIVATE 宏创建了私有区域。注意 G_ADD_PRIVATE 宏后面没有逗号。
-
68-92:t_str_comparable_cmp 函数。
-
70-73:检查 other 的类型。如果它不是 TStr,则发出 “arg-error” 信号。
-
78-79:从 TStr 对象 self 和 other 中获取字符串 s 和 o。
-
81-88:使用 C 标准函数 strcmp 比较 s 和 o。
-
89-90:释放 s 和 o。
-
91:返回结果。
-
94-97:t_comparable_interface_init 函数。它用 t_str_comparable_cmp 覆盖了 iface->comp。
TStr 可以与 TStr 比较,但不能与 TInt 或 TDouble 比较。通常,比较只在两个同类型实例之间可用。
TNumStr 本身没有实现 TComparable。但它是 TStr 的子类,所以它是可比较的。比较基于字母顺序。所以,”a” 大于 “b”,”three” 大于 “two”。
3 Test program
main.c 是一个测试程序
3.1 main.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) {
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:函数 t_print 有三个参数,并构建一个输出字符串,然后将其显示在显示器上。在构建输出时,字符串被双引号包围。
-
44-58:函数 compare 比较两个 TComparable 对象,并调用 t_print 来显示结果。
-
60-87:main 函数。
-
69-73:创建 TInt、TDouble 和三个 TStr 实例。它们被赋予了值。
-
75:比较 TInt 和 TDouble。
-
76-77:比较两个 TStr。
-
78:将 TInt 与 TStr 比较。这会产生一个 “arg-error”。
-
80-84:释放对象。
4 Compilation and execution
改变目录到 /assets/GObjectStudy/202311/09_GObject/tcomparable/
$ cd ./assets/GObjectStudy/202311/09_GObject/tcomparable/
$ meson setup _build
$ ninja -C _build
执行
$ _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".
5 Build an interface without macros
我们使用了如 G_DECLARE_INTERFACE
、G_DEFINE_INTERFACE
等宏来构建接口。并使用 G_DEFINE_TYPE_WITH_CODE
来实现接口。我们也可以不使用宏来构建它。在 tcomparable 目录中有三个文件。
-
tcomparable_without_macro.h
-
tcomparable_without_macro.c
-
tint_without_macro.c
它们不使用宏。相反,它们直接将接口或接口的实现注册到类型系统中。如果你想了解这方面的内容,可以查看 /assets/GObjectStudy/202311/09_GObject/tcomparable/ 中的源文件。
参考
Interface:https://github.com/ToshioCP/Gobject-tutorial/blob/main/gfm/sec9.md