5.2 KiB
04 - 函数定义与调用
Viper 的函数定义使用 Python 的 def 语法,通过类型注解指定参数类型和返回类型。
基本函数定义
def add(a: t.CInt, b: t.CInt) -> t.CInt:
return a + b
等价 C 代码:
int add(int a, int b) {
return a + b;
}
无返回值函数
def print_msg(msg: t.CConst | t.CChar | t.CPtr) -> t.CVoid:
serial.puts(msg)
等价 C 代码:
void print_msg(const char* msg) {
serial_puts(msg);
}
函数修饰符
通过返回类型的 | 组合添加函数修饰符:
导出函数
t.CExport 控制函数的符号可见性——标记为 t.CExport 的函数不加 SHA1 前缀,全局可见。详见 01-overview.md 中 t.CExport 深度解析。
def _start() -> t.CInt | t.CExport:
return 0
外部函数声明
使用 c.State 表示仅声明不定义:
def isr0() -> t.CExtern | t.CVoid | c.State: pass
def isr1() -> t.CExtern | t.CVoid | c.State: pass
等价 C 代码:
extern void isr0();
extern void isr1();
内联函数
t.CInline 标记函数为内联函数,编译器在调用点直接展开函数体。详见 01-overview.md 中 t.CInline 深度解析。
def fast_add(a: t.CInt, b: t.CInt) -> t.CInt | t.CInline:
return a + b
宏函数
当函数返回类型注解包含 t.CDefine 时,该函数被视为宏函数,不生成 LLVM IR 函数定义,在调用处内联展开。详见 01-overview.md 中 t.CDefine 函数。
def MAKE_FLAG(bit) -> t.CDefine:
return (1 << bit)
静态函数
def helper() -> t.CVoid | t.CStatic:
pass
函数属性装饰器
使用 @c.Attribute 添加 GCC __attribute__ 属性:
@c.Attribute(t.attr.section(".text.startup"), t.attr.aligned(16))
def _start() -> t.CInt:
return 0
等价 C 代码:
__attribute__((section(".text.startup"), aligned(16)))
int _start() {
return 0;
}
常用属性组合
@c.Attribute(t.attr.always_inline())
def hot_path() -> t.CInt:
return 0
@c.Attribute(t.attr.noreturn())
def panic(msg: t.CConst | t.CChar | t.CPtr) -> t.CVoid:
serial.puts(msg)
while True: pass
@c.Attribute(t.attr.packed)
LLVM 函数属性
在返回类型注解中通过 t.attr.llvm 指定 LLVM 属性:
def foo() -> t.CInt | t.attr.llvm.nobuiltin | t.attr.llvm.nounwind:
return 0
多返回值(CReturn)
使用 @c.CReturn 装饰器实现多返回值,通过指针参数实现:
@c.CReturn(t.CInt, t.CInt)
def divmod(a: t.CInt, b: t.CInt) -> t.CVoid:
q: t.CInt = a // b
r: t.CInt = a % b
return q, r
规则:
CReturn中的类型数量决定返回值个数- 自动为每个返回类型添加指针参数(
t.CInt→t.CInt | t.CPtr) - 参数名自动生成为
__ReturnValue0__、__ReturnValue1__等 - 调用处自动传参
&xxx - 必须使用左右值赋值,返回值不能直接传入函数。
指针参数
def read_data(buf: t.CChar | t.CPtr, size: t.CSizeT) -> t.CInt:
pass
等价 C 代码:
int read_data(char* buf, size_t size);
输出参数模式
使用 c.Addr 传递变量地址作为输出参数:
res: t.CInt = fat32.opendir("/", c.Addr(dp))
等价 C 代码:
int res = fat32_opendir("/", &dp);
后续会使用隐式左值获取来减少 c.Addr 的直接使用。
函数指针类型
使用 t.Callable 定义函数指针类型:
irq_handler_t: t.CTypedef | t.Callable[[], t.CInt]
等价 C 代码:
typedef int (*irq_handler_t)();
带参数的函数指针:
callback_t: t.Callable[[t.CInt, t.CInt], t.CVoid]
默认参数
Viper 支持函数默认参数值:
def create_window(x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt, title: t.CConst | str = "Window") -> t.CInt:
pass
return 语句
return 0 # return 0;
return # return; (void 函数)
return a, b # 多返回值(需配合 @c.CReturn)
全局变量声明
函数外的变量声明为全局变量,函数内通过 global 关键字访问:
BootInfo: bootinfo | t.CPtr
def init() -> t.CVoid:
global BootInfo
BootInfo = saved_rdi
支持 func-in-func 嵌套函数。
Call Func 函数调用
函数调用最好通过顺序调用来实现
def add(x: int, y: int, z: int):
return (x + y) * z
def main() -> int | t.CExport:
print(add(1, 2, 3)) # (1 + 2) * 3 = 9
这也是 C 中的标准实现方法,但实际上,你也可以乱序传参。
# 仍然用刚才的 add 举例
def main() -> int | t.CExport:
print(add(z=3, x=1, y=2)) # (1 + 2) * 3 = 9
乱序传参或带参传参对于结构体的初始化同样有效。同时,你也能够给函数指定默认值。
def add(x: int, y: int, z: int = 1):
return (x + y) * z
def main() -> int | t.CExport:
print(add(1, 2)) # (1 + 2) * 1 = 3
在结构体中也可以指定默认值。