C99 goto label地址实现C语言协程

来源:互联网 发布:msa是什么软件啊 编辑:程序博客网 时间:2024/05/18 22:50

以前用C语言中的switch case实现过一个简单的协程,并应用到了实际的项目里。文章在这里,C语言实现协程,最近了解到C99中,goto语句可以跳转到一个变量里,变量保存的是label的地址。于是瞬间想到,这个可以替换协程实现中的switch case的机制。


首先,说明一下什么是goto到标签地址。

int main() {     static void* p = &&label;     goto *p;     printf("before label\n");     label:     printf("after label\n");     return 0;}

  • &&label是语法,&&就是获得label的地址,并不是取地址在取地址。
  • goto *p 就是跳转到p指针,所指向的地址。
  • 如果p指向了的是不正确的地址,程序会运行时崩溃。
  • label地址需要用void*指针才存放。

那么,现在我们看怎么用这个机制来实现协程。之前需要阅读,C语言实现协程,重复内容不再叙述。只贴出改造的的部分。

/** * Construct goto label with line number */#define _ACoroutineLabel(line) label##line#define  ACoroutineLabel(line) _ACoroutineLabel(line)#define ACoroutineBegin()                      \    if (coroutine->step != NULL)               \    {                                          \        goto *coroutine->step;                 \    }                                          \    coroutine->state = coroutine_state_running \#define ACoroutineEnd() \    coroutine->state = coroutine_state_finish#define ACoroutineYieldFrame(waitFrameCount)               \    coroutine->waitValue    = waitFrameCount;              \    coroutine->curWaitValue = 0.0f;                        \    coroutine->waitType     = coroutine_wait_frame;        \    coroutine->step         = &&ACoroutineLabel(__LINE__); \    return;                                                \    ACoroutineLabel(__LINE__):#define ACoroutineYieldSecond(waitSecond)                  \    coroutine->waitValue    = waitSecond;                  \    coroutine->curWaitValue = 0.0f;                        \    coroutine->waitType     = coroutine_wait_second;       \    coroutine->step         = &&ACoroutineLabel(__LINE__); \    return;                                                \    ACoroutineLabel(__LINE__):#define ACoroutineYieldCoroutine(waitCoroutine)            \    coroutine->waitValue    = 0.0f;                        \    coroutine->curWaitValue = 0.0f;                        \    coroutine->waitType     = coroutine_wait_coroutine;    \    AArrayListAdd((waitCoroutine)->waits, coroutine);      \    coroutine->step         = &&ACoroutineLabel(__LINE__); \    return;                                                \    ACoroutineLabel(__LINE__):

  1.  首先,我们把coroutine->step改成void*类型,存储label标签地址。
  2.  ACoroutineLabel 是为了展开__Line__定义,构造label + 行号的标签名称。
  3. 接下来就是,在每次进入协程函数的时候,直接goto到step存储的标签上。
  4. 没有了switch case,就避免了switch嵌套的可能性错误。并且提升了效率。
  5. 协程函数用使用的,中断宏定义,就是退出+打标签,下次goto到标签处继续运行。

最后,看看使用的样子。

static void LoadingRun(Coroutine* coroutine){    static int progress = 0;//--------------------------------------------------------------------------------------------------    ACoroutineBegin();    for (; progress < progressSize; progress++)    {        ACoroutineYieldFrame(0);    }    ACoroutineYieldSecond(1.0f);    ACoroutineEnd();}static void OnReady(){    ACoroutine->StartCoroutine(LoadingRun);}



1 1
原创粉丝点击