這一段時間看了《programming in lua》中的第28章,看一遍並不是很難,但是只是朦朧的感覺,霧里看花,水中望月。最終還是決定敲出來自己看看,練練手,結果受益不少,也遇到了一些問題,記在這里。自己做一個總結,也希望能幫助和我一樣lua的初學者。
1. 書上並沒有寫清楚對於CAPI的制作和使用內容。主要包括dll的生成,以及使用
(1)dll生成。
直接用vs新建一個dll工程,要記得包含依賴路徑。如下圖所示,包含你的lua安裝路徑。
工程名要和庫的最終名字一致(默認是一致的)。luaopen_array(lua_State *L)也最好一致。這一塊,我沒有去研究和測試,就是按照書上來做的,沒有碰到什么大的問題,主要和使用方式有一些關系。
(2)使用
兩種方法:
第一種:require "myarray"。這就需要dll的名字是myarray.dll。而且luaopen_xxx(lua_State *L)也是luaopen_array的形式;
第二種:package.loadlib("array", "luaopen_array")()。這種的話,第一個參數array是你在寫c代碼時候注冊的table名字,后面一個函數名。這種使用方式我個人覺得是需要dll名字以及注冊的table名必須是"array",但是luaoepn_xxx就不需要了。
(3)linux
直接使用命令:gcc mylib.c -fPIC -shared -o libmylib.so即可生成可以使用的動態庫。
使用和window下面的使用一樣,自己用的是第二種方法:package.loadlib("./libmydir.so", "luaopen_mydir")()
2. 一些問題
(1)直接使用loadlib不行
mylib = loadlib("mylib", "luaopen_mylib")
解決:加上庫,package.loadlib
(2)使用package.loadlib的時候報錯
描述:lua: capi_study.lua:57: attempt to call a nil value
原因:工程名字生成的庫名字和使用的時候不一致出錯
(3)luaL_openlib和luaL_register
*接口變化,我用的lua版本是5.1.所以用luaL_register接口,而不是書上仍然使用的luaL_openlib接口。官網的document的5.1《reference manual》中有提到,可以自己去看。
*linux卻仍然還是需要使用前者。(linux下面的版本是5.2.0了,不明白是什么原因)
(4)面向對象例子報錯
描述:lua: capi_study.lua:102: calling 'size' on bad self (luaBook.array expected, got userdata)
原因:void *ud = luaL_checkudata(L, 1, "LuaBook.array");中的"LuaBook.array"寫成了"luaBook.array"
結果:Success!
package.loadlib("array", "luaopen_array")() a = array.new(1000) print(a:size()) a:set(10, 3.4) print(a:get(10))
輸出結果:
1000 3.4
(5)linux編譯報錯
描述:“錯誤:數組元素的類型不完全”
原因:
*用gcc4編譯時出現數組元素的類型不完全錯誤,這是因為gcc4不允許類型在聲明前使用。【引用:http://blog.csdn.net/horsefaced/article/details/1678965】
*luaL_Reg寫成了luaL_reg
解決:
(6)linux編譯警告
描述:警告:傳遞參數 2 (屬於 ‘lua_getmetatable’)時將指針賦給整數,未作類型轉換
原因:低級錯誤luaL_getmetatable寫成了lua_getmetatable,希望碰到這個問題的能夠不用再糾結了
3.一個例子
附上dir例子的源碼,linux編譯通過。
#include <math.h> #include <dirent.h> #include <errno.h> #include "lua.h" #include "lualib.h" #include "lauxlib.h" /* forward declaration */ static int dir_iter(lua_State *L); static int l_dir(lua_State *L){ const char *path = luaL_checkstring(L, 1); /* create a userdatum */ DIR **d = (DIR **)lua_newuserdata(L, sizeof(DIR *)); /* set its metatable */ luaL_getmetatable(L, "LuaBook.dir"); lua_setmetatable(L, -2); /* try to pen then given dirctory */ *d = opendir(path); if (*d == NULL) luaL_error(L, "cannot open %s: %s", path, strerror(errno)); /* creates and returns the iterator function */ lua_pushcclosure(L, dir_iter, 1); return 1; } static int dir_iter(lua_State *L){ DIR *d = *(DIR **)lua_touserdata(L, lua_upvalueindex(1)); struct dirent *entry; if (( entry = readdir(d)) != NULL){ lua_pushstring(L, entry->d_name); return 1; } else return 0; /* no more valuse to return */ } static int dir_gc(lua_State *L){ DIR *d = *(DIR **)lua_touserdata(L, 1); if (d) closedir(d); return 0; } int luaopen_mydir (lua_State *L){ luaL_newmetatable(L, "LuaBook.dir"); /* set its __Gc field */ lua_pushstring(L, "__gc"); lua_pushcfunction(L, dir_gc); lua_settable(L, -3); /* register the 'dir' function */ lua_pushcfunction(L, l_dir); lua_setglobal(L, "dir"); return 0; }
使用和結果
> package.loadlib("./libmydir.so", "luaopen_mydir")() > for fname in dir(".") do print(fname) end . .. libmylib.so libdir.so dir.c .dir.c.swp dir.so mydir_none.c mylib.c libmydir.so mydir.c capi_study.lua lua-5.1.2 >
4. 總結
(1)lua調用c函數,返回的就是棧上的內容。例如:你在c函數壓入一個整數,那么返回的第一個就是這個整數。
(2)linux命令:gcc mylib.c -fPIC -shared -o libmylib.so
(3)看書學習的時候,當覺得朦朧不清晰的時候,要放慢速度,最好的就是Do it。敲出來去看看,會有不同的收獲,感覺有東西,這種感覺個人覺得是學習的時候最好的。