7.2 KiB
05 - 类与数据布局
Viper 中的 class 有两种用途:定义数据布局(结构体/联合体/枚举,本章内容),以及定义面向对象的对象类型(见 06-oop.md)。类型书写推荐用短名别名(int/uchar/uint64/bool…)与 T*,详见 02-type-system.md 类型书写规范。
结构体定义
普通 class 定义编译为 LLVM 结构体类型:
class Point:
x: int
y: int
在 C 中:
struct Point {
int x;
int y;
};
在 LLVM IR 中:
%"<SHA1>.Point" = type { i32, i32 }
带属性的结构体
用 __attribute__(packed)(推荐)或 @c.Attribute(t.attr.packed):
@__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]):
idt: [256]IdtEntry = []
结构体指针成员
class Node:
value: int
next: Node* # 指向自身的指针(有向图链表)
结构体实例化与初始化
entry: IdtEntry = IdtEntry()
或带(含乱序)构造参数:
mousepoint = Point(x=1, y=2)
entry2: IdtEntry = IdtEntry(selector=0x08, base_low=0x0000)
也可以给结构体成员指定默认值:
class Config:
width: int = 640
height: int = 480
depth: int = 32
用 & 获取实例指针:
obj_ptr: MyClass* = &MyClass(arg1, arg2)
匿名结构体成员(❌)
WARNING: 警告:
t.Anonymous已废弃。由于 Viper 在声明结构体,枚举等处不需要关键字,因此t.Anonymouse是不需要使用的。此关键字不再维护,强行使用可能导致未定义行为。
继承 t.Anonymous 的类作为匿名成员嵌入:
class Header(t.Anonymous):
magic: uint32
version: uint32
class Packet:
Header # 匿名嵌入,可直接访问 Packet.magic
length: uint32
字节序标记
待实现(尚无字节序处理代码)。
WARNING: 警告: 此关键字是实验性的。未经充分测试,不当使用可能导致未定义行为。
使用 t.BigEndian 或 t.LittleEndian 标记成员的字节序(仍需 |,属尚未对齐的少数语法之一):
class NetworkPacket:
length: uint16 | t.BigEndian # 大端序存储
type: uint8 # 默认小端序
联合体
用软关键字 union(等价 class X(t.CUnion):):
union Value:
ival: int
fval: float
pval: void*
等价 C 代码:
union Value {
int ival;
float fval;
void* pval;
};
枚举
用软关键字 enum(等价 class Color(t.CEnum):)。成员为裸标识符,默认编号 0..N、底层类型 i32;也支持逗号组、显式类型或显式编号:
enum Color:
RED, GREEN, BLUE # 裸名,自动编号 0,1,2
enum Status:
OK = 0 # 显式编号
FAIL: uint8 = 1 # 显式类型 + 编号
UNKNOWN # 续编号
等价 C 代码:
enum Color { RED = 0, GREEN = 1, BLUE = 2 };
枚举成员可直接使用名称:
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) |
定义
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(...):
ty: LLVMType = LLVMType.Int(32) # 构造 Int 变体,payload Bits = 32
kind() 是编译器内建:编译期翻译为「bitcast 到 __tag* + load」(不生成函数调用),返回当前变体的 tag;tag 类型取自 __tag 字段声明类型(当前 i32)。
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.vp 的 LLVMType 实现。
Typedef
用软关键字 typedef(等价 Name: t.CTypedef = ...)创建类型别名:
typedef IrqHandler = callable[[], int] # typedef int (*IrqHandler)();
typedef MyStruct = OriginalStruct
等价旧写法:
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]:,定义类型参数化的类。
基本泛型类
novirtual class GSListNode[T]:
Next: T*
novirtual class GSList[T]:
Head: T*
Tail: T*
Count: size_t
def append(self, node: T): ...
递归泛型继承
泛型类可递归引用自身作为类型参数,实现强类型自引用结构(无需转型):
novirtual class GNode(GSListNode[GNode]): # 递归泛型继承
value: int
# 此时 GNode.Next: GNode* —— 强类型,零转型
这是 C(void* + cast)、C++(模板继承但无 novirtual 精细控制)、Rust(无继承,需 trait bound)均无法如此简洁表达的能力。
实例化
lst: GSList[GNode]* = &GSList[GNode]()
node: GNode* = &GNode()
lst.append(node) # 类型安全:append 只接受 GNode
详见 includes/linkedlist.vp 和 includes/vector.vp。
位域
待实现(TPC/TPV 均无解析路径)。
使用 t.Bit(n) 定义位域成员(仍需 |):
class Flags:
a: uint32 | t.Bit[1]
b: uint32 | t.Bit[3]
c: uint32 | t.Bit[4]
注意:如果存入超出位域宽度的值,会被截断。例如 a 为 1 位,存入 2(二进制 10)会被截断为 0。