Files
TransPyC/wiki/05-classes.md
2026-07-18 19:25:40 +08:00

6.1 KiB
Raw Blame History

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.BigEndiant.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:        # 位置绑定 payloadty.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.pyLLVMType 实现。

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 —— 强类型,零转型

这是 Cvoid* + 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.pyincludes/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。