c_yield.py 是一个源到源的转换器,可将包含 yield() 调用的 C 函数转换为可恢复的状态机(协程)。
- 将包含
yield()的函数转换为状态机 - 在 yield 之间保留局部变量
- 处理循环、条件语句和嵌套作用域
- 生成初始化函数和恢复函数
- 仅支持 C(由于 RAII 问题不支持 C++)
- Python 3.8+
- Python
libclang包(提供带有捆绑 libclang 二进制文件的clang.cindex模块)
安装 Python 包:
pip install libclang基本用法:
python c_yield.py input.c output.c带选项:
python c_yield.py --help示例:
python c_yield.py test.c transformed.c需要转换的函数必须满足:
- 返回类型:
void - 第一个参数: 指向结构体的指针(状态结构)
- 包含
yield()调用 - 不支持 C++(不支持析构函数和 RAII)
struct my_state {};
void my_func(struct my_state *s, int param) {
int local = param * 2;
printf("Start: %d\n", local);
yield();
local += 10;
printf("After: %d\n", local);
yield();
printf("Done\n");
}转换器生成:
- 扩展的状态结构,包含局部变量和 yield 索引
- 初始化函数 (
my_func),用于设置状态 - 恢复函数 (
my_func_resume),用于继续执行
struct my_state {
int param;
union {
struct {
int local;
} __scope_1;
};
int __Idx;
};
void my_func_resume(void *state_ptr) {
struct my_state *s = (struct my_state*)state_ptr;
switch (s->__Idx) {
case 0: break;
case -1: return;
case 1: s->__Idx = -1; goto __Label_0;
case 2: s->__Idx = -1; goto __Label_1;
}
(s->__scope_1.local) = (s->param) * 2;
printf("Start: %d\n", (s->__scope_1.local));
{ s->__Idx = 1; return; __Label_0:; }
(s->__scope_1.local) += 10;
printf("After: %d\n", (s->__scope_1.local));
{ s->__Idx = 2; return; __Label_1:; }
printf("Done\n");
}
void my_func(struct my_state *s, int param) {
s->param = param;
s->__Idx = 0;
}- 解析: 使用 libclang 解析 C 源代码,查找包含
yield()调用的函数 - 作用域分析: 识别局部变量及其相对于 yield 点的生命周期
- 变量提升: 将局部变量移动到状态结构中
- 状态机生成: 创建基于 switch 的分派和 goto 标签
- 代码重写: 用初始化函数和恢复函数替换原始函数
0: 初始状态(开始执行)1..N: yield 点索引(从特定 yield 恢复)-1: 最终状态(执行完成)
- 不支持 C++: 不支持 C++ 特性(构造函数、析构函数、异常、RAII)
- 变量地址: 获取局部变量的地址在转换后可能无法正常工作
- setjmp/longjmp: 与状态机方法不兼容
- 递归 Yield: 在嵌套函数调用中 yield 的函数需要谨慎设计
- 线程安全: 状态机本身不是线程安全的
struct my_state s = {};
my_func(&s, 42); // 初始化
while (s.__Idx != -1) {
my_func_resume(&s); // 恢复直到完成
// 处理中间结果
}- 生成器: 产生值序列
- 异步 I/O: 协作式调度的操作
- 游戏 AI: 有状态的行为树
- 解析器: 增量输入处理
- 协议处理器: 有状态的网络协议
项目包含 CMake 构建系统,便于编译和测试。
c_yield.py is a source-to-source transformer that converts C functions containing yield() calls into resumable state machines (coroutines).
- Converts functions with
yield()into state machines - Preserves local variables across yields
- Handles loops, conditionals, and nested scopes
- Generates initialization and resume functions
- Supports C only (no C++ due to RAII issues)
- Python 3.8+
- Python
libclangpackage (providesclang.cindexmodule with bundled libclang binaries)
Install the Python package:
pip install libclangBasic usage:
python c_yield.py input.c output.cWith options:
python c_yield.py --helpExample:
python c_yield.py test.c transformed.cFunctions to be transformed must satisfy:
- Return type:
void - First parameter: Pointer to a struct (state structure)
- Contains
yield()calls - No C++ (destructors and RAII not supported)
struct my_state {};
void my_func(struct my_state *s, int param) {
int local = param * 2;
printf("Start: %d\n", local);
yield();
local += 10;
printf("After: %d\n", local);
yield();
printf("Done\n");
}The transformer generates:
- Expanded state structure with local variables and yield index
- Initialization function (
my_func) that sets up the state - Resume function (
my_func_resume) that continues execution
struct my_state {
int param;
union {
struct {
int local;
} __scope_1;
};
int __Idx;
};
void my_func_resume(void *state_ptr) {
struct my_state *s = (struct my_state*)state_ptr;
switch (s->__Idx) {
case 0: break;
case -1: return;
case 1: s->__Idx = -1; goto __Label_0;
case 2: s->__Idx = -1; goto __Label_1;
}
(s->__scope_1.local) = (s->param) * 2;
printf("Start: %d\n", (s->__scope_1.local));
{ s->__Idx = 1; return; __Label_0:; }
(s->__scope_1.local) += 10;
printf("After: %d\n", (s->__scope_1.local));
{ s->__Idx = 2; return; __Label_1:; }
printf("Done\n");
}
void my_func(struct my_state *s, int param) {
s->param = param;
s->__Idx = 0;
}- Parsing: Uses libclang to parse C source and find functions with
yield()calls - Scope Analysis: Identifies local variables and their lifetimes relative to yield points
- Variable Lifting: Moves local variables into the state structure
- State Machine Generation: Creates switch-based dispatch with goto labels
- Code Rewriting: Replaces original function with initialization + resume functions
0: Initial state (start execution)1..N: Yield point indices (resume from specific yield)-1: Final state (execution completed)
- No C++: C++ features (constructors, destructors, exceptions, RAII) are not supported
- Variable Address: Taking address of local variables may not work correctly after transformation
- setjmp/longjmp: Not compatible with the state machine approach
- Recursive Yields: Functions that yield within nested function calls require careful design
- Thread Safety: State machines are not inherently thread-safe
struct my_state s = {};
my_func(&s, 42); // Initialize
while (s.__Idx != -1) {
my_func_resume(&s); // Resume until done
// Process intermediate results
}- Generators: Produce sequences of values
- Async I/O: Cooperatively scheduled operations
- Game AI: Stateful behavior trees
- Parsers: Incremental input processing
- Protocol Handlers: Stateful network protocols
The project includes a CMake build system for easy compilation and testing.