Gobject tutorial 三

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的实现了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值