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