詳解關(guān)于Lua棧介紹以及實例
關(guān)于Lua棧介紹以及實例是本文要介紹的內(nèi)容,主要是棧在lua中如何使用,具體內(nèi)容的實現(xiàn)來看本文詳解。c++中載入lua的代碼
- #include <> //所需要的頭文件
- extern "C"
- {
- #include "include\lua.h"
- #include "include\lualib.h"
- #include "include\lauxlib.h"
- #pragma comment(lib, "lua.lib")
- };
- int main(void)
- {
- char buff[256]; //棧
- int error; //錯誤代碼
- lua_State* L = lua_open(); //lua指針
- luaL_openlibs(L); //載入所有l(wèi)ua庫
- //在此加入所需代碼...
- lua_close(L); //關(guān)閉lua
- return 0;
- }
在Programming in lua中載入庫的方法是分別載入5個庫,分別是base、table、io、string、math,但是在使用中(lua5.1.3 + vs.net2003)發(fā)現(xiàn)io庫在載入的時候會出現(xiàn)錯誤,程序無法繼續(xù)執(zhí)行,但不提示錯誤。
在網(wǎng)上查詢了一下,有些人遇到的是非法引用內(nèi)存(見這里),他的解決方法是改成上面代碼中的方式:直接載入全部庫。
在這里有一段解釋:“關(guān)于luaopen_io調(diào)用失敗的問題,在Lua的maillist里問了一下,有人說是因為io庫里有些函數(shù)的運行是依賴于Lua建立的特定的環(huán)境,所以要用lua_call來調(diào)用,要么,就直接用luaL_openlibs來引入所有標準庫。看了看幫助文檔,還有Lua的源代碼,似乎好像就是這么回事??!”
再查官方文檔(http://www.lua.org/manual/5.1/manual.html)中有一段:
- To have access to these libraries, the C host program should call the luaL_openlibs function,
- which opens all standard libraries. Alternatively, it can open them individually by calling luaopen_base (for the basic library),
- luaopen_package (for the package library), luaopen_string (for the string library),
- luaopen_table (for the table library), luaopen_math (for the mathematical library),
- luaopen_io (for the I/O library), luaopen_os (for the Operating System library),
- and luaopen_debug (for the debug library).
- These functions are declared in lualib.h and should not be called directly: you must call them like any other Lua C function, e.g.,
- by using lua_call. "
最后這句的意思是:“這些函數(shù)在lualib.h中定義并且不能直接調(diào)用:你必須以其他C函數(shù)調(diào)用方式來進行調(diào)用,例如使用lua_call。”
接著是lua_call的用法:
lua_call
原型:void lua_call (lua_State *L, int nargs, int nresults);
Calls a function.
功能:調(diào)用一個方法
調(diào)用一個函數(shù)必須按照以下的規(guī)則:首先,將要調(diào)用的函數(shù)入棧;之后,將函數(shù)參數(shù)按順序入棧,就是說第一個參數(shù)最先入棧。最后調(diào)用lua_call;nargs是入棧的參數(shù)數(shù)。當(dāng)函數(shù)被調(diào)用時彈出全部的參數(shù)和函數(shù)值。當(dāng)函數(shù)返回后函數(shù)結(jié)果會壓入棧。結(jié)果的數(shù)量取決于nresults(lua_call的最后一個參數(shù))。除非nresults的值是LUA_MULTRET。以這種方式所有的函數(shù)結(jié)果都被入棧。由Lua管理??臻g中的這些返回值。函數(shù)的結(jié)果按順序入棧(第一個元素最先入棧),所以在調(diào)用完成后最后一個參數(shù)在棧頂。
- Any error inside the called function is propagated upwards (with a longjmp).
在函數(shù)調(diào)用中產(chǎn)生的error會被向上傳遞(使用longjmp方式)。
- The following example shows how the host program may do the equivalent to this Lua code:
下面的例子展示了如何使宿主程序做如下lua代碼的功能:
- a = f("how", t.x, 14)
- it is in C:
這是在C中:
- lua_getfield(L, LUA_GLOBALSINDEX, "f"); /* function to be called */
- lua_pushstring(L, "how"); /* 1st argument */
- lua_getfield(L, LUA_GLOBALSINDEX, "t"); /* table to be indexed */
- lua_getfield(L, -1, "x"); /* push result of t.x (2nd arg) */
- lua_remove(L, -2); /* remove 't' from the stack */
- lua_pushinteger(L, 14); /* 3rd argument */
- lua_call(L, 3, 1); /* call 'f' with 3 arguments and 1 result */
- lua_setfield(L, LUA_GLOBALSINDEX, "a"); /* set global 'a' */
如果看不明白(其實我也不明白)那就直接按照最上面的代碼中所寫的方式載入lua庫吧。
學(xué)習(xí)LUA也有一些時日了,個人認為對于LUA中的棧的理解很重要,嗯,寫個小文章(真的很小)
如果你看了LUA的文檔,那么就應(yīng)該很清楚LUA與C交互數(shù)據(jù)時都是用到LUA中所謂的stack。那么當(dāng)我調(diào)用lua_open函數(shù)之后棧是什么樣的呢?空的(luaopen_base等會往棧上加進一些東西)。那么至于如何操作棧上的數(shù)據(jù),我想官方文檔上已經(jīng)說得很清楚了,不過最初我對于棧的順序有一些迷糊,所以就說說這個?,F(xiàn)在假如我有如下的一段代碼:
- lua_State* L = lua_open();
- lua_pushnumber( L, 211 );
- lua_pushnumber( L, 2222 );
- lua_newtable( L );
- lua_close( L );
那么當(dāng)執(zhí)行完lua_newtable之后棧上有三個元素,大致就是這樣:
- 211
- 222
- table
現(xiàn)在211是第一個元素,index為1,不過LUA也可以用負數(shù)來表示,那么現(xiàn)在他是多少?
- index -index value
- 1 -3 211
- 2 -2 222
- 3 -1 table
嗯,很簡單,再看看如果我們要設(shè)置一個TABLE的值怎么做?文檔中說用lua_settable或是lua_rawset(這兩者有什么區(qū)別應(yīng)該和這里說的無關(guān)),它們參數(shù)意義、以及準備工作都一樣,-1是值,-2是鍵值
- lua_settable( lua_state*, int )
第一個參數(shù)是要操作的腳本環(huán)境,第二個則是要操作的表在棧上的位置
一般的寫法可能是這樣
- // 代碼A
- lua_getglobal( L, "myTable" ); // 獲取要設(shè)置值的table
- lua_pushstring( L, "hp" ); // "hp"在棧上的位置為-1
- lua_pushnumber( L, 211 ); // "hp"在棧上的位置變?yōu)?2,而211則是-1
- lua_settable( L, -3 ); // 值被正確的設(shè)置到全局變量(表)的myTable中
如果我是想把hp這個值設(shè)置到全局表中呢?一般通過調(diào)用lua_setglobal宏
- lua_pushnumber( L, 211 );
- lua_setglobal( L, "hp" );
就這么簡單,不過我們來看看lua_setglobal這個宏
- #define lua_setglobal(L,s) \
- (lua_pushstring(L, s), lua_insert(L, -2), lua_settable(L, LUA_GLOBALSINDEX))
這么看來實際上我們上面的代碼被替換成了
- lua_pushnumber( L, 211 );
- lua_pushstring( L, "hp" );
- lua_insert( L, -2 ); // 這一步看上去或許比較詭異,實際上是把-1的值放到lua_insert的第二個參數(shù)所指的位置,然后這個位置后面的參數(shù)往上移
- //這里實際上最終結(jié)果就是-1和-2對調(diào),但從邏輯上并不是對調(diào)
- lua_settable( L, LUA_GLOBALSINDEX ); // 這里為什么不用lua_rawset?我認為是有原因的^@^
將上面的代碼與代碼A結(jié)合起來看,在lua_settable時index值不同,而它做的工作是如果發(fā)現(xiàn)index是LUA_GLOBALSINDEX 那么就取出全局表(還有一個LUA_REGISTERINDEX,類似),否則從stack上取元素,當(dāng)然,這個棧位置取出的不是一個table就會失敗。所以代碼A中指定的是-3是剛從全局表中取出的myTable表(這里假設(shè)他是一個table),上面的代碼片段則是取出的全局表。所以lua_settable的index是什么值都可以,只要它指向的是一個table
實際上lua中與c的接口也就主要在棧的操作上,基本上你在寫一個lua與C結(jié)合的程序時你最最需要做的工作就是明白你當(dāng)前棧上有什么元素以及它們的位置。我一般會在紙上畫出他們的位置,如果你熟了,對于幾句在一起有關(guān)聯(lián)的lua調(diào)用則可以很快的看出棧的變化。比如
- lua_gettable/lua_rawget
- lua_pushstring( L, "hp" );
- lua_gettable( L, LUA_GLOBALSINDEX );
只看第一句,棧頂是一個字符串,但兩句放在一起,最終棧頂是一個全局表上一個名為hp的實際值
- lua_pushstring( L, "hp" );
- lua_pushnumber( L, 211 );
- lua_settable( L, LUA_GLOBALSINDEX );
無論第二句pushnumber還是pushvalue,pushstring什么的,最終這三句執(zhí)行之后對于棧來說是沒有任何變化的,因為lua_settable/lua_rawset會移走-1和-2
總之,對于棧的變化,在看一個函數(shù)的文檔時先看它參數(shù)又需要棧上那些位置的元素并正確設(shè)置棧上的值,看清楚他會取棧上那些位置的元素作為這個lua api調(diào)用的使用并為之把正確的值放到棧上,最后注意函數(shù)完成之后會清除/移走那些位置的元素,我想應(yīng)該就沒什么問題了
- lua_gettable
- lua_getglobal(L, "mytable") <== push mytable
- lua_pushnumber(L, 1) <== push key 1
- lua_gettable(L, -2) <== pop key 1, push mytable[1]
- lua_settable
- lua_getglobal(L, "mytable") <== push mytable
- lua_pushnumber(L, 1) <== push key 1
- lua_pushstring(L, "abc") <== push value "abc"
- lua_settable(L, -3) <== mytable[1] = "abc", pop key & value
lua_rawget:
用法同lua_gettable,但更快(因為當(dāng)key不存在時不用訪問元方法__index)
lua_rawset:
用法同lua_settable,但更快(因為當(dāng)key不存在時不用訪問元方法__newindex)
lua_rawgeti必須為數(shù)值鍵
- lua_getglobal(L, "mytable") <== push mytable
- lua_rawgeti(L, -1, 1) <== push mytable[1],作用同下面兩行調(diào)用
- --lua_pushnumber(L, 1) <== push key 1
- --lua_rawget(L,-2) <== pop key 1, push mytable[1]
lua_rawseti必須為數(shù)值鍵
- lua_getglobal(L, "mytable") <== push mytable
- lua_pushstring(L, "abc") <== push value "abc"
- lua_rawseti(L, -2, 1) <== mytable[1] = "abc", pop value "abc"
lua_getfield必須為字符串鍵
- lua_getglobal(L, "mytable") <== push mytable
- lua_getfield(L, -1, "x") <== push mytable["x"],作用同下面兩行調(diào)用
- --lua_pushstring(L, "x") <== push key "x"
- --lua_gettable(L,-2) <== pop key "x", push mytable["x"]
lua_setfield必須為字符串鍵
- lua_getglobal(L, "mytable") <== push mytable
- lua_pushstring(L, "abc") <== push value "abc"
- lua_setfield(L, -2, "x") <== mytable["x"] = "abc", pop value "abc"
詳解:詳解關(guān)于Lua棧介紹以及實例的內(nèi)容介紹完了,希望通過本文的學(xué)習(xí)能對你有所幫助!