我正在爲我們的應用程序編寫一個lua(5.1)擴展程序,允許我們的客戶在沒有我們的支持的情況下將其擴展到更多屏幕。如何從另一個lua函數調用lua函數?
一般來說,這工作得很好,有包裝的一打的幫助,甚至很舒服,但現在我有以下情況:
我有兩個用戶的數據對象,對象定時器和對象的窗口。 用戶可以通過簡單定義成員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++星座中完成它本身?
非常感謝。
感謝您的快速響應,是的,我還考慮過這個備用解決方案,並且可以通過將某種StackOffset變量附加到我們自己的腳本實例對象上,然後可以使用它獲得「真實「參數計數像argumentCount = StackOffset-lua_gettop(L)。 問題是,我們所寫的所有擴展(例如luasocket)仍然依賴gettop來驗證參數計數,但它不起作用。 只是好奇,如果有一個「乾淨」的方式,讓一個遞歸的c調用行爲,就像它已經從一個lua腳本調用。 – Alyxion