【问题标题】:How to call a lua cfunction from another lua cfunction?如何从另一个 lua c 函数调用 lua c 函数?
【发布时间】:2015-07-14 20:41:30
【问题描述】:

我正在为我们的应用程序编写一个 lua (5.1) 扩展,允许我们的客户在没有我们支持的情况下使用更多屏幕对其进行扩展。

总的来说,这工作得很好,并且在十几个包装器的帮助下甚至非常舒适,但现在我遇到了以下情况:

我有两个用户数据对象,一个对象 Timer 和一个对象 Window。 用户可以通过简单地定义他们的成员 OnTimer 和 OnSizeChanged 来将事件“附加”到他们。

这仍然很有效: mainLoop (C++) -> 检查所有计时器 (C++)-> 附加的脚本对象? (C++),然后:

// get ud by reference / stack: +1 / 1
lua_rawgeti(mState, LUA_REGISTRYINDEX, miReferenceId); 
lua_getfenv(mState, -1); // get attached table / +1 / 2 total
lua_getfield(mState, -1, functionName.ToChar()); // +1 / 3 total
if (!lua_isfunction(mState, -1))
{
...
}
lua_remove(mState, -2); // remove table / -1 / 2 total
lua_remove(mState, -2); // remove ud / -1 / 1 total, just function remaining
// ( .. user data still pushed at this point ..)
lua_pcall(mState, 1, 0, 0);

所以在以下场景中一切都很好(在开始调用之前和之后堆栈肯定是 0):

function OnTimer(Self)
Log("Some text") // Log calls C++ luaLog with 1 argument
end

myTimer = timer.new()
myTimer.OnTimer=OnTimer;

如果 lua 脚本看起来像这样:

timer = timer.new()
window = window.new()

function OnTimer(Self)
   window:SetSize(323.5,234.5)   
end

function OnSizeChanged(Self,NewWidth,NewHeight)
Log("Sized changed")
end

timer.OnTimer = OnTimer
window.OnSizeChanged = OnSizeChanged

那么C++中的调用栈将是:

timer::HandleTimer (C++) -> 
push "OnTimer"  function ->
push timer ud -> 
pcall (L,1) -> .. (Lua) 
static int luaChangeSize(lua_State *L) (back to C++) ->
Window::SetSize -> 
The window sees "oooooooh, an event handler has been assigned" -> 
push "OnSizeChanged" function, window ud, x, y -> 
pcall(L,3,0,0) -> .. (Lua) -> 
int luaLog(lua_state* L) (back to C++ again)

当然,在调用 OnSizeChanged 的​​时候,堆栈仍然包含之前的 luaChangeSize 回调的内容,并且不为空。

我认为我可以通过在调用 Window::SetSize 之前弹出 luaChangeSize 的所有参数并在之后恢复它们来简单地清理堆栈,但这不起作用。结果有点随机,具体取决于调用堆栈的星座,从“无法调用数字”到再次随机调用堆栈上的前一个函数。 (所有调用前后 gettop 绝对为 0)。

那么:我如何“备份”当前堆栈并在之后恢复它,因为 lua 在 C++ -> Lua -> C++ 星座中自己完成它?

非常感谢。

【问题讨论】:

    标签: c++ recursion lua backup callstack


    【解决方案1】:

    备份和恢复堆栈的概念似乎是错误的。你不能让堆栈保持原样,让新调用在现有堆栈之上工作。确保所有内容都使用相对于顶部的索引而不是绝对索引来访问堆栈上的项目。

    【讨论】:

    • 感谢您的快速响应,是的,我已经考虑过这个后备解决方案,并且可以通过将某种 StackOffset 变量附加到我们自己的脚本实例对象,然后可以使用它获取“真实”参数计数,如 argumentCount = StackOffset-lua_gettop(L)。问题是所有不是我们编写的扩展(例如 luasocket)仍然依赖 gettop 来验证参数计数将不起作用。只是好奇是否有一种“干净”的方式可以让递归 c 调用表现得就好像它是从 lua 脚本调用的一样。
    猜你喜欢
    • 2014-09-01
    • 1970-01-01
    • 2015-12-17
    • 2021-04-15
    • 2013-02-19
    • 2015-12-30
    • 2022-01-04
    • 2010-11-15
    相关资源
    最近更新 更多