# 05 - 类与数据布局 Viper 中的 `class` 有两种用途:定义**数据布局**(结构体/联合体/枚举,本章内容),以及定义**面向对象的对象类型**(见 [06-oop.md](06-oop.md))。类型书写推荐用短名别名(`int`/`uchar`/`uint64`/`bool`…)与 `T*`,详见 [02-type-system.md 类型书写规范](02-type-system.md#类型书写规范新语法推荐)。 ## 结构体定义 普通 `class` 定义编译为 LLVM 结构体类型: ```python class Point: x: int y: int ``` 在 C 中: ```c struct Point { int x; int y; }; ``` 在 LLVM IR 中: ```llvm %".Point" = type { i32, i32 } ``` ### 带属性的结构体 用 `__attribute__(packed)`(推荐)或 `@c.Attribute(t.attr.packed)`: ```python @__attribute__(packed) class IdtEntry: base_low: uint16 selector: uint16 ist_attr: uint8 type_attr: uint8 base_middle: uint16 base_high: uint32 reserved1: uint32 ``` ### 结构体数组 定长数组推荐用前缀写法 `[N]T`(等价 `t.CArray[T, N]` / 软关键字 `array[T, N]`): ```python idt: [256]IdtEntry = [] ``` ### 结构体指针成员 ```python class Node: value: int next: Node* # 指向自身的指针(有向图链表) ``` ### 结构体实例化与初始化 ```python entry: IdtEntry = IdtEntry() ``` 或带(含乱序)构造参数: ```python mousepoint = Point(x=1, y=2) entry2: IdtEntry = IdtEntry(selector=0x08, base_low=0x0000) ``` 也可以给结构体成员指定默认值: ```python class Config: width: int = 640 height: int = 480 depth: int = 32 ``` 用 `&` 获取实例指针: ```python obj_ptr: MyClass* = &MyClass(arg1, arg2) ``` ### 匿名结构体成员(❌) > WARNING: **警告:** `t.Anonymous` 已废弃。由于 Viper 在声明结构体,枚举等处不需要关键字,因此 `t.Anonymouse` 是不需要使用的。此关键字不再维护,强行使用可能导致未定义行为。 继承 `t.Anonymous` 的类作为匿名成员嵌入: ```python class Header(t.Anonymous): magic: uint32 version: uint32 class Packet: Header # 匿名嵌入,可直接访问 Packet.magic length: uint32 ``` ### 字节序标记 > 待实现(尚无字节序处理代码)。 > WARNING: **警告:** 此关键字是实验性的。未经充分测试,不当使用可能导致未定义行为。 使用 `t.BigEndian` 或 `t.LittleEndian` 标记成员的字节序(**仍需 `|`**,属尚未对齐的少数语法之一): ```python class NetworkPacket: length: uint16 | t.BigEndian # 大端序存储 type: uint8 # 默认小端序 ``` ## `struct`(纯数据结构体) 用软关键字 `struct` 定义**纯数据结构体**。字段声明与布局规则和 `class` 相同,但语义更严格: ```python struct Point: x: int y: int ``` 与 `class` 的区别与规则: - 与 C 的 `struct` 一样,**只能表示数据类型**(不做“类”的行为); - **禁止 `virtual` / `novirtual`**(不涉及 vtable / 非多态继承); - **禁止在 `struct` 内定义 `def`**(不允许方法); - 允许在 `struct` 内**嵌套 `struct`**; - 允许在 **`renum` 内部**像 `class` 一样用 `struct` 作为变体载荷。 > **推荐**:**纯数据**(结构体 / 记录)优先用 `struct`;需要方法、多态、继承的才用 `class`。`class` 也能描述纯数据,但 `class` 更偏“类”,`struct` 更偏“数据”。 > > 说明:`struct` 关键字编译器侧正在实现,语法以本节为准。 ## 联合体 用软关键字 `union`(等价 `class X(t.CUnion):`)定义联合体: ```python union Value: ival: int fval: float pval: void* ``` 等价 C 代码: ```c union Value { int ival; float fval; void* pval; }; ``` ## 枚举 用软关键字 `enum`(等价 `class Color(t.CEnum):`)。成员为**裸标识符**,默认编号 0..N、底层类型 `i32`;也支持逗号组、显式类型或显式编号: ```python enum Color: RED, GREEN, BLUE # 裸名,自动编号 0,1,2 enum Status: OK = 0 # 显式编号 FAIL: uint8 = 1 # 显式类型 + 编号 UNKNOWN # 续编号 ``` 等价 C 代码: ```c enum Color { RED = 0, GREEN = 1, BLUE = 2 }; ``` 枚举成员可直接使用名称: ```python c: int = Color.RED ``` ## REnum(标签联合体) `t.CEnum` 是普通枚举(整数常量集合),用软关键字 `renum` 定义的是**标签联合体**(tagged union / Rust enum 风格)。每个变体可携带不同的 payload,通过 `match` 模式匹配分派。 ### 适用场景 | 类型 | 变体数 | 通用字段 | 访问模式 | 适用 | |------|--------|---------|---------|------| | `enum`(CEnum) | 任意 | 无 | 整数比较 | 状态码、opcode、token 类型 | | `renum`(REnum) | 少(~10) | 无 | match 分派 | 类型系统、AST 少变体场景 | | 胖节点(如 AST) | 多(70+) | 有 | vtype + 直接属性 | AST 节点(见 [includes/ast](../includes/ast)) | ### 定义 ```python renum LLVMType: class Int: # 变体 Int,携带 payload Bits: int class Ptr: # 变体 Ptr,携带 payload Pointee: LLVMType* # 自引用用指针 class Void: # 变体 Void,无 payload pass ``` 等价旧写法 `class LLVMType(t.REnum): …`。 布局为 `{ i32 __tag, _p1, _p2, ... }`:按字段位置逐槽取所有变体的最大尺寸(i32/i64),而非单一最大 payload 区。 ### 变体构造与 `kind()` 变体有两种书写:嵌套 `class Variant:` 带载荷,或 `Name = 值` 无载荷。构造用 `EnumType.Variant(...)`: ```python ty: LLVMType = LLVMType.Int(32) # 构造 Int 变体,payload Bits = 32 ``` `kind()` 是编译器内建:编译期翻译为「bitcast 到 `__tag*` + load」(不生成函数调用),返回当前变体的 tag;tag 类型取自 `__tag` 字段声明类型(当前 `i32`)。 ### match 分派 ```python match ty: case LLVMType.Int: # 位置绑定 payload:ty.Bits 可用 snprintf(buf, size, "i%d", ty.IntBits) case LLVMType.Ptr: # ty.PtrPointee 可用 TypePrint(buf, size, ty.PtrPointee) case LLVMType.Void: string.strcpy(buf, "void") ``` 详见 [includes/llvmlite/__types.vp](../includes/llvmlite/__types.vp) 的 `LLVMType` 实现。 ## Typedef 用软关键字 `typedef`(等价 `Name: t.CTypedef = ...`)创建类型别名: ```python typedef IrqHandler = callable[[], int] # typedef int (*IrqHandler)(); typedef MyStruct = OriginalStruct ``` 等价旧写法: ```python irq_handler_t: t.CTypedef | t.Callable[[], t.CInt] MyStruct2: t.CTypedef = original_struct ``` > `typedef T1 = T2` 与 `T1: t.CTypedef = T2` 完全等价,落到同一条注册 / 查找路径。 ## 泛型类(PEP 695 语法) Viper 支持 PEP 695 泛型语法 `class Name[T]:`,定义类型参数化的类。 ### 基本泛型类 ```python novirtual class GSListNode[T]: Next: T* novirtual class GSList[T]: Head: T* Tail: T* Count: size_t def append(self, node: T): ... ``` ### 递归泛型继承 泛型类可递归引用自身作为类型参数,实现强类型自引用结构(无需转型): ```python novirtual class GNode(GSListNode[GNode]): # 递归泛型继承 value: int # 此时 GNode.Next: GNode* —— 强类型,零转型 ``` 这是 C(`void*` + cast)、C++(模板继承但无 `novirtual` 精细控制)、Rust(无继承,需 trait bound)均无法如此简洁表达的能力。 ### 实例化 ```python lst: GSList[GNode]* = &GSList[GNode]() node: GNode* = &GNode() lst.append(node) # 类型安全:append 只接受 GNode ``` 详见 [includes/linkedlist.vp](../includes/linkedlist.vp) 和 [includes/vector.vp](../includes/vector.vp)。 ## 位域 > 待实现(TPC/TPV 均无解析路径)。 使用 `t.Bit(n)` 定义位域成员(**仍需 `|`**): ```python class Flags: a: uint32 | t.Bit[1] b: uint32 | t.Bit[3] c: uint32 | t.Bit[4] ``` 注意:如果存入超出位域宽度的值,会被截断。例如 `a` 为 1 位,存入 2(二进制 `10`)会被截断为 0。