# 06 - 面向对象与运算符重载

Viper 中的 `class` 有两种语义：**数据布局**（结构体/联合体/枚举，见 [05-classes.md](05-classes.md)）和**面向对象**。面向对象特性通过 `@t.Object` 装饰器启用，多态通过 `@t.CVTable` 装饰器启用，非多态继承（C++ 风格，无 vtable 开销）通过 `@t.NoVTable` 装饰器启用。

## `@t.Object` 面向对象

使用 `@t.Object` 装饰器启用面向对象特性，类将拥有成员方法、运算符重载、属性装饰器等 OOP 支持。编译器也会自动识别：如果结构体内有函数定义，会自动使用 `@t.Object` 行为，但在实际工程中更推荐明确标记，避免未定义行为。

### 基本定义

```python
@t.Object
class ViperKernel:
    BootInfo: bootinfo | t.CPtr
    VGA: vga._VGAScreenDriver | t.CPtr
    VGA_drv: vga._VGAScreenDriver

    def __init__(self):
        self.BootInfo = BootInfo
        serial.init()
        serial.puts("ViperOS VKernel Test\n")
```

避免在 `__init__` 中使用 `return`，虽然不会报错，但是你永远无法获得 `return` 的结果，如果需要提前结束代码流，`return` 也不失为一种办法。

### 成员方法

```python
@t.Object
class Sheet:
    id: t.CInt
    x: t.CInt
    y: t.CInt
    w: t.CInt
    h: t.CInt

    def __init__(self, x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt):
        self.x = x
        self.y = y
        self.w = w
        self.h = h

    def MoveTo(self, nx: t.CInt, ny: t.CInt):
        self.x = nx
        self.y = ny

    def Resize(self, nw: t.CInt, nh: t.CInt):
        self.w = nw
        self.h = nh
```

### 方法调用

```python
sheet: Sheet = Sheet(0, 0, 640, 480)
sheet.MoveTo(100, 100)
sheet.Resize(800, 600)
```

### 属性装饰器

支持 Python 的 `@property` 和 `@xxx.setter` 装饰器：

> `@xxx.setter` 和自定义装饰器正在试验阶段，尽可能不要使用

```python
@t.Object
class MyObj:
    _value: t.CInt

    @property
    def value(self) -> t.CInt:
        return self._value

    @value.setter
    def value(self, v: t.CInt):
        self._value = v
```

### 静态方法

```python
@staticmethod
def _yield():
    pass
```

### 对象指针与解引用

`@t.Object` 的实例通常通过指针操作：

```python
obj_ptr: MyClass | t.CPtr = c.Addr(MyClass())
obj: MyClass = c.Deref(obj_ptr)
```

### 成员访问

通过 `self` 访问成员变量和方法：

```python
def method(self) -> t.CVoid:
    self.x = 10
    y: t.CInt = self.y
    self.DoSomething()
```

### 内嵌对象

`@t.Object` 可以包含其他对象实例（非指针）：

```python
@t.Object
class Sheet:
    _surf_obj: gfx.Surface
    _renderer_obj: gfx.Renderer
    surf: gfx.Surface | t.CPtr
    renderer: gfx.Renderer | t.CPtr

    def __init__(self, fb: UINT32PTR, zb: float | t.CPtr, w: t.CInt, h: t.CInt):
        self._surf_obj = gfx.Surface(fb, zb, w, h)
        self.surf = c.Addr(self._surf_obj)
        self._renderer_obj = gfx.Renderer(self.surf)
        self.renderer = c.Addr(self._renderer_obj)
```

### `__before_init__` 方法

所有类（不限 `@t.Object`/`@t.CVTable`）自动生成 `__before_init__` 方法，在 `__init__` 之前**自动填充成员默认值**，并对 `@t.CVTable` 类初始化 vtable：

```llvm
define void @"<SHA1>.ClassName.__before_init__"(%"<SHA1>.ClassName"* %self) {
entry:
  ; 对 @t.CVTable 类：存储 vtable 指针到 slot 0
  ; 遍历所有成员：若有默认值则 store 默认值，否则 store 零值
  ret void
}
```

**职责**：
- 填充类定义中声明的成员默认值（如 `x: t.CInt = 0` 中的 `0`）
- 对无默认值的成员填充类型零值
- 对 `@t.CVTable` 类额外初始化 vtable 指针

**调用时机**（由编译器在构造时自动插入）：
1. `alloca` 栈分配后调用一次
2. 若存在 `__new__` 方法且返回堆指针，对堆指针再调用一次（alloca 的默认值已随栈帧丢弃，必须重新填充）
3. `__init__` 调用前

**就地构造**：用户也可手动调用 `__before_init__` 实现就地构造（placement new 语义），典型用法见 `mpool.alloc_buf`：

```python
buf: Buf | t.CPtr = pool.alloc(Buf.__sizeof__())
buf.__before_init__()          # 填充默认值
buf.__init__(args)             # 用户初始化逻辑
```

跨模块导入类时，导入侧也会声明 `__before_init__`，保证跨模块构造行为一致。

## `@t.NoVTable` 非多态继承

`@t.NoVTable` 启用 C++ 风格的非多态继承（对应 `struct A : B {}` 无 virtual）。与 `@t.CVTable` 的区别：

| 特性 | `@t.NoVTable` | `@t.CVTable` |
|------|---------------|--------------|
| vtable 指针 | ❌ 无 | ✅ 自动插入 slot 0 |
| 虚派发 | ❌ 无 | ✅ 支持 |
| 方法继承 | ✅ 生成包装函数转发 | ✅ 通过 vtable |
| 字段展平 | ✅ 父类字段前置嵌入 | ✅ 父类字段前置嵌入（vtable 之后） |
| 运行时开销 | 零 | 每次虚调用一次间接跳转 |

### 基本定义

```python
@t.NoVTable
class LinkedNode:
    next: "LinkedNode" | t.CPtr
    prev: "LinkedNode" | t.CPtr
    def append(self, node: "LinkedNode" | t.CPtr): ...

@t.NoVTable
class GNode(LinkedNode):    # 非多态继承
    value: t.CInt
```

子类 `GNode` 的结构体布局为 `{next, prev, value}`（父类字段前置，无 vtable 指针）。

### 方法继承机制

子类调用父类方法时，编译器通过 `_generate_inherited_method_wrappers` 自动生成包装函数：将 `self` bitcast 为父类指针后尾调用父类方法。例如 `gnode.append(child)` 会生成：

```llvm
define void @"<SHA1>.GNode.append"(%"<SHA1>.GNode"* %self, ...) {
entry:
  %base = bitcast %"<SHA1>.GNode"* %self to %"<SHA1>.LinkedNode"*
  call void @"<SHA1>.LinkedNode.append"(%"<SHA1>.LinkedNode"* %base, ...)
  ret void
}
```

### 跨模块继承

跨模块父类（如 `linkedlist.LinkedNode`）的 `@t.NoVTable` 标记会在导入时被检测并记录，子类继承时同样跳过 vtable 插入。

### 向下转型

父类指针→子类指针需显式转型（生成 bitcast IR）：

```python
root: Node = Node()                    # Node 继承 LinkedNode
child: LinkedNode | t.CPtr = root.child
# child 是 LinkedNode*，需要转回 Node*：
node: Node | t.CPtr = (Node | t.CPtr)(child)
```

隐式转型仅向上（子→父）。

## `@t.CVTable` 虚函数表与多态

> 尽可能避免使用 `self[0]` 来获取结构体成员，其不稳定，也不要手动操作虚表。

使用 `@t.CVTable` 装饰器可以启用虚函数表支持。启用后，结构体第一个成员自动插入 `i8*` 类型的 vtable 指针。`@t.CVTable` 的操作必须配合 `@t.Object`，但有时编译器会自动识别是否存在结构体内函数，自动使用 `@t.Object`。

### 基本定义

```python
@t.Object
@t.CVTable
class Base:
    def __init__(self):
        pass
    def method(self) -> t.CInt:
        return 0
```

在 LLVM IR 中，`@t.CVTable` 类的结构体类型自动插入 vtable 指针：

```llvm
%"<SHA1>.Base" = type { i8*, i32 }
```

同时，编译器会生成全局 vtable 变量：

```llvm
@"<SHA1>.Base_Vtable" = internal global [1 x i8*] zeroinitializer
```

### 继承与多态

虚表可以帮你使用继承语法实现多态：

```python
@t.Object
@t.CVTable
class Base:
    def __init__(self):
        pass
    def method(self) -> t.CInt:
        return 0

@t.Object
@t.CVTable
class M1(Base):
    def method(self) -> t.CInt:
        return 1
```

当 M1 被初始化时，VTable 会自动修改，这点和 C++ 相同，尽可能避免直接操作 VTable。

继承时，子类会自动继承父类的成员变量和方法。子类重写父类方法时，vtable 中对应位置的函数指针被子类方法替换，实现多态。

### VTable 运行时修改

如果需要临时构造一个额外的 `Base` 结构体，还可以使用：

```python
@t.Object
@t.CVTable
class Base:
    def __init__(self):
        pass
    def method(self) -> t.CInt:
        return 0

def __method1(self) -> t.CInt:
    return 1

def main() -> t.CInt | t.CExport:
    b: Base = Base()
    b.method = __method1
```

启用 `VTable` 的表中，允许直接修改内部函数为函数指针，在底层，此操作将修改 `VTable` 来达成目的。编译器会检测当前 vtable 是否与类 vtable 相同，如果相同则创建 vtable 的副本（memcpy），避免修改影响所有实例。

### 虚方法调度机制

虚方法调用的核心流程：

1. 从对象指针 GEP 获取第 0 个 slot（vtable 指针）
2. 加载 vtable 指针
3. bitcast 为 `[N x i8*]*` 类型
4. GEP 获取方法索引处的函数指针
5. 加载函数指针
6. bitcast 为正确的函数指针类型
7. 间接调用

## 继承

### 结构体成员继承

当子类继承父类（父类在 `Gen.class_members` 中存在）时：

- 父类的成员变量被继承到子类（排在子类成员之前）
- 父类的默认值也被继承
- 父类的方法被继承（重命名为 `子类名.方法名`）

```python
@t.Object
class Animal:
    name: list[t.CChar, 32]
    age: t.CInt

    def __init__(self, age: t.CInt):
        self.age = age

    def Speak(self) -> t.CVoid:
        serial.puts("...\n")

@t.Object
class Dog(Animal):
    breed: t.CInt

    def Speak(self) -> t.CVoid:
        serial.puts("Woof!\n")
```

### 异常类继承

Viper 支持异常类的继承，用于 `try/except` 的异常匹配：

```python
class MyError(Exception):
    pass

class SpecificError(MyError):
    pass
```

详见 [09-exceptions.md](09-exceptions.md)。

## 运算符重载

`@t.Object` 类支持运算符重载。编译器在遇到运算符时，首先检查左操作数是否为结构体类型，如果是则尝试调用对应的 dunder 方法。

### 算术运算符重载

| 运算符 | Dunder 方法 | 示例 |
|--------|------------|------|
| `+` | `__add__` | `a + b` → `a.__add__(b)` |
| `-` | `__sub__` | `a - b` → `a.__sub__(b)` |
| `*` | `__mul__` | `a * b` → `a.__mul__(b)` |
| `/` | `__truediv__` | `a / b` → `a.__truediv__(b)` |
| `//` | `__floordiv__` | `a // b` → `a.__floordiv__(b)` |
| `%` | `__mod__` | `a % b` → `a.__mod__(b)` |
| `**` | `__pow__` | `a ** b` → `a.__pow__(b)` |

示例：

```python
@t.Object
class Vec2:
    x: t.CFloat
    y: t.CFloat

    def __init__(self, x: t.CFloat, y: t.CFloat):
        self.x = x
        self.y = y

    def __add__(self, other: Vec2) -> Vec2:
        result: Vec2 = Vec2(0.0, 0.0)
        result.x = self.x + other.x
        result.y = self.y + other.y
        return result

    def __mul__(self, scalar: t.CFloat) -> Vec2:
        result: Vec2 = Vec2(0.0, 0.0)
        result.x = self.x * scalar
        result.y = self.y * scalar
        return result
```

### 增量赋值运算符重载

| 增量运算符 | 优先 Dunder | 回退 Dunder |
|-----------|------------|------------|
| `+=` | `__iadd__` | `__add__` |
| `-=` | `__isub__` | `__sub__` |
| `*=` | `__imul__` | `__mul__` |
| `/=` | `__itruediv__` | `__truediv__` |
| `//=` | `__ifloordiv__` | `__floordiv__` |
| `%=` | `__imod__` | `__mod__` |
| `**=` | `__ipow__` | `__pow__` |

如果未定义增量版本（如 `__iadd__`），编译器自动回退到普通版本（如 `__add__`）。

### 比较运算符重载

> **实验性**：以下比较运算符 dunder 方法在编译器中被识别，但尚未实现自动调度。定义它们不会报错，但比较运算不会自动调用。

| 运算符 | Dunder 方法 | 状态 |
|--------|------------|------|
| `==` | `__eq__` | 仅类型推断识别 |
| `!=` | `__ne__` | 仅类型推断识别 |
| `<` | `__lt__` | 仅类型推断识别 |
| `<=` | `__le__` | 仅类型推断识别 |
| `>` | `__gt__` | 仅类型推断识别 |
| `>=` | `__ge__` | 仅类型推断识别 |

### 反向运算符重载

> **实验性**：以下反向运算符 dunder 方法在编译器中被识别，但尚未实现自动调度。

| Dunder 方法 | 预期语义 | 状态 |
|------------|---------|------|
| `__radd__` | 右操作数加法 | 仅类型推断识别 |
| `__rsub__` | 右操作数减法 | 仅类型推断识别 |
| `__rmul__` | 右操作数乘法 | 仅类型推断识别 |
| `__rtruediv__` | 右操作数真除法 | 仅类型推断识别 |
| `__rfloordiv__` | 右操作数整除 | 仅类型推断识别 |
| `__rmod__` | 右操作数取模 | 仅类型推断识别 |
| `__rpow__` | 右操作数幂运算 | 仅类型推断识别 |

### 一元运算符重载

> **实验性**：以下一元运算符 dunder 方法在编译器中被识别，但尚未实现自动调度。

| 运算符 | Dunder 方法 | 状态 |
|--------|------------|------|
| `-a` | `__neg__` | 仅类型推断识别 |
| `+a` | `__pos__` | 仅类型推断识别 |

## 可调用对象 `__call__`

```python
@t.Object
class Counter:
    value: t.CInt

    def __init__(self):
        self.value = 0

    def __call__(self) -> t.CInt:
        self.value += 1
        return self.value

c: Counter = Counter()
x: t.CInt = c()   # x = 1
y: t.CInt = c()   # y = 2
```

## 下标访问重载

### `__getitem__` — 读取

```python
def __getitem__(self, key: t.CInt) -> t.CInt:
    return self.data[key]
```

### `__setitem__` — 赋值

```python
def __setitem__(self, key: t.CInt, value: t.CInt):
    self.data[key] = value
```

### `__delitem__` — 删除

```python
def __delitem__(self, key: t.CInt):
    pass
```

## 布尔转换 `__bool__`

```python
@t.Object
class Buffer:
    size: t.CInt
    data: list[t.CChar, 1024]

    def __bool__(self) -> t.CBool:
        return self.size > 0

buf: Buffer = Buffer()
if buf:
    serial.puts("buffer is not empty\n")
```

## 长度 `__len__`

```python
@t.Object
class Buffer:
    size: t.CInt

    def __len__(self) -> t.CInt:
        return self.size

buf: Buffer = Buffer()
n: t.CInt = len(buf)
```

## 字符串转换 `__str__`

```python
@t.Object
class Point:
    x: t.CInt
    y: t.CInt

    def __str__(self) -> t.CConst | t.CChar | t.CPtr:
        return "Point"

p: Point = Point(1, 2)
print(p)          # 调用 p.__str__()
```

## 绝对值 `__abs__`

```python
@t.Object
class SignedValue:
    val: t.CInt

    def __abs__(self) -> t.CInt:
        if self.val < 0:
            return -self.val
        return self.val

sv: SignedValue = SignedValue()
sv.val = -42
a: t.CInt = abs(sv)   # a = 42
```

## 迭代器协议

Viper 的迭代器协议与 Python 相同，但 `__next__` 的实现方式通过 `StopIteration` 异常来结束。

### `__iter__` 和 `__next__`

```python
@t.Object
class IntRange:
    start: t.CInt
    end: t.CInt
    current: t.CInt

    def __init__(self, start: t.CInt, end: t.CInt):
        self.start = start
        self.end = end
        self.current = start

    def __iter__(self):
        return self

    def __next__(self, stop_flag) -> t.CInt:
        if self.current >= self.end:
            raise StopIteration
        val: t.CInt = self.current
        self.current += 1
        return val
```

### for 循环使用

```python
r: IntRange = IntRange(0, 10)
for i in r:
    print(i)
```

编译器生成的等价逻辑：

```c
IntRange r = IntRange_init(0, 10);
IntRange iter = IntRange___iter__(&r);
while (1) {
    i1 stop_flag = 0;
    int i = IntRange___next__(&iter, &stop_flag);
    if (stop_flag == 1) break;
    print_int(i);
}
```

## 上下文管理器

`with` 语句要求对象实现 `__enter__` 和 `__exit__` 方法：

```python
@t.Object
class File:
    fd: t.CInt
    path: t.CConst | t.CChar | t.CPtr

    def __init__(self, path: t.CConst | t.CChar | t.CPtr, flags: t.CInt):
        self.path = path
        self.fd = -1

    def __enter__(self):
        self.fd = fat32.open(self.path, self.flags)
        return self

    def __exit__(self):
        fat32.close(self.fd)

    def read(self) -> t.CInt:
        return fat32.read(self.fd)
```

使用方式：

```python
with File("/test.txt", FA_READ) as f:
    data: t.CInt = f.read()
```

编译器会自动保证 `__exit__` 在 `with` 块结束时被调用。

## 析构 `__del__` / `__delete__`

```python
del obj        # 调用 obj.__del__() 或 obj.__delete__()
del arr[idx]   # 调用 arr.__delitem__(idx)
```

## Dunder 方法完整参考

### 已实现自动调度的 Dunder 方法

| Dunder 方法 | 触发语法 | 说明 |
|------------|---------|------|
| `__init__` | `ClassName(args)` | 构造函数 |
| `__before_init__` | 自动生成 | vtable 初始化，在 `__init__` 之前执行 |
| `__add__` | `a + b` | 加法 |
| `__sub__` | `a - b` | 减法 |
| `__mul__` | `a * b` | 乘法 |
| `__truediv__` | `a / b` | 真除法 |
| `__floordiv__` | `a // b` | 整除 |
| `__mod__` | `a % b` | 取模 |
| `__pow__` | `a ** b` | 幂运算 |
| `__iadd__` | `a += b` | 增量加法（回退到 `__add__`） |
| `__isub__` | `a -= b` | 增量减法（回退到 `__sub__`） |
| `__imul__` | `a *= b` | 增量乘法（回退到 `__mul__`） |
| `__itruediv__` | `a /= b` | 增量真除法（回退到 `__truediv__`） |
| `__ifloordiv__` | `a //= b` | 增量整除（回退到 `__floordiv__`） |
| `__imod__` | `a %= b` | 增量取模（回退到 `__mod__`） |
| `__ipow__` | `a **= b` | 增量幂运算（回退到 `__pow__`） |
| `__call__` | `obj(args)` | 可调用对象 |
| `__getitem__` | `obj[key]` | 下标读取 |
| `__setitem__` | `obj[key] = value` | 下标赋值 |
| `__delitem__` | `del obj[key]` | 下标删除 |
| `__bool__` | `if obj:` | 布尔转换 |
| `__len__` | `len(obj)` | 长度 |
| `__str__` | `str(obj)` / `print(obj)` | 字符串转换 |
| `__abs__` | `abs(obj)` | 绝对值 |
| `__iter__` | `for x in obj:` | 获取迭代器 |
| `__next__` | `for x in obj:` | 获取下一个值（标志位方式） |
| `__enter__` | `with obj as x:` | 上下文管理器进入 |
| `__exit__` | `with obj as x:` | 上下文管理器退出 |
| `__del__` | `del obj` | 析构 |
| `__delete__` | `del obj` | 删除描述符 |

### 声明支持但未实现自动调度的 Dunder 方法

| Dunder 方法 | 预期触发语法 | 当前状态 |
|------------|------------|---------|
| `__eq__` | `a == b` | 仅类型推断识别，比较运算不自动调度 |
| `__ne__` | `a != b` | 同上 |
| `__lt__` | `a < b` | 同上 |
| `__le__` | `a <= b` | 同上 |
| `__gt__` | `a > b` | 同上 |
| `__ge__` | `a >= b` | 同上 |
| `__neg__` | `-a` | 仅类型推断识别，一元负号不自动调度 |
| `__pos__` | `+a` | 同上 |
| `__radd__` | 右操作数加法 | 仅类型推断识别 |
| `__rsub__` | 右操作数减法 | 同上 |
| `__rmul__` | 右操作数乘法 | 同上 |
| `__rtruediv__` | 右操作数真除法 | 同上 |
| `__rfloordiv__` | 右操作数整除 | 同上 |
| `__rmod__` | 右操作数取模 | 同上 |
| `__rpow__` | 右操作数幂运算 | 同上 |
