# 05 - 类与数据布局 Viper 中的 `class` 有两种用途:定义**数据布局**(结构体/联合体/枚举,本章内容),以及定义**面向对象的对象类型**(见 [06-oop.md](06-oop.md))。 ## 结构体定义 普通 `class` 定义编译为 LLVM 结构体类型: ```python class point: x: t.CInt y: t.CInt ``` 在 C 中: ```c struct point { int x; int y; }; ``` 在 LLVM IR 中: ```llvm %".point" = type { i32, i32 } ``` ### 带属性的结构体 ```python @c.Attribute(t.attr.packed) class idt_entry: base_low: t.CUInt16T selector: t.CUInt16T ist_attr: t.CUInt8T type_attr: t.CUInt8T base_middle: t.CUInt16T base_high: t.CUInt32T reserved1: t.CUInt32T ``` ### 结构体数组 ```python idt: list[idt_entry, 256] = [] ``` ### 结构体指针成员 ```python class node: value: t.CInt next: node | t.CPtr # 指向自身的指针(有向图链表) ``` ### 结构体实例化与初始化 ```python entry: idt_entry = idt_entry() ``` 或带构造函数: ```python mousepoint = Point(x=1, y=2) ``` 乱序传参或带参传参对于结构体的初始化同样有效: ```python entry: idt_entry = idt_entry(selector=0x08, base_low=0x0000) ``` 也可以给结构体成员指定默认值: ```python class config: width: t.CInt = 640 height: t.CInt = 480 depth: t.CInt = 32 ``` 使用 `c.Addr` 获取实例指针: ```python obj_ptr: MyClass | t.CPtr = c.Addr(MyClass(arg1, arg2)) ``` ### 匿名结构体成员(❌) > WARNING: **警告:** 由于 Viper 在声明结构体,枚举等处不需要关键字,因此 `t.Anonymouse` 是不需要使用的。此关键字不再维护,强行使用可能导致未定义行为。 继承 `t.Anonymous` 的类作为匿名成员嵌入: ```python class header(t.Anonymous): magic: t.CUInt32T version: t.CUInt32T class packet: header # 匿名嵌入,可直接访问 packet.magic length: t.CUInt32T ``` ### 字节序标记 > WARNING: **警告:** 此关键字是实验性的。未经充分测试,不当使用可能导致未定义行为。 使用 `t.BigEndian` 或 `t.LittleEndian` 标记成员的字节序: ```python class network_packet: length: t.CUInt16T | t.BigEndian # 大端序存储 type: t.CUInt8T # 默认小端序 ``` ## 联合体 继承 `t.CUnion` 定义联合体: ```python class value(t.CUnion): ival: t.CInt fval: t.CFloat pval: t.CVoid | t.CPtr ``` 等价 C 代码: ```c union value { int ival; float fval; void* pval; }; ``` ## 枚举 继承 `t.CEnum` 定义枚举: ```python class color(t.CEnum): RED: t.CEnum = 0 GREEN: t.CEnum = 1 BLUE: t.CEnum = 2 ``` 等价 C 代码: ```c enum color { RED = 0, GREEN = 1, BLUE = 2 }; ``` 枚举成员可直接使用名称: ```python c: t.CInt = color.RED ``` ## REnum(标签联合体) `t.CEnum` 是普通枚举(整数常量集合),`t.REnum` 是**标签联合体**(tagged union / Rust enum 风格)。每个变体可携带不同的 payload,通过 `match` 模式匹配分派。 ### 适用场景 | 类型 | 变体数 | 通用字段 | 访问模式 | 适用 | |------|--------|---------|---------|------| | `t.CEnum` | 任意 | 无 | 整数比较 | 状态码、opcode、token 类型 | | `t.REnum` | 少(~10) | 无 | match 分派 | 类型系统、AST 少变体场景 | | 胖节点(如 AST) | 多(70+) | 有 | vtype + 直接属性 | AST 节点(见 [includes/ast](../includes/ast)) | ### 定义 ```python class LLVMType(t.REnum): class Int: # 变体 Int,携带 payload Bits: t.CInt class Ptr: # 变体 Ptr,携带 payload Pointee: LLVMType | t.CPtr # 自引用用指针 class Void: # 变体 Void,无 payload pass ``` 布局为 `{ i32 __tag, }`,所有变体共享最大变体的 payload 空间。 ### 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.py](../includes/llvmlite/__types.py) 的 `LLVMType` 实现。 ## Typedef 使用 `t.CTypedef` 创建类型别名: ```python irq_handler_t: t.CTypedef | t.Callable[[], t.CInt] # typedef int (*irq_handler_t)(); ``` 或通过注解赋值: ```python MyStruct: t.CTypedef = original_struct # typedef struct original_struct MyStruct; ``` 更推荐通过后者的形式,前者编译器不一定能够识别到注解是对 `t.CTypedef` 的,还是后面的注解的,而且容易触发未定义行为。 ## 泛型类(PEP 695 语法) TransPyC 支持 PEP 695 泛型语法 `class Name[T]:`,定义类型参数化的类。 ### 基本泛型类 ```python @t.NoVTable class GSListNode[T]: Next: T | t.CPtr @t.NoVTable class GSList[T]: Head: T | t.CPtr Tail: T | t.CPtr Count: t.CSizeT def append(self, node: T): ... ``` ### 递归泛型继承 泛型类可递归引用自身作为类型参数,实现强类型自引用结构(无需转型): ```python @t.NoVTable class GNode(GSListNode[GNode]): # 递归泛型继承 value: t.CInt # 此时 GNode.Next: GNode|CPtr —— 强类型,零转型 ``` 这是 C(`void*` + cast)、C++(模板继承但无 `@t.NoVTable` 精细控制)、Rust(无继承,需 trait bound)均无法如此简洁表达的能力。 ### 实例化 ```python lst: GSList[GNode] | t.CPtr = GSList[GNode]() node: GNode | t.CPtr = GNode() lst.append(node) # 类型安全:append 只接受 GNode ``` 详见 [includes/linkedlist.py](../includes/linkedlist.py) 和 [includes/vector.py](../includes/vector.py)。 ## 位域 使用 `t.Bit(n)` 定义位域成员: ```python class flags: a: t.CUInt32T | t.Bit[1] b: t.CUInt32T | t.Bit[3] c: t.CUInt32T | t.Bit[4] ``` 注意:如果存入超出位域宽度的值,会被截断。例如 `a` 为 1 位,存入 2(二进制 `10`)会被截断为 0。