# 类型、对象和接口之间的转换

因为用c语言实现了一个类面向对象的设备模型，在类型和实例之间的转换都需要手工实现。所以在代码中有几个重要的宏定义来完成这样的工作。

## obj -> class

对应的obj找到它的class。

```
#define OBJECT_GET_CLASS(class, obj, name) \
    OBJECT_CLASS_CHECK(class, object_get_class(OBJECT(obj)), name)
```

obj就是我们传入的对象实例， object\_get\_class就是获得obj->class这个成员，也就是对应的class。 class就是这个对象对应的class的类型，name呢就是这个class的名字。 对于没有intefaces的类型来说，这就是一个强制类型转换。也就是得到了obj对应的类型。

## ObjectClass -> 子Class

在上面的图中我们也看到，类型之间也有着父子关系。比如PCIDeviceClass就是DeviceClass的子类。

所以有时候在代码中我们需要从父类获得具体的子类。这个工作交给了：

```
#define OBJECT_CLASS_CHECK(class_type, class, name) \
    ((class_type *)object_class_dynamic_cast_assert(OBJECT_CLASS(class), (name), \
                                               __FILE__, __LINE__, __func__))
```

这个宏其实就是上面的那个宏，如果没有interfaces成员这也是一个强制类型转换。把class类型转换成class\_type。

但是因为Qemu自身的面向对象的模型，子类和父类的基地址相同所以能够通过强制转换来得到子类。

其实这么看，反过来转换也是可以的。

## Object -> 子Object

同上面的类型一样，object也有父子关系。所以当代码中需要从父对象转换到一个具体的子对象时就要用：

```
#define OBJECT_CHECK(type, obj, name) \
    ((type *)object_dynamic_cast_assert(OBJECT(obj), (name), \
                                        __FILE__, __LINE__, __func__))
```

对obj来讲就没有类型Class这么复杂了，在生产环境中纯粹就是强制类型转换，把obj类型转换到type类型。 因为按照Qemu的面向对象模型，父子obj的基地址是一样的。

## Class -> InterfaceClass

这个转换和Class到子Class的方式一样，也是

```
#define OBJECT_CLASS_CHECK(class_type, class, name) \
    ((class_type *)object_class_dynamic_cast_assert(OBJECT_CLASS(class), (name), \
                                               __FILE__, __LINE__, __func__))
```

但是因为这个Class有interfaces成员，所以会在interfaces链表上查找名字为name的接口。

如果有则返回对应name的接口。

## Object -> Interface

从上面的转换可以看出，如果想要从一个obj得到interface可以分成两步：

* obj -> class
* class -> interface

但是因为当Class有interface成员，且传入的name就是接口类型的名字时，obj可以直接转换成接口。

也就是用OBJECT\_CLASS\_CHECK可以直接转换得到。

## obj -> 虚obj

所以虚obj就是obj对象对应的Class类型的接口类型的对象。这么说还真有点绕，估计我自己过段时间来看也得花点时间回忆回忆。

之所以我叫它虚obj，是因为接口类型本身是没有对应的对象的。所以这个东西还挺神奇，没想到代码里还会这么用。

大家也一定觉得不信，那先举个例子：

```
#define MEMORY_DEVICE(obj) \
     INTERFACE_CHECK(MemoryDeviceState, (obj), TYPE_MEMORY_DEVICE)

typedef struct MemoryDeviceState MemoryDeviceState;
```

这个MemoryDeviceState就定义了这么一个类型，没有任何的成员。而它对应的Class类型是TYPE\_MEMORY\_DEVICE的接口类型。

而在代码中通常的用法还是要将这个虚obj再转换到接口类型。通常用法如下：

```
const MemoryDeviceState *md = MEMORY_DEVICE(obj);
const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(obj);
```

这个obj就是一个真实的obj对象，而md则是对应的虚obj。其实他们俩的指是一样的，都指向了同一个地址。

还是觉得很神奇，为什么代码要这么写呢？就不能直接传obj么？

我能想到的一个作用就是在debug选项打开时能够检测类型，而不至于传错参数。
