Files
Viper_Wiki/05-classes.md
T

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