詳解關(guān)于Lua棧介紹以及實(shí)例
關(guān)于Lua棧介紹以及實(shí)例是本文要介紹的內(nèi)容,主要是棧在lua中如何使用,具體內(nèi)容的實(shí)現(xiàn)來(lái)看本文詳解。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; //錯(cuò)誤代碼
- lua_State* L = lua_open(); //lua指針
- luaL_openlibs(L); //載入所有l(wèi)ua庫(kù)
- //在此加入所需代碼...
- lua_close(L); //關(guān)閉lua
- return 0;
- }
在Programming in lua中載入庫(kù)的方法是分別載入5個(gè)庫(kù),分別是base、table、io、string、math,但是在使用中(lua5.1.3 + vs.net2003)發(fā)現(xiàn)io庫(kù)在載入的時(shí)候會(huì)出現(xiàn)錯(cuò)誤,程序無(wú)法繼續(xù)執(zhí)行,但不提示錯(cuò)誤。
在網(wǎng)上查詢了一下,有些人遇到的是非法引用內(nèi)存(見(jiàn)這里),他的解決方法是改成上面代碼中的方式:直接載入全部庫(kù)。
在這里有一段解釋:“關(guān)于luaopen_io調(diào)用失敗的問(wèn)題,在Lua的maillist里問(wèn)了一下,有人說(shuō)是因?yàn)閕o庫(kù)里有些函數(shù)的運(yùn)行是依賴于Lua建立的特定的環(huán)境,所以要用lua_call來(lái)調(diào)用,要么,就直接用luaL_openlibs來(lái)引入所有標(biāo)準(zhǔn)庫(kù)??戳丝磶椭臋n,還有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)用方式來(lái)進(jìn)行調(diào)用,例如使用lua_call。”
接著是lua_call的用法:
lua_call
原型:void lua_call (lua_State *L, int nargs, int nresults);
Calls a function.
功能:調(diào)用一個(gè)方法
調(diào)用一個(gè)函數(shù)必須按照以下的規(guī)則:首先,將要調(diào)用的函數(shù)入棧;之后,將函數(shù)參數(shù)按順序入棧,就是說(shuō)第一個(gè)參數(shù)最先入棧。最后調(diào)用lua_call;nargs是入棧的參數(shù)數(shù)。當(dāng)函數(shù)被調(diào)用時(shí)彈出全部的參數(shù)和函數(shù)值。當(dāng)函數(shù)返回后函數(shù)結(jié)果會(huì)壓入棧。結(jié)果的數(shù)量取決于nresults(lua_call的最后一個(gè)參數(shù))。除非nresults的值是LUA_MULTRET。以這種方式所有的函數(shù)結(jié)果都被入棧。由Lua管理?xiàng)?臻g中的這些返回值。函數(shù)的結(jié)果按順序入棧(第一個(gè)元素最先入棧),所以在調(diào)用完成后最后一個(gè)參數(shù)在棧頂。
- Any error inside the called function is propagated upwards (with a longjmp).
在函數(shù)調(diào)用中產(chǎn)生的error會(huì)被向上傳遞(使用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' */
如果看不明白(其實(shí)我也不明白)那就直接按照最上面的代碼中所寫(xiě)的方式載入lua庫(kù)吧。
學(xué)習(xí)LUA也有一些時(shí)日了,個(gè)人認(rèn)為對(duì)于LUA中的棧的理解很重要,嗯,寫(xiě)個(gè)小文章(真的很?。?nbsp;
如果你看了LUA的文檔,那么就應(yīng)該很清楚LUA與C交互數(shù)據(jù)時(shí)都是用到LUA中所謂的stack。那么當(dāng)我調(diào)用lua_open函數(shù)之后棧是什么樣的呢?空的(luaopen_base等會(huì)往棧上加進(jìn)一些東西)。那么至于如何操作棧上的數(shù)據(jù),我想官方文檔上已經(jīng)說(shuō)得很清楚了,不過(guò)最初我對(duì)于棧的順序有一些迷糊,所以就說(shuō)說(shuō)這個(gè)。現(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之后棧上有三個(gè)元素,大致就是這樣:
- 211
- 222
- table
現(xiàn)在211是第一個(gè)元素,index為1,不過(guò)LUA也可以用負(fù)數(shù)來(lái)表示,那么現(xiàn)在他是多少?
- index -index value
- 1 -3 211
- 2 -2 222
- 3 -1 table
嗯,很簡(jiǎn)單,再看看如果我們要設(shè)置一個(gè)TABLE的值怎么做?文檔中說(shuō)用lua_settable或是lua_rawset(這兩者有什么區(qū)別應(yīng)該和這里說(shuō)的無(wú)關(guān)),它們參數(shù)意義、以及準(zhǔn)備工作都一樣,-1是值,-2是鍵值
- lua_settable( lua_state*, int )
第一個(gè)參數(shù)是要操作的腳本環(huán)境,第二個(gè)則是要操作的表在棧上的位置
一般的寫(xiě)法可能是這樣
- // 代碼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這個(gè)值設(shè)置到全局表中呢?一般通過(guò)調(diào)用lua_setglobal宏
- lua_pushnumber( L, 211 );
- lua_setglobal( L, "hp" );
就這么簡(jiǎn)單,不過(guò)我們來(lái)看看lua_setglobal這個(gè)宏
- #define lua_setglobal(L,s) \
- (lua_pushstring(L, s), lua_insert(L, -2), lua_settable(L, LUA_GLOBALSINDEX))
這么看來(lái)實(shí)際上我們上面的代碼被替換成了
- lua_pushnumber( L, 211 );
- lua_pushstring( L, "hp" );
- lua_insert( L, -2 ); // 這一步看上去或許比較詭異,實(shí)際上是把-1的值放到lua_insert的第二個(gè)參數(shù)所指的位置,然后這個(gè)位置后面的參數(shù)往上移
- //這里實(shí)際上最終結(jié)果就是-1和-2對(duì)調(diào),但從邏輯上并不是對(duì)調(diào)
- lua_settable( L, LUA_GLOBALSINDEX ); // 這里為什么不用lua_rawset?我認(rèn)為是有原因的^@^
將上面的代碼與代碼A結(jié)合起來(lái)看,在lua_settable時(shí)index值不同,而它做的工作是如果發(fā)現(xiàn)index是LUA_GLOBALSINDEX 那么就取出全局表(還有一個(gè)LUA_REGISTERINDEX,類似),否則從stack上取元素,當(dāng)然,這個(gè)棧位置取出的不是一個(gè)table就會(huì)失敗。所以代碼A中指定的是-3是剛從全局表中取出的myTable表(這里假設(shè)他是一個(gè)table),上面的代碼片段則是取出的全局表。所以lua_settable的index是什么值都可以,只要它指向的是一個(gè)table
實(shí)際上lua中與c的接口也就主要在棧的操作上,基本上你在寫(xiě)一個(gè)lua與C結(jié)合的程序時(shí)你最最需要做的工作就是明白你當(dāng)前棧上有什么元素以及它們的位置。我一般會(huì)在紙上畫(huà)出他們的位置,如果你熟了,對(duì)于幾句在一起有關(guān)聯(lián)的lua調(diào)用則可以很快的看出棧的變化。比如
- lua_gettable/lua_rawget
- lua_pushstring( L, "hp" );
- lua_gettable( L, LUA_GLOBALSINDEX );
只看第一句,棧頂是一個(gè)字符串,但兩句放在一起,最終棧頂是一個(gè)全局表上一個(gè)名為hp的實(shí)際值
- lua_pushstring( L, "hp" );
- lua_pushnumber( L, 211 );
- lua_settable( L, LUA_GLOBALSINDEX );
無(wú)論第二句pushnumber還是pushvalue,pushstring什么的,最終這三句執(zhí)行之后對(duì)于棧來(lái)說(shuō)是沒(méi)有任何變化的,因?yàn)閘ua_settable/lua_rawset會(huì)移走-1和-2
總之,對(duì)于棧的變化,在看一個(gè)函數(shù)的文檔時(shí)先看它參數(shù)又需要棧上那些位置的元素并正確設(shè)置棧上的值,看清楚他會(huì)取棧上那些位置的元素作為這個(gè)lua api調(diào)用的使用并為之把正確的值放到棧上,最后注意函數(shù)完成之后會(huì)清除/移走那些位置的元素,我想應(yīng)該就沒(méi)什么問(wèn)題了
- 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,但更快(因?yàn)楫?dāng)key不存在時(shí)不用訪問(wèn)元方法__index)
lua_rawset:
用法同lua_settable,但更快(因?yàn)楫?dāng)key不存在時(shí)不用訪問(wèn)元方法__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棧介紹以及實(shí)例的內(nèi)容介紹完了,希望通過(guò)本文的學(xué)習(xí)能對(duì)你有所幫助!