310 lines
8.2 KiB
Markdown
310 lines
8.2 KiB
Markdown
# 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/VPC 均无解析路径)。
|
||
|
||
使用 `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。
|