# 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 @".ClassName.__before_init__"(%".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 @".GNode.append"(%".GNode"* %self, ...) { entry: %base = bitcast %".GNode"* %self to %".LinkedNode"* call void @".LinkedNode.append"(%".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 %".Base" = type { i8*, i32 } ``` 同时,编译器会生成全局 vtable 变量: ```llvm @".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__` | 右操作数幂运算 | 同上 |