|
Posted on 2012-07-11 16:43 點點滴滴 閱讀(351) 評論(0) 編輯 收藏 引用 所屬分類: 02 編程語言
lua_State Lua狀態機或叫Lua虛擬機,支持多線程,可創建多個狀態機 typedef int (*lua_Cfunction)(lua_State* L) Lua所調用的C函數的函數原型,在C程序中需要將這種函數類型的 函數注冊到Lua狀態機中,此后Lua才能調用到C的函數 如果有函數 int c_Hello(lua_State* l) { luaL_checktype(l, 1, LUA_TSTRING) luaL_checktype(l, 2, LUA_TSTRING) // 兩個參數都是字符串 // 先輸出第二個參數,后輸出第一個參數 printf("%s\t%s\n", lua_tostring(l,-1),lua_tostring(l, -2)); }
需在C程序初始化的時候執行以下代碼: // 將函數名壓入LuaVM棧 lua_pushstring(l, "c_hello"); // 將函數地址壓入LuaVM棧 lua_pushcclosure(l, &c_Hello, 0) // 把LuaVM棧上的這個表放入全局表中 lua_settable(l, LUA_GLOBALSINDEX) 此后在Lua中就可以這樣調用: c_hello()
注:C函數的實現如果有參數的話最好為每一個參數進行類型檢查 以確保所棧里的參數是函數參數類型匹配
lua_State* lua_newstate(lua_Alloc f, void* ud) 新建一個LuaVM,可以直接調用lua_open來調用這個函數
void lua_close(lua_State* l)) 關閉LuaVM
關于棧幀 當一個函數調用發生的時候就形成了一個棧幀,當這個函數調用結束 后這個棧幀就消失了
如在Lua這樣調用c_hello("Hello", "World"),那么c_Hello所看到 棧幀就是從它的參數開始,如下圖: 可以有兩種索引方式對棧頂進行操作,正數和負數。
棧相關操作 int (lua_gettop) (lua_State *L) 取得當前使用了多少棧空間 void (lua_settop) (lua_State *L, int idx) 把棧頂設置在idx處,相當于做了一次函數調用改變了棧幀 如果這個時候調用 lua_settop(L, 1),則會把剛壓入棧的兩個參數刪除 void (lua_pushvalue) (lua_State *L, int idx) 向此棧幀的第idx個位置的值壓入棧頂,相當于復制 void (lua_remove) (lua_State *L, int idx) 刪除第idx個位置的值 如果lua_remove(L, -2)則會將"Hello"刪除而剩下"World"在棧頂 并會被移到原來"Hello"存放的位置 void (lua_insert) (lua_State *L, int idx); 先將整個上移或下移,并將棧頂的值壓入空出的位置 void (lua_replace) (lua_State *L, int idx) 把棧頂的值替換掉idx處的值 int (lua_checkstack) (lua_State *L, int sz) 檢查棧的大小是否已經增長到最大值,如果沒有則將棧的大小增長sz 返回0表示棧溢出,返回1表示成功 void (lua_xmove) (lua_State *from, lua_State*to, int n) 從LavVM的棧頂移動n個值到to的LuaVM的棧頂
棧存取操作 以下函數往棧頂壓入一個C值 void (lua_pushnil) (lua_State *L) 往棧頂壓入一個NIL值 void (lua_pushnumber) (lua_State *L, lua_Number n) 往棧頂壓入一個實數 void (lua_pushinteger) (lua_State *L,lua_Integer n) 往棧頂壓入一個整數 void (lua_pushlstring) (lua_State *L, const char*s, size_t l) 往棧頂壓入一個二進制串 void (lua_pushstring) (lua_State *L, const char*s) 往棧頂壓入一個字符串 const char *(lua_pushvfstring) (lua_State *L,const char *fmt, va_list argp) 往棧頂壓入一個格式化串,不過argp是變參 const char *(lua_pushfstring) (lua_State *L,const char *fmt, ...) 往棧頂壓入一個格式化串 void (lua_pushcclosure) (lua_State *L,lua_CFunction fn, int n) 壓入一個函數 void (lua_pushboolean) (lua_State *L, int b) 往棧頂壓入一個bool類型 void (lua_pushlightuserdata) (lua_State *L, void*p) 往棧頂壓入一個數組,數組的內存塊指向p int (lua_pushthread) (lua_State *L)
以下函數往從棧中復制一個值存入棧頂 void (lua_gettable) (lua_State *L, int idx) idx指向表在棧中的位置,彈出棧頂的key取得表中的值并壓入棧頂 此函數與lua_rawget的區別在于此函數會調用metatable的方法 void (lua_getfield) (lua_State *L, int idx,const char *k) 以k所在棧中的索引(idx)作為參數,取得k對應的值并壓入棧頂 效率與lua_gettable相同 void (lua_rawget) (lua_State *L, int idx) idx指向表在棧中的位置,彈出棧頂的key取得表中的值并壓入棧頂 為了提高數據訪問的效率而直接讀取表中的數據,不需要對metamethod的調用 這個函數在循環訪問數組元素比較方便 void (lua_rawgeti) (lua_State *L, int idx, intn) idx指向表在棧中的位置,n指向key在棧中的位置,取得表中的值并入棧 相當于:lua_pushnumber(L,n);lua_rawget(L,idx) void (lua_createtable) (lua_State *L, int narr,int nrec) 新建一個表并壓入棧頂,表的大小為narr,并預留了nrec個元素空間 void *(lua_newuserdata) (lua_State *L, size_t sz) 在棧中分配一塊大小為sz的內存并把管理結構入棧且返回內存地址 int (lua_getmetatable) (lua_State *L, intobjindex) objindex指向對象在棧中的位置,取得對象的metatable并將其壓入棧頂 如果失敗或者對象沒有metatable表則返回0 void (lua_getfenv) (lua_State *L, int idx) idx指向對象所在棧中的位置,取得此對象并將其環境量壓入棧
以下函數用于判斷棧內某處值的類型 int (lua_isnumber) (lua_State *L, int idx) 棧idx處的值是否是實數 int (lua_isstring) (lua_State *L, int idx) 棧idx處的值是否是字符串 int (lua_iscfunction) (lua_State *L, int idx) 棧idx處的值是否是函數 int (lua_isuserdata) (lua_State *L, int idx) 棧idx處的值是否是二進制數據 int (lua_type) (lua_State *L, int idx) 取得棧idx處的值的類型 const char *(lua_typename) (lua_State *L, inttp) 將類型轉換成C字符串 int (lua_equal) (lua_State *L, int idx1, intidx2) 棧內兩個值是否相等,類型和值都需要相等才返回真 int (lua_rawequal) (lua_State *L, int idx1, intidx2) 棧內兩個對象是否相等,如果是對象時所指對象相同也算相等 int (lua_lessthan) (lua_State *L, int idx1, intidx2) 棧內兩個值的大小,idx1處的值是否小于idx2處的值
以下函數將棧內的某個格轉換成C能理解的值,棧結構不變 lua_Number (lua_tonumber) (lua_State *L, intidx) 將idx所指向的Lua值轉換成實數返回 lua_Integer (lua_tointeger) (lua_State *L, intidx) 將idx所指向的Lua值轉換成整數返回 int (lua_toboolean) (lua_State *L, int idx) 將idx所指向的Lua值轉換成bool值返回 const char *(lua_tolstring) (lua_State *L, intidx, size_t *len) 將idx所指向的Lua值轉換成字符串返回,并把長度存入len中 size_t (lua_objlen) (lua_State *L, int idx) 取得idx所指向的Lua值長度 lua_Cfunction (lua_tocfunction) (lua_State *L,int idx) 將idx所指向的Lua值轉換成函數返回 void *(lua_touserdata) (lua_State *L, int idx) 將idx所指向的Lua值轉換成內存塊(數組)返回 lua_State *(lua_tothread) (lua_State *L, intidx) 將idx所指向的Lua值轉換成LuaVM對象返回 const void *(lua_topointer) (lua_State *L, intidx) 取得idx所指對象的值的存儲地址返回
以下函數與其對應的get函數類似 void (lua_settable) (lua_State *L, int idx) void (lua_setfield) (lua_State *L, int idx,const char *k) void (lua_rawset) (lua_State *L, int idx) void (lua_rawseti) (lua_State *L, int idx, intn) int (lua_setmetatable) (lua_State *L, intobjindex) int (lua_setfenv) (lua_State *L, int idx)
函數調用 關于函數調用 當需要調用一個Lua函數時,先要將函數壓入棧中,然后將其參數依次壓入 如下圖:
假設在Lua代碼中有函數: function hello(x, y) print(x, y) end 則在C中可以這樣來調用: lua_getglobal(L, "hello"); -- 從全局域中取得hello函數并壓入棧頂 lua_pushstring(L, "Hello"); -- 依次壓入兩個參數 lua_pushstring(L, "World"); lua_call(L, 2, 0); -- 跳過兩個參數取得函數并調用此函數,函數中會取得相應參數 void (lua_call) (lua_State *L, int nargs, intnresults) 無保護的函數調用 nargs是指調用函數的參數個數,nresults是指所調用的函數的返回值個數 int (lua_pcall) (lua_State *L, int nargs, intnresults, int errfunc) 與lua_call相似,不過此函數是在保護模式下被調用 如果發生錯誤則會將錯誤壓入棧,否則功能與lua_call一樣 int (lua_cpcall) (lua_State *L, lua_CFunction func, void *ud) 直接調用一個C函數,并把ud做為func函數調用的參數 也就是說在func函數內部看來第一個參數是ud int (lua_load) (lua_State *L, lua_Reader reader,void *dt, const char *chunkname);
|