Derivable type and abstract type
Derivable type
在GLib中,类型可以分为两种。一种是可以被继承的(derivable),一种是不能被继承的(final)。二者的主要区别是,final 类型对象的类结构中,除了其父类外,再无其他成员。而derivable类型对象的类结构中会存在其他成员, 且derivable类型的类对其衍生类是可见的。对于上篇我们的例子TDouble,就是一个final类。我们在注册时使用的是G_DEFINE_TYPE,实际上还可以使用G_DECLARE_FINAL_TYPE。
Abstract type
abstract type 属于derivable type,只是abstract type 不会生成实例,但其衍生对象可以使用其函数和信号。
对于abstract type,我们将会创建TNumber类型来进行说明。上篇我们说TDouble 代表了浮点数,那么,更抽象一层,TNumber则表示全体数。TNumber是TDouble 的父类,由于TNumber属于abstract type,因此不能按照我们实例化TDouble那样进行实例化。毕竟不存在能够对数字进行统一表示的方式。int 类型的5.0,与double类型的5.0,虽然绝对值是一样的,但是int类型的5.0相对于double类型的5.0,其实是丧失精度的,因此两者并不相同。但任何包含TDouble的对象在实例化时,也意味着TNumber也被实例化了。例如,当TDouble中value为5.0,我们可以说5.0就是TDouble的实例,同时,5.0首先是个数字,因此,5.0也是TNumber的实例化。
TNumber class
G_DECLARE_DERIVABLE_TYPE (TNumber, t_number, T, NUMBER, GObject)
struct _TNumberClass {
GObjectClass parent_class;
TNumber* (*add) (TNumber *self, TNumber *other);
TNumber* (*sub) (TNumber *self, TNumber *other);
TNumber* (*mul) (TNumber *self, TNumber *other);
TNumber* (*div) (TNumber *self, TNumber *other);
TNumber* (*uminus) (TNumber *self);
char * (*to_s) (TNumber *self);
/* signal */
void (*div_by_zero) (TNumber *self);
};
在glib 2.78.2中,宏G_DECLARE_DERIVABLE_TYPE的定义如下:
#define G_DECLARE_DERIVABLE_TYPE(ModuleObjName, module_obj_name, MODULE, OBJ_NAME, ParentName) \
GType module_obj_name##_get_type (void); \
G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
typedef struct _##ModuleObjName ModuleObjName; \
typedef struct _##ModuleObjName##Class ModuleObjName##Class; \
struct _##ModuleObjName { ParentName parent_instance; }; \
\
_GLIB_DEFINE_AUTOPTR_CHAINUP (ModuleObjName, ParentName) \
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ModuleObjName##Class, g_type_class_unref) \
\
G_GNUC_UNUSED static inline ModuleObjName * MODULE##_##OBJ_NAME (gpointer ptr) { \
return G_TYPE_CHECK_INSTANCE_CAST (ptr, module_obj_name##_get_type (), ModuleObjName); } \
G_GNUC_UNUSED static inline ModuleObjName##Class * MODULE##_##OBJ_NAME##_CLASS (gpointer ptr) { \
return G_TYPE_CHECK_CLASS_CAST (ptr, module_obj_name##_get_type (), ModuleObjName##Class); } \
G_GNUC_UNUSED static inline gboolean MODULE##_IS_##OBJ_NAME (gpointer ptr) { \
return G_TYPE_CHECK_INSTANCE_TYPE (ptr, module_obj_name##_get_type ()); } \
G_GNUC_UNUSED static inline gboolean MODULE##_IS_##OBJ_NAME##_CLASS (gpointer ptr) { \
return G_TYPE_CHECK_CLASS_TYPE (ptr, module_obj_name##_get_type ()); } \
G_GNUC_UNUSED static inline ModuleObjName##Class * MODULE##_##OBJ_NAME##_GET_CLASS (gpointer ptr) { \
return G_TYPE_INSTANCE_GET_CLASS (ptr, module_obj_name##_get_type (), ModuleObjName##Class); } \
G_GNUC_END_IGNORE_DEPRECATIONS
对于TNumber对象来说,G_DECLARE_DERIVABLE_TYPE宏的作用如下:
- 声明函数t_number_get_type()。
- 定义TNumber数据结构,此数据结构的成员只有其父实例。
- 声明TNumberClass数据结构,具体定义需要用户实现。
- 定义宏T_NUMBER(cast to instance)、T_NUMBER_CLASS(cast to class)、T_IS_NUMBER(instance check)、T_IS_NUMBER_CLASS(class check)、T_NUMBER_GET_CLASS.
- g_autoptr()。
struct _TNumber中的函数指针称为类方法或者虚拟函数,他们会被TNumber的衍生对象重写。
对照上篇所说的G_DEFINE_TYPE宏的工作,对于abstract type,有个同样功能的宏G_DEFINE_ABSTRACT。
TInt object
介绍完Derivable type和Abstract type,现在我们来介绍其应用。我们使用新类型TInt来说明。
/*tint.h*/
#pragma once
#include <glib-object.h>
#define T_TYPE_INT (t_int_get_type ())
G_DECLARE_FINAL_TYPE (TInt, t_int, T, INT, TNumber)
/* create a new TInt instance */
TInt *
t_int_new_with_value (int value);
TInt *
t_int_new (void);
/*tint.c*/
#include "tnumber.h"
#include "tint.h"
#include "tdouble.h"
#define PROP_INT 1
static GParamSpec *int_property = NULL;
struct _TInt {
TNumber parent;
int value;
};
G_DEFINE_TYPE (TInt, t_int, T_TYPE_NUMBER)
static void
t_int_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) {
TInt *self = T_INT (object);
if (property_id == PROP_INT)
self->value = g_value_get_int (value);
else
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
t_int_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) {
TInt *self = T_INT (object);
if (property_id == PROP_INT)
g_value_set_int (value, self->value);
else
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
t_int_init (TInt *self) {
}
/* arithmetic operator */
/* These operators create a new instance and return a pointer to it. */
#define t_int_binary_op(op) \
int i; \
double d; \
if (T_IS_INT (other)) { \
g_object_get (T_INT (other), "value", &i, NULL); \
return T_NUMBER (t_int_new_with_value (T_INT(self)->value op i)); \
} else { \
g_object_get (T_DOUBLE (other), "value", &d, NULL); \
return T_NUMBER (t_int_new_with_value (T_INT(self)->value op (int) d)); \
}
static TNumber *
t_int_add (TNumber *self, TNumber *other) {
g_return_val_if_fail (T_IS_INT (self), NULL);
t_int_binary_op (+)
}
static TNumber *
t_int_sub (TNumber *self, TNumber *other) {
g_return_val_if_fail (T_IS_INT (self), NULL);
t_int_binary_op (-)
}
static TNumber *
t_int_mul (TNumber *self, TNumber *other) {
g_return_val_if_fail (T_IS_INT (self), NULL);
t_int_binary_op (*)
}
static TNumber *
t_int_div (TNumber *self, TNumber *other) {
g_return_val_if_fail (T_IS_INT (self), NULL);
int i;
double d;
if (T_IS_INT (other)) {
g_object_get (T_INT (other), "value", &i, NULL);
if (i == 0) {
g_signal_emit_by_name (self, "div-by-zero");
return NULL;
} else
return T_NUMBER (t_int_new_with_value (T_INT(self)->value / i));
} else {
g_object_get (T_DOUBLE (other), "value", &d, NULL);
if (d == 0) {
g_signal_emit_by_name (self, "div-by-zero");
return NULL;
} else
return T_NUMBER (t_int_new_with_value (T_INT(self)->value / (int) d));
}
}
static TNumber *
t_int_uminus (TNumber *self) {
g_return_val_if_fail (T_IS_INT (self), NULL);
return T_NUMBER (t_int_new_with_value (- T_INT(self)->value));
}
static char *
t_int_to_s (TNumber *self) {
g_return_val_if_fail (T_IS_INT (self), NULL);
int i;
g_object_get (T_INT (self), "value", &i, NULL);
return g_strdup_printf ("%d", i);
}
static void
t_int_class_init (TIntClass *class) {
TNumberClass *tnumber_class = T_NUMBER_CLASS (class);
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
/* override virtual functions */
tnumber_class->add = t_int_add;
tnumber_class->sub = t_int_sub;
tnumber_class->mul = t_int_mul;
tnumber_class->div = t_int_div;
tnumber_class->uminus = t_int_uminus;
tnumber_class->to_s = t_int_to_s;
gobject_class->set_property = t_int_set_property;
gobject_class->get_property = t_int_get_property;
int_property = g_param_spec_int ("value", "val", "Integer value", G_MININT, G_MAXINT, 0, G_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_INT, int_property);
}
TInt *
t_int_new_with_value (int value) {
TInt *i;
i = g_object_new (T_TYPE_INT, "value", value, NULL);
return i;
}
TInt *
t_int_new (void) {
TInt *i;
i = g_object_new (T_TYPE_INT, NULL);
return i;
}
TDouble object
我们对TDouble进行重构,以增加其功能,使TDouble类型的数据能够使用TNumber的函数进行基础运算。
/*tdouble.h*/
#pragma once
#include <glib-object.h>
#define T_TYPE_DOUBLE (t_double_get_type ())
G_DECLARE_FINAL_TYPE (TDouble, t_double, T, DOUBLE, TNumber)
/* create a new TDouble instance */
TDouble *
t_double_new_with_value (double value);
TDouble *
t_double_new (void);
/*tdouble.c*/
#include "tnumber.h"
#include "tdouble.h"
#include "tint.h"
#define PROP_DOUBLE 1
static GParamSpec *double_property = NULL;
struct _TDouble {
TNumber parent;
double value;
};
G_DEFINE_TYPE (TDouble, t_double, T_TYPE_NUMBER)
static void
t_double_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) {
TDouble *self = T_DOUBLE (object);
if (property_id == PROP_DOUBLE) {
self->value = g_value_get_double (value);
} else
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
t_double_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) {
TDouble *self = T_DOUBLE (object);
if (property_id == PROP_DOUBLE)
g_value_set_double (value, self->value);
else
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
t_double_init (TDouble *self) {
}
/* arithmetic operator */
/* These operators create a new instance and return a pointer to it. */
#define t_double_binary_op(op) \
int i; \
double d; \
if (T_IS_INT (other)) { \
g_object_get (T_INT (other), "value", &i, NULL); \
return T_NUMBER (t_double_new_with_value (T_DOUBLE(self)->value op (double) i)); \
} else { \
g_object_get (T_DOUBLE (other), "value", &d, NULL); \
return T_NUMBER (t_double_new_with_value (T_DOUBLE(self)->value op d)); \
}
static TNumber *
t_double_add (TNumber *self, TNumber *other) {
g_return_val_if_fail (T_IS_DOUBLE (self), NULL);
t_double_binary_op (+)
}
static TNumber *
t_double_sub (TNumber *self, TNumber *other) {
g_return_val_if_fail (T_IS_DOUBLE (self), NULL);
t_double_binary_op (-)
}
static TNumber *
t_double_mul (TNumber *self, TNumber *other) {
g_return_val_if_fail (T_IS_DOUBLE (self), NULL);
t_double_binary_op (*)
}
static TNumber *
t_double_div (TNumber *self, TNumber *other) {
g_return_val_if_fail (T_IS_DOUBLE (self), NULL);
int i;
double d;
if (T_IS_INT (other)) {
g_object_get (T_INT (other), "value", &i, NULL);
if (i == 0) {
g_signal_emit_by_name (self, "div-by-zero");
return NULL;
} else
return T_NUMBER (t_double_new_with_value (T_DOUBLE(self)->value / (double) i));
} else {
g_object_get (T_DOUBLE (other), "value", &d, NULL);
if (d == 0) {
g_signal_emit_by_name (self, "div-by-zero");
return NULL;
} else
return T_NUMBER (t_double_new_with_value (T_DOUBLE(self)->value / d));
}
}
static TNumber *
t_double_uminus (TNumber *self) {
g_return_val_if_fail (T_IS_DOUBLE (self), NULL);
return T_NUMBER (t_double_new_with_value (- T_DOUBLE(self)->value));
}
static char *
t_double_to_s (TNumber *self) {
g_return_val_if_fail (T_IS_DOUBLE (self), NULL);
double d;
g_object_get (T_DOUBLE (self), "value", &d, NULL);
return g_strdup_printf ("%lf", d);
}
static void
t_double_class_init (TDoubleClass *class) {
TNumberClass *tnumber_class = T_NUMBER_CLASS (class);
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
/* override virtual functions */
tnumber_class->add = t_double_add;
tnumber_class->sub = t_double_sub;
tnumber_class->mul = t_double_mul;
tnumber_class->div = t_double_div;
tnumber_class->uminus = t_double_uminus;
tnumber_class->to_s = t_double_to_s;
gobject_class->set_property = t_double_set_property;
gobject_class->get_property = t_double_get_property;
double_property = g_param_spec_double ("value", "val", "Double value", -G_MAXDOUBLE, G_MAXDOUBLE, 0, G_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_DOUBLE, double_property);
}
TDouble *
t_double_new_with_value (double value) {
TDouble *d;
d = g_object_new (T_TYPE_DOUBLE, "value", value, NULL);
return d;
}
TDouble *
t_double_new (void) {
TDouble *d;
d = g_object_new (T_TYPE_DOUBLE, NULL);
return d;
}
测试
完成对TInt、TDouble类型的定义,现在,看看这两个类型在应用程序中应该如何使用。
#include <glib-object.h>
#include "tnumber.h"
#include "tint.h"
#include "tdouble.h"
static void
notify_cb (GObject *gobject, GParamSpec *pspec, gpointer user_data) {
const char *name;
int i;
double d;
name = g_param_spec_get_name (pspec);
if (T_IS_INT (gobject) && strcmp (name, "value") == 0) {
g_object_get (T_INT (gobject), "value", &i, NULL);
g_print ("Property \"%s\" is set to %d.\n", name, i);
} else if (T_IS_DOUBLE (gobject) && strcmp (name, "value") == 0) {
g_object_get (T_DOUBLE (gobject), "value", &d, NULL);
g_print ("Property \"%s\" is set to %lf.\n", name, d);
}
}
int
main (int argc, char **argv) {
TNumber *i, *d, *num;
char *si, *sd, *snum;
i = T_NUMBER (t_int_new ());
d = T_NUMBER (t_double_new ());
g_signal_connect (G_OBJECT (i), "notify::value", G_CALLBACK (notify_cb), NULL);
g_signal_connect (G_OBJECT (d), "notify::value", G_CALLBACK (notify_cb), NULL);
g_object_set (T_INT (i), "value", 100, NULL);
g_object_set (T_DOUBLE (d), "value", 12.345, NULL);
num = t_number_add (i, d);
si = t_number_to_s (i);
sd = t_number_to_s (d);
snum = t_number_to_s (num);
g_print ("%s + %s is %s.\n", si, sd, snum);
g_object_unref (num);
g_free (snum);
num = t_number_add (d, i);
snum = t_number_to_s (num);
g_print ("%s + %s is %s.\n", sd, si, snum);
g_object_unref (num);
g_free (sd);
g_free (snum);
g_object_set (T_DOUBLE (d), "value", 0.0, NULL);
sd = t_number_to_s (d);
if ((num = t_number_div(i, d)) != NULL) {
snum = t_number_to_s (num);
g_print ("%s / %s is %s.\n", si, sd, snum);
g_object_unref (num);
g_free (snum);
}
g_object_unref (i);
g_object_unref (d);
g_free (si);
g_free (sd);
return 0;
}
程序运行结果如下:
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.
有趣的是,两次加法的结果不同。这是为什么呢?TInt与TDouble都对TNumber 的add 函数进行了覆盖,那么当使用两个不同类型的数据调用加法函数时,应该使用哪个类型的add函数呢,这就取决于t_number_add的实现了。