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

275 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
%"<SHA1>.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, <max_variant_payload> }`,所有变体共享最大变体的 payload 空间。
### match 分派
```python
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.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。