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