qemu对象模型——QOM实现分析
——lvyilong316
QEMU提供了一套面向对象编程的模型——QOM,即QEMU Object Module,几乎所有的设备如CPU、内存、总线等都是利用这一面向对象的模型来实现的。QOM模型的实现代码位于qom/文件夹下的文件中。
那么qemu为什么要引入QOM这个概念呢?首先,qemu作为一个纯软件虚拟化的虚拟机,需要模拟各种硬件,引入QOM能够对这些各种各样的硬件进行抽象,提取共性作为基类,特性作为子类;其次我们知道linux设备驱动模型中有设备、驱动、总线的概念,qemu为了模拟这些关系也需要借助于QOM。
QOM模型的数据结构
QOM中只要的数据结构有ObjectClass、Object、TypeImpl、InterfaceClass,知道这些数据结构基本就能清楚QOM的实现方式了。首先,我们要暂时抛弃传统高级语言的面向对象实现,因为传统的class和object和QOM中的概念稍有不同。
l TypeImpl
正如其名称所示,这个结构表示一种类型(TYPE),每种ObjectClass都有其对应的type。看到这里有人可能有些迷糊,class本身不就表示一种类型吗?没错,但前面已经说过不要和高级语言中的概念混淆,如果非要类比,可以把这里的TypeImpl理解为传统意义的class。
-
struct TypeImpl
-
{
-
const char *name;
-
-
size_t class_size; /*该数据类型所代表的类的大小*/
-
size_t instance_size; /*该数据类型产生的对象的大小*/
-
-
/*类的 Constructor & Destructor*/
-
void (*class_init)(ObjectClass *klass, void *data);
-
void (*class_base_init)(ObjectClass *klass, void *data);
-
void (*class_finalize)(ObjectClass *klass, void *data);
-
-
void *class_data;
-
-
/*实例的Contructor & Destructor*/
-
void (*instance_init)(Object *obj);
-
void (*instance_post_init)(Object *obj);
-
void (*instance_finalize)(Object *obj);
-
-
bool abstract; /*表示类是否是抽象类*/
-
-
const char *parent; /*父类的名字*/
-
TypeImpl *parent_type; /*指向父类TypeImpl的指针*/
-
-
ObjectClass *class; /*该类型对应的类的指针*/
-
-
int num_interfaces; /*所实现的接口的数量*/
-
InterfaceImpl interfaces[MAX_INTERFACES];
-
};
其中InterfaceImpl的定义如下,只是一个类型的名字
-
struct InterfaceImpl
-
{
-
const char *typename;
-
};
首先TypeImpl本身作为一种类型(Class),有其继承关系,这些关系表现在parent和parent_type成员,parent_type指向其父TypeImpl;当然和传统面向对象意义,TypeImpl也可以实现接口,其中interfaces记录其实现的接口名称;
其次我们看到TypeImpl主要分为两部分,一部分是class的Constructor
& Destructor,另一部分是object的Constructor & Destructor。注意这里的class指的是ObjectClass或其衍生类,不是传统意义上的class。我们知道传统意义上的class中有数据成员和方法,对应到QOM中,ObjectClass中存放的就是class中的方法,而object中存放的数据成员。
最后,说到TypeImpl,不得不提TypeInfo这个数据结构。这个结构是用户用来创建一个TypeImpl使用的。用户定义了一个TypeInfo,然后调用type_register(TypeInfo )或者type_register_static(TypeInfo )函数,就会生成相应的TypeImpl实例,将这个TypeInfo注册到全局的TypeImpl的hash表中。
-
/*TypeInfo的属性与TypeImpl的属性对应,实际上qemu就是通过用户提供的TypeInfo创建的TypeImpl的对象*/
-
struct TypeInfo
-
{
-
const char *name;
-
const char *parent;
-
-
size_t instance_size;
-
void (*instance_init)(Object *obj);
-
void (*instance_post_init)(Object *obj);
-
void (*instance_finalize)(Object *obj);
-
-
bool abstract;
-
size_t class_size;
-
-
void (*class_init)(ObjectClass *klass, void *data);
-
void (*class_base_init)(ObjectClass *klass, void *data);
-
void (*class_finalize)(ObjectClass *klass, void *data);
-
void *class_data;
-
-
InterfaceInfo *interfaces;
-
};
l ObjectClass
-
typedef struct TypeImpl *Type;
-
-
struct ObjectClass
-
{
-
/*< private >*/
-
Type type; /*指向该class所属的TypeImpl*/
-
GSList *interfaces;
-
-
const char *object_cast_cache[OBJECT_CLASS_CAST_CACHE];
-
const char *class_cast_cache[OBJECT_CLASS_CAST_CACHE];
-
-
ObjectUnparent *unparent;
-
};
ObjectClass是所有class的基类(这里说的class不是TypeImpl),如果你要实现一个class,就需要继承ObjectClass(所谓继承,就是将ObjectClass当做你的class的第一个成员)。如qemu中为为描述设备,定义了一个class——DeviceClass。
-
struct DeviceClass {
-
/*< private >*/
-
ObjectClass parent_class;
-
/*< public >*/
-
DECLARE_BITMAP(categories, DEVICE_CATEGORY_MAX);
-
const char *fw_name;
-
const char *desc;
-
Property *props;
-
bool cannot_instantiate_with_device_add_yet;
-
bool hotpluggable;
-
/* callbacks */
-
void (*reset)(DeviceState *dev);
-
DeviceRealize realize;
-
DeviceUnrealize unrealize;
-
/* device state */
-
const struct VMStateDescription *vmsd;
-
/* Private to qdev / bus. */
-
qdev_initfn init; /* TODO remove, once users are converted to realize */
-
qdev_event exit; /* TODO remove, once users are converted to unrealize */
-
const char *bus_type;
-
};
l Object
如果说ObjectClass是所有要定义class的基类,那么Object就是所有要定义object的基类。
-
struct Object
-
{
-
/*< private >*/
-
ObjectClass *class;
-
ObjectFree *free; /*当对象的引用为0时,清理垃圾的回调函数*/
-
GHashTable *properties; /*Hash表记录Object的属性*/
-
uint32_t ref; /*该对象的引用计数*/
-
Object *parent;
-
};
同样qemu中任何对象都需要继承Object,如qemu中为了描述设备定义的object——DeviceState。
-
struct DeviceState {
-
/*< private >*/
-
Object parent_obj;
-
/*< public >*/
-
-
const char *id;
-
bool realized; // 指示设备是否实现,然后调用callback
-
bool pending_deleted_event;
-
QemuOpts *opts;
-
int hotplugged;
-
BusState *parent_bus;
-
QLIST_HEAD(, NamedGPIOList) gpios;
-
QLIST_HEAD(, BusState) child_bus;
-
int num_child_bus;
-
int instance_id_alias;
-
int alias_required_for_version;
-
};
下面这张图可以简要说明ObjectClass、Object、TypeImpl、InterfaceClass之间的关系。通过这些数据结构中的指针,我们可以很方便地找到其他对应的数据结构。
用户如何通过QOM提供的数据结构表示一个类
在qemu中如何创建一个新的类(这里其实就是TypeImpl)。用户在创建类时要实现两个数据结构:描述类的数据结构(第一个属性必须是父类数据结构的实例,其祖先一定是ObjectClass)、描述类产生的实例的数据结构(第一个属性必须是父类对象的数据结构的实例,其祖先一定是Object),并且需要创建一个数据结构TypeInfo的实例,用来描述这种类的类型。通过创建TypeInfo,可以告诉qemu哪些信息呢?
我们再次查看一下TypeInfo数据结构,这个数据结构中包含了这个类型的父类(父TypeImpl)的名字,insatnce的size信息、class的size信息,instance的初始化和垃圾回收函数、class的初始化和垃圾回收函数。有了这些信息我们就可以:
(1) 为instance、class初始化第一个属性(instance的第一个属性必须是父类对象的数据结构的实例,class的一个属性必须是父类的数据结构的实例)
(2) 为instance、class分配合适的内存空间,这样我们就可以将一个Object的指针动态cast为我们需要的对象类型。
(3) 类的数据结构一般包含大量的函数指针,用于对对象进行操作。class的init函数可以将这些函数指针初始化。然后所有含有这个类数据结构指针的对象,都可以调用这些函数。
(4) 对象的数据结构一般包含了大量变量,是对象的一些属性。instance的init函数可以把这些属性初始化,相当于C++中的构造函数。
一个类可以实现多个接口,这些接口也是一个类,并且是抽象类,含有虚拟函数指针。
可以说,有了这个数据结构,就有了类的最基本的信息。(代码在include/qom/object.h中)
一个类是如何注册到QOM中的
QOM提供了type_register和type_register_static方法,用户可以调用这两个方法注册一个Type,需要传进去的参数就是TypeInfo的指针。(代码在qom/object.c中)
-
TypeImpl *type_register(const TypeInfo *info)
-
{
-
assert(info->parent);
-
return type_register_internal(info);
-
}
-
-
TypeImpl *type_register_static(const TypeInfo *info)
-
{
-
return type_register(info);
-
}
可以看到它们都调用了type_register_internal(TypeInfo*)函数。(代码在qom/object.c中)
l type_register_internal
-
static TypeImpl *type_register_internal(const TypeInfo *info)
-
{
-
TypeImpl *ti;
-
ti = type_new(info);
-
-
type_table_add(ti);
-
return ti;
-
}
type_new(TypeInfo*)函数将TypeInfo数据结构中的相关信息,赋值给TypeImpl数据结构中的属性。而type_table_add(TypeImpl *)函数将调用type_new()获得的TypeImpl指针保存到静态的hash表中。
l type_new
针对TypeInfo中的各种信息,给TypeImpl赋值:(代码在qom/object.c中)
-
static TypeImpl *type_new(const TypeInfo *info)
-
{
-
TypeImpl *ti = g_malloc0(sizeof(*ti));
-
int i;
-
-
g_assert(info->name != NULL);
-
-
if (type_table_lookup(info->name) != NULL) {
-
fprintf(stderr, "Registering `%s' which already exists\n", info->name);
-
abort();
-
}
-
-
ti->name = g_strdup(info->name);
-
ti->parent = g_strdup(info->parent);
-
-
ti->class_size = info->class_size;
-
ti->instance_size = info->instance_size;
-
-
ti->class_init = info->class_init;
-
ti->class_base_init = info->class_base_init;
-
ti->class_finalize = info->class_finalize;
-
ti->class_data = info->class_data;
-
-
ti->instance_init = info->instance_init;
-
ti->instance_post_init = info->instance_post_init;
-
ti->instance_finalize = info->instance_finalize;
-
-
ti->abstract = info->abstract;
-
-
for (i = 0; info->interfaces && info->interfaces[i].type; i++) {
-
ti->interfaces[i].typename = g_strdup(info->interfaces[i].type);
-
}
-
ti->num_interfaces = i;
-
-
return ti;
-
}
最后,通过type_table_add()保存到静态的hash表中(代码在qom/object.c中)
l type_table_add
-
static void type_table_add(TypeImpl *ti)
-
{
-
assert(!enumerating_types);
-
g_hash_table_insert(type_table_get(), (void *)ti->name, ti);
-
}
-
static GHashTable *type_table_get(void)
-
{
-
static GHashTable *type_table; //静态的类型的hash表,保存了全部被注册的类型
-
-
if (type_table == NULL) {
-
type_table = g_hash_table_new(g_str_hash, g_str_equal);
-
}
-
-
return type_table;
-
}
面向对象特性的实现
封装的实现
在考察QOM如何实现封装时,我们需要再次审视Object这个数据结构包含了哪些属性:(代码在/include/qom/object.h中)
-
struct Object
-
{
-
/*< private >*/
-
ObjectClass *class; //指向对应的类的数据结构的指针
-
ObjectFree *free; //当引用计数为0时调用
-
GHashTable *properties; //Object中的所有属性的hash表
-
uint32_t ref; //对象的引用计数
-
Object *parent; //指向父对象的指针
-
};
值得注意的是,这个Object的数据结构中的一行注释:“private”,它表示Object中的所有属性都是私有的,只能被类的内部函数访问、修改。这个数据结构中体现封装特性的私有变量是properties,它是一张hash表,这个变量包含了Object中的所有可访问和修改的数据、函数。这个Hash表中,每一个entry有对应的key和value,key是这个property的name,而value是一个ObjectProperty数据结构的指针,ObjectProperty的实现代码如下:(代码在include/qom/object.h中)
-
typedef struct ObjectProperty
-
{
-
gchar *name;
-
gchar *type;
-
gchar *description;
-
ObjectPropertyAccessor *get;
-
ObjectPropertyAccessor *set;
-
ObjectPropertyResolve *resolve;
-
ObjectPropertyRelease *release;
-
void *opaque;
-
} ObjectProperty;
可以看到,ObjectProperty包含了这个属性的名字、类型、描述、get和set的方法,解析(resolve)和释放(release)的方法,以及这个property特有的属性,用void *类型的指针来表示。
QOM使用这样一个数据结构,将对象的每个数据都保存在这样一个单元之中,从而让实现了封装。
当用户需要向Object中增加属性时,就可以直接调用object_property_add函数,这个函数向object的properties的hash表中插入了一个property。(代码在qom/object.c中)
l object_property_add
-
ObjectProperty * object_property_add(Object *obj, const char *name, const char *type,
-
ObjectPropertyAccessor *get,
-
ObjectPropertyAccessor *set,
-
ObjectPropertyRelease *release,
-
void *opaque, Error **errp)
-
{
-
ObjectProperty *prop;
-
size_t name_len = strlen(name);
-
-
if (name_len >= 3 && !memcmp(name + name_len - 3, "[*]", 4)) {
-
int i;
-
ObjectProperty *ret;
-
char *name_no_array = g_strdup(name);
-
-
name_no_array[name_len - 3] = '\0';
-
for (i = 0; ; ++i) {
-
char *full_name = g_strdup_printf("%s[%d]", name_no_array, i);
-
-
ret = object_property_add(obj, full_name, type, get, set,
-
release, opaque, NULL);
-
g_free(full_name);
-
if (ret) {
-
break;
-
}
-
}
-
g_free(name_no_array);
-
return ret;
-
}
-
-
if (g_hash_table_lookup(obj->properties, name) != NULL) {
-
error_setg(errp, "attempt to add duplicate property '%s'"
-
" to object (type '%s')", name,
-
object_get_typename(obj));
-
return NULL;
-
}
-
-
prop = g_malloc0(sizeof(*prop));
-
-
prop->name = g_strdup(name);
-
prop->type = g_strdup(type);
-
-
prop->get = get;
-
prop->set = set;
-
prop->release = release;
-
prop->opaque = opaque;
-
-
g_hash_table_insert(obj->properties, prop->name, prop);
-
return prop;
-
}
继承的实现
继承包括:实现继承、接口继承。实现继承是指使用基类的属性和方法而无需额外编码的能力; 接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力。
l 实现继承
我们创建一个新类时,需要实现两个数据结构:类的数据结构和对象的数据结构,由于类的数据结构中第一个属性就是父类的数据结构的实例,而对象的数据结构中,第一个属性就是父类对应的对象的实例。这样的实现方式,使得QOM天然地支持显现继承。
l 接口继承
接口在QOM中也有一个对应的类的数据结构:(代码在include/qom/object.h中)
-
struct InterfaceClass
-
{
-
ObjectClass parent_class; //它的父类就是ObjectClass
-
/*< private >*/
-
ObjectClass *concrete_class; //实现这个接口的类的指针
-
Type interface_type; //这个interface的类型(TypeImpl*指针),这个属性指示要实现的接口类型。
-
};
注意InterfaceClass也是一个ObjectClass。在QOM中一个类可以实现多个接口,也就是实现接口继承。在ObjectClass中与接口继承相关的属性就是interfaces,它在ObjectClass是一条链表,链表中的每个entry都是一个InterfaceClass的指针,最终对应到interface的TypeImpl数据结构的指针。我们可以通过给TypeImpl指针对应的类数据结构中的函数指针赋值,达到实现接口的目的。(代码在include/qom/include.h中)
多态的实现
多态是指同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。为了实现多态,QOM实现了一个非常重要的功能,就是动态cast。我们可以使用相关的函数,将一个Object的指针在运行时cast为子类对象的指针,可以将一个ObjectClass的指针在运行时cast为子类的指针。这样就可以调用子类中定义的函数指针来完成相应的功能。
动态cast检查的主要实现函数是:(代码在qom/object.c中)
-
ObjectClass *object_class_dynamic_cast(ObjectClass *class,
-
const char *typename)
-
{
-
ObjectClass *ret = NULL;
-
TypeImpl *target_type;
-
TypeImpl *type;
-
-
if (!class) {
-
return NULL;
-
}
-
-
type = class->type; //class的type指针总是指向这个类的TypeImpl,因此从这里获得这个类的类型
-
if (type->name == typename) {
-
return class;
-
}
-
-
target_type = type_get_by_name(typename);
-
if (!target_type) {
-
/* target class type unknown, so fail the cast */
-
return NULL;
-
}
-
//检查是否需要动态cast为接口类对象,然后检查每个接口
-
//主要检查目标类型是不是当前所指向的类型的祖先。
-
if (type->class->interfaces &&
-
type_is_ancestor(target_type, type_interface)) {
-
int found = 0;
-
GSList *i;
-
-
for (i = class->interfaces; i; i = i->next) {
-
ObjectClass *target_class = i->data;
-
-
if (type_is_ancestor(target_class->type, target_type)) {
-
ret = target_class;
-
found++;
-
}
-
}
-
-
/* The match was ambiguous, don't allow a cast */
-
if (found > 1) {
-
ret = NULL;
-
}
-
} else if (type_is_ancestor(type, target_type)) {
-
ret = class;
-
}
-
-
return ret;
-
}
一般object_class_dynamic_cast是在object_class_dynamic_cast_assert中调用的。
l object_class_dynamic_cast_assert
-
ObjectClass *object_class_dynamic_cast_assert(ObjectClass *class,
-
const char *typename,
-
const char *file, int line,
-
const char *func)
-
{
-
ObjectClass *ret;
-
-
if (!class || !class->interfaces) {
-
return class;
-
}
-
-
ret = object_class_dynamic_cast(class, typename);
-
if (!ret && class) {
-
fprintf(stderr, "%s:%d:%s: Object %p is not an instance of type %s\n",
-
file, line, func, class, typename);
-
abort();
-
}
-
-
return ret;
-
}
可以看到只有class和class->interfaces都不空时才会调用object_class_dynamic_cast,也就是当前class实现了接口的时候才需要object_class_dynamic_cast,否则直接返回当前class。
垃圾回收
在Object的数据结构中有一个变量:ref,这个变量用于对Object引用的计数,如果ref的值变为0,就意味着系统不会继续使用这个对象了,那么就可以对这个变量的内存空间等进行回收操作。
(1) 在TypeInfo中,我们可以定义instance_finalize,对于引用计数为0的Object进行垃圾回收操作。
(2) Object数据结构中有一个ObjectFree
*类型的函数指针free,当Object的引用计数为0时,就会调用这个函数进行垃圾回收。
(3) QOM自己实现了默认的垃圾回收操作:(代码在qom/object.c中)
-
//减少obj的引用计数,如果引用计数为0,将进行垃圾回收
-
void object_unref(Object *obj)
-
{
-
if (!obj) {
-
return;
-
}
-
g_assert_cmpint(obj->ref, >, 0);
-
-
/* parent always holds a reference to its children */
-
if (atomic_fetch_dec(&obj->ref) == 1) {
-
object_finalize(obj);
-
}
-
}
-
//obj的默认的析构函数
-
static void object_finalize(void *data)
-
{
-
Object *obj = data;
-
TypeImpl *ti = obj->class->type;
-
-
object_property_del_all(obj); //删除obj中的所有属性
-
object_deinit(obj, ti); //调用TypeImpl中finalize函数进行析构(请看后面)
-
-
g_assert_cmpint(obj->ref, ==, 0); //确定引用计数为0
-
if (obj->free) {
-
obj->free(obj); //如果obj有free函数指针,那么就会调用该函数
-
}
-
}
-
// 调用TypeImpl中的实例析构函数。如果存在父类,需要继续调用父类的实例析构函数
-
// 调用父类实例析构函数是因为一个对象数据结构中,第一个属性就是父类对象的实例
-
// 当我们需要对对象析构时,不仅要调用当前类的析构方法,也需要调用父类的析构方法
-
// 将对象中的第一个属性进行析构。
-
static void object_deinit(Object *obj, TypeImpl *type)
-
{
-
if (type->instance_finalize) {
-
type->instance_finalize(obj);
-
}
-
-
if (type_has_parent(type)) {
-
object_deinit(obj, type_get_parent(type));
-
}
-
}
怎样使用QOM模型创建新类型
使用QOM模型创建新类型时,需要用到以上的OjectClass、Object和TypeInfo。关于QOM的用法,在include/qom/object.h一开始就有一长串的注释,这一长串的注释说明了创建新类型时的各种用法。我们下面是对这些用法的简要说明。
1. 从最简单的开始,创建一个最小的type:
-
#include "qdev.h"
-
#define TYPE_MY_DEVICE "my-device"
-
-
// 用户需要定义新类型的类和对象的数据结构
-
// 由于不实现父类的虚拟函数,所以直接使用父类的数据结构作为子类的数据结构
-
// No new virtual functions: we can reuse the typedef for the
-
// superclass.
-
typedef DeviceClass MyDeviceClass; //定义class,直接就使用qemu本身的DeviceClass
-
typedef struct MyDevice //定义instance
-
{
-
DeviceState parent; //父对象必须是该对象数据结构的第一个属性,以便实现父对象向子对象的cast (DeviceState又是继承Object)
-
-
int reg0, reg1, reg2;
-
} MyDevice;
-
-
static const TypeInfo my_device_info = {
-
.name = TYPE_MY_DEVICE,
-
.parent = TYPE_DEVICE,
-
.instance_size = sizeof(MyDevice), //必须向系统说明对象的大小,以便系统为对象的实例分配内存
-
};
-
-
//向系统中注册这个新类型
-
static void my_device_register_types(void)
-
{
-
type_register_static(&my_device_info);
-
}
-
type_init(my_device_register_types)
2 . 为了方便编程,对于每个新类型,都会定义由ObjectClass动态cast到MyDeviceClass的方法,也会定义由Object动态cast到MyDevice的方法。以下涉及的函数OBJECT_GET_CLASS、OBJECT_CLASS_CHECK、OBJECT_CHECK都在include/qemu/object.h中定义。
-
#define MY_DEVICE_GET_CLASS(obj) \
-
OBJECT_GET_CLASS(MyDeviceClass, obj, TYPE_MY_DEVICE)
-
#define MY_DEVICE_CLASS(klass) \
-
OBJECT_CLASS_CHECK(MyDeviceClass, klass, TYPE_MY_DEVICE)
-
#define MY_DEVICE(obj) \
-
OBJECT_CHECK(MyDevice, obj, TYPE_MY_DEVICE)
3 . 如果我们在定义新类型中,实现了父类的虚拟方法,那么需要定义新的class的初始化函数,并且在TypeInfo数据结构中,给TypeInfo的class_init字段赋予该初始化函数的函数指针。
-
#include "qdev.h"
-
-
void my_device_class_init(ObjectClass *klass, void *class_data)
-
{
-
DeviceClass *dc = DEVICE_CLASS(klass);
-
dc->reset = my_device_reset;
-
}
-
-
static const TypeInfo my_device_info = {
-
.name = TYPE_MY_DEVICE,
-
.parent = TYPE_DEVICE,
-
.instance_size = sizeof(MyDevice),
-
.class_init = my_device_class_init, /*在类初始化时就会调用这个函数,将虚拟函数赋值*/
-
};
4 . 当我们需要从一个类创建一个派生类时,如果需要覆盖 类原有的虚拟方法,派生类中,可以增加相关的属性将类原有的虚拟函数指针保存,然后给虚拟函数赋予新的函数指针,保证父类原有的虚拟函数指针不会丢失。
-
typedef struct MyState MyState;
-
typedef void (*MyDoSomething)(MyState *obj);
-
-
typedef struct MyClass {
-
ObjectClass parent_class;
-
-
MyDoSomething do_something;
-
} MyClass;
-
-
static void my_do_something(MyState *obj)
-
{
-
// do something
-
}
-
-
static void my_class_init(ObjectClass *oc, void *data)
-
{
-
MyClass *mc = MY_CLASS(oc);
-
-
mc->do_something = my_do_something;
-
}
-
-
static const TypeInfo my_type_info = {
-
.name = TYPE_MY,
-
.parent = TYPE_OBJECT,
-
.instance_size = sizeof(MyState),
-
.class_size = sizeof(MyClass),
-
.class_init = my_class_init,
-
};
-
-
typedef struct DerivedClass {
-
MyClass parent_class;
-
-
MyDoSomething parent_do_something;
-
} DerivedClass;
-
-
static void derived_do_something(MyState *obj)
-
{
-
DerivedClass *dc = DERIVED_GET_CLASS(obj);
-
-
// do something here
-
dc->parent_do_something(obj);
-
// do something else here
-
}
-
-
static void derived_class_init(ObjectClass *oc, void *data)
-
{
-
MyClass *mc = MY_CLASS(oc);
-
DerivedClass *dc = DERIVED_CLASS(oc);
-
-
dc->parent_do_something = mc->do_something;
-
mc->do_something = derived_do_something;
-
}
-
-
static const TypeInfo derived_type_info = {
-
.name = TYPE_DERIVED,
-
.parent = TYPE_MY,
-
.class_size = sizeof(DerivedClass),
-
.class_init = derived_class_init,
-
};