如何保存状态并返回深度 C 函数?

Posted

技术标签:

【中文标题】如何保存状态并返回深度 C 函数?【英文标题】:How to save state and return to a deep C function? 【发布时间】:2014-11-23 13:41:17 【问题描述】:

背景

我正在移植一个现有的 C 程序以使用 Emscripten 作为在线游戏工作。

问题在于 Emscripten 希望程序围绕一个每秒调用 60 次的函数来组织。这对于主游戏循环来说是可以的,除了有很多地方代码显示一组选项,然后等待按下一个键来选择选项。这表示为调用层次结构深处的函数,使用 getch() 来等待按键。我发现很难看出如何将其融入所需的 Emscripten 风格的函数,该函数运行然后完成。

问题

当代码调用了一个函数,它调用了一个函数,它调用了一个函数,有没有一种简单的方法可以保存调用堆栈的整个状态,以便我以后可以返回到同一个地方?

我尝试过的

    我目前使用的方法是设置一个全局状态变量来指示我当前的位置,并将堆栈上所有看起来很重要的东西写入静态变量。然后我从所有函数返回。要重新输入,我使用全局变量来决定调用哪个函数以及从保存的数据中重新加载哪些变量。但是,这涉及编写大量额外代码并且非常容易出错。

    我想知道为游戏逻辑使用线程并仅从 GUI 线程发送消息,但 Emscripten 中当前的 thread API 似乎要求我尝试将所有游戏数据复制到消息中,所以这感觉就像做更多的工作却收效甚微。

    Emscripten 支持 setjmp/longjmp,但据我了解,这只完成了一半的工作。我想我可以使用 longjmp 简单地从深层函数返回到上层,但我看不出我可以使用它稍后回到我原来的位置。

关于如何做到这一点有什么更好的想法吗?

【问题讨论】:

阅读更多关于continuations的信息;另见setcontext(3) 【参考方案1】:

您不能从调用堆栈返回并重新输入它。您只能进行更深入的调用才能仍然能够返回到当前状态。一旦函数返回,相同的堆栈(相同的物理内存位置)将被重复用于后续调用,并且值将被覆盖。

我不知道 Emscripten; getch() 包装器能否递归地驱动循环,直到按下一个键?

setjmp/longjmp 保存堆栈偏移量,但不保存堆栈上的值。它仅对从堆栈中弹出多个帧有用;这是最接近抛出异常的 C。

【讨论】:

如果我需要重复调​​用渲染函数,我认为您的建议会有所帮助,但是要求(据我了解 emscripten)是我需要按顺序从***函数返回让浏览器响应,所以我认为 getch 包装器没有帮助。 (也许我误解了你的建议?) 你理解正确,我就是这个意思,我对它的了解不够,知道它不起作用,对不起【参考方案2】:

您可以尝试使用 asyncify (https://github.com/kripken/emscripten/wiki/Asyncify) 但不推荐。更好的方法是使用 emterpreter (https://github.com/kripken/emscripten/wiki/Emterpreter) 或 doppio (https://github.com/plasma-umass/doppio)。但是,如果您可以使用 WebAssembly 的演进标准,将来可能会有更好的解决方案。到目前为止,使此类程序运行的唯一确定方法是消除所有递归或实现您自己的调用堆栈。尽管如此,您必须将您的状态保存在 javascript 堆栈之外,因为它不能被程序本身操作。这也是longjmp在这种情况下不起作用的原因。

【讨论】:

我其实很喜欢 Asyncify 的建议。使用它,您可以编写一个 getch() 的实现,它调用一个 JavaScript 函数,该函数返回一个承诺,该承诺在指定输入字段上触发按键时解析。

以上是关于如何保存状态并返回深度 C 函数?的主要内容,如果未能解决你的问题,请参考以下文章

Android导航组件:如何保存片段状态

AngularJS - 将路由组件保存在缓存中(保持活动状态)

再说C模块的编写

如何保存 WPF UI 状态?

在C 函数中保存状态:registryreference和upvalues

单击编辑按钮并返回时保存 GridView 状态