lua C api
PS:這里是默認我已經學完了lua腳本的基本知識(包括table,元表,函數,基本庫, 文件io,庫導入等等)
lua是c寫的,無論是lua調用c還是c調用lua都非常容易,以下是基于文檔做的一些學習筆記,在文檔的索引可以快速找到api的名稱方便參考,文檔地址如下:
官網5.3文檔:http://www.lua.org/manual/5.3/#index
中文文檔:http://www.runoob.com/manual/lua53doc/contents.html#index
文檔包括兩部分,一部分是介紹lua的語法,以及一些注(keng)意(die)事項,比如其中說到空字符串和0都被認為是true,恩,要注意(MDZZ),感覺lua的強大之處在于他的table(手動咸魚),下面開始修仙,下面通過問答的方式給自己學習lua做一些筆記,在此之前,先編譯lua,我們到官網可以下載lua的源碼
源碼下載頁面:http://www.lua.org/download.html
5.3源碼下載:http://www.lua.org/ftp/lua-5.3.4.tar.gz
lua源碼非常小,很容易編譯,windows下,裝個Mingw即可用make編譯,以下是linux的
1.下載解壓源碼
wget http://www.lua.org/ftp/lua-5.3.4.tar.gz
tar -xzvf lua-5.3.4.tar.gz
~/local/lua/lua-5.3.4$ make
Please do 'make PLATFORM' where PLATFORM is one of these:
aix bsd c89 freebsd generic linux macosx mingw posix solaris
See doc/readme.html for complete instructions.
上面提示指定編譯選項,如果是windows下用Mingw的,就make -j12 mingw
, -j12是多線程編譯,分12個線程編譯,現在是debian,可以選擇posix,如果提示缺少一些庫,使用apt-get 安裝即可
make -j12 posix
編譯成功后,在src文件生成一個lua(lua解釋器),luac(lua腳本編譯器),以及靜態庫liblua.a,c調用lua的時候,現在是用靜態庫,現在把以下文件復制到項目目錄下
頭文件:lua.h lualib.h lauxlib.h luaconf.h
靜態庫文件:liblua.a
我的項目目錄為:~/project/lua
我把頭文件放置在~/project/lua/include
靜態庫文件放在~/project/lua/lib
以下所有的操作都是在項目目錄(用$PRO_HOME描述這個目錄)
1.c是如何調用lua寫的代碼的?
1.首先了解,lua在c代碼中是如何“工作”的,在c中要調用lua,首先我得創建一個lua狀態機(lua_State)
文件$PRO_HOME/main.c
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
int main(void)
{
//創建一個lua狀態機
lua_State *L = luaL_newstate();
//銷毀這貨
lua_close(L);
return 0;
}
然后嘗試編譯這個源文件
$gcc -I ../include -c main.c
$gcc -lm -o main main.o ../lib/liblua.a
編譯成功說明配置沒有問題
c調用lua代碼,首先要學會怎么把lua代碼“翻譯”成c的代碼,先不著急如何把lua代碼加載到lua狀態機運行,先了解下它是怎么運行的,根據文檔參考,寫以下一段代碼
-- a是一個全局變量
a="測試"
print(a)
a是一個全局變量,在lua中,無非在維護一個堆棧stack和一個環境表_G(暫時理解)
那么用c怎么寫呢?
1.創建一個狀態機(lua_State),加載基本庫(luaL_openlibs())
2.把字符串“測試”壓入堆棧(lua_pushstring)
3.把這個字符串彈出,設置為global,名稱為a(lua_setglobal())
4.從全局變量中,把print函數壓棧(lua_getglobal())
5.把參數a從全局變量中壓棧(lua_getglobal())
6.調用lua_call執行函數
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
int main(void)
{
lua_State *L = luaL_newstate();
//加載基本庫(print在其中)
luaL_openlibs(L);
//這里相當于設置全局變量a="測試"
lua_pushstring(L,"測試");
lua_setglobal(L,"a");
//print函數壓棧
lua_getglobal(L,"print");
//參數壓棧yiban
lua_getglobal(L,"a");
//調用函數,1個參數,0個返回值
lua_call(L,1,0);
lua_close(L);
return 0;
}
小總結:
在lua 的 C api中,
數據類型有:
lua_Integer 整形
lua_Number 默認對應的是C double
lua_Unsigned 無符號整形
字符串、布爾型或者是lua的table之類的,都是不能直接“拿出來”的
如果要操作,設置某某某的變量,可以通過lua_pushxxxx來把對應的數據類型的變量的值壓入堆棧,如果,要把變量設置為一個全局變量,就把它設置為global(lua_setglobal()),lua_setglobal會把棧頂的元素彈出,病設置到環境表,詳情查看文檔,
關于堆棧
lua的堆棧是lua自己實現的一個數據結構,并不是c語言執行時的那個堆棧,這個堆棧,由棧底往棧頂數,元素的序號是1 2 3 4 5...,這是它的絕對索引的方式,還有它支持負索引,也就是參考點是棧頂,而不是棧底,比如-1代表的是棧頂的元素,-2代表棧頂的下一個元素
對于堆棧的操作
有:
1.獲取棧頂的索引值,lua_gettop(L)(這個值,可以當做是棧元素個數)
2.把數據壓棧(lua_pushxxx,根據壓入的數據類型不一樣,有對應有一系列的操作函數,例如,lua_pushstring(L,"測試"),把字符串壓入,詳情看文檔lua_pushxxxx),又或者是
3.把堆棧的元素“讀取”出來,lua_toxxxx,就可以不彈出堆棧中的數據,把它轉換成c的數據類型,例如:lua_tolstring,注意,這個返回值是const類型,也就是說你不能對他進行更改,如果要做其他用途,你需要
把他copy出來
4.更改元素在堆棧中的位置
5.lua有垃圾回收機制GC,一般不用擔心內存釋放的問題
2.如何在lua狀態機中執行一個c寫的函數?
剛剛已經學了,如何在c中,“執行”lua,這個print函數他已經實現了,但是我們自己怎么實現一個函數,然后在lua中調用呢?
這個函數可以是
1.lua寫的函數
2.c寫的可以讓lua識別的函數
我們先來搞第二種,程序還是在原來的基礎上進行擴充
我們把print換成我們自己實現的一個函數,這個函數在lua里叫print2,它比較簡單,只能接受一個參數,不做任何檢查,也不做任何的錯誤的處理
lua注冊的函數的類型是:
static int fun(lua_State *L);
返回值是這個函數的返回值個數(因為lua支持多個返回值,執行完畢后,把返回值依次壓棧,然后返回返回值個數就行了),調用結束后,堆棧中剩下的是fun的返回值(如果有返回值,并且按照順序壓棧)
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
static int print2(lua_State *L)
{
const char * s = luaL_optlstring (L,1,"啥都沒",NULL); //獲取函數調用時第1個參數為字符串,如果參數不存在或者是nil就返回“啥都沒”,并且把長度放在第四個參數(這里設置為NULL,不獲取長度),
printf("這是:%s\n",s);
return 0; //返回值個數為0
}
int main(void)
{
lua_State *L = luaL_newstate();
luaL_openlibs(L); //加載基本庫(print在其中)
//這里相當于設置全局變量a=image.png"測試"
lua_pushstring(L,"測試");
lua_setglobal(L,"a");
//注冊這個函數到全局表
lua_register(L,"print2",print2);
lua_getglobal(L,"print2"); //把這個函數從全局表中壓棧
lua_getglobal(L,"a"); //參數壓棧
lua_call(L,1,0); //調用函數,1個參數,0個返回值
lua_close(L); //關閉狀態機
return 0;
}