Lua可做爲擴展性語言(Lua能夠做爲程序庫用來擴展應用的功能),同時也是個可擴展的語言(Lua程序中能夠註冊由其餘語言實現的函數)。函數
C和Lua交互的部分稱爲C API。C API是一個C代碼與Lua進行交互的函數集。他由如下部分組成:讀寫Lua全局變量的函數、調用Lua函數的函數、運行Lua代碼片段的函數、註冊C函數而後能夠在Lua中被調用的函數,等等。lua
API中有些函數爲了方便以宏的方式實現。spa
當在Lua和C之間交換數據時咱們面臨着兩個問題:動態與靜態類型系統的不匹配和自動與手動內存管理的不一致。解決辦法是在C和Lua之間通訊關鍵在於一個虛擬的棧。幾乎全部的API調用都是對棧上的值進行操做,全部C與Lua之間的數據交換也都經過這個棧來完成。由於棧是由Lua來管理的,垃圾回收器知道那個值正在被C使用。指針
Lua以一個嚴格的LIFO規則(後進先出;也就是說,始終存取棧頂)來操做棧:
(1)當你調用Lua時,它只會改變棧頂部分。
(2)你的C代碼卻有更多的自由;更明確的來說,你能夠查詢棧上的任何元素,甚至是在任何一個位置插入和刪除元素。code
在調用C API時有幾個重要的頭文件:blog
(1)lua.h:Lua基礎函數庫,lua_前綴
(2)lauxlib.h:輔助庫,luaL_前綴,利用lua.h實現的更高層的抽象
(3)lualib.h:爲了保持Lua的苗條,全部的標準庫以單獨的包提供,因此若是你不須要就不會強求你使用它們。頭文件lualib.h定義了打開這些庫的函數。例如,調用luaopen_io,以建立io table並註冊I/O函數(io.read,io.write等等)到Lua環境中。索引
API用索引來訪問棧中的元素。棧底爲1,依次往上增長,也可用負數索引,-1表示棧頂元素。下面列出一些經常使用的C API函數:內存
lua_push*:壓入棧元素
void lua_pushnil (lua_State *L);
void lua_pushboolean (lua_State *L, int bool);
void lua_pushnumber (lua_State *L, double n);
void lua_pushlstring (lua_State *L, const char *s, size_t length);
void lua_pushstring (lua_State *L, const char *s);rem
lua_to*:從棧中得到值。即便給定的元素類型不正確,調用這些函數也沒問題。
int lua_toboolean (lua_State *L, int index);
double lua_tonumber (lua_State *L, int index);
const char * lua_tostring (lua_State *L, int index);字符串
Lua_tostring函數返回一個指向字符串的內部拷貝的指針。你不能修改它(使你想起那裏有一個const)。只要這個指針對應的值還在棧內,Lua會保證這個指針一直有效。當一個C函數返回後,Lua會清理他的棧,因此,有一個原則:永遠不要將指向Lua字符串的指針保存到訪問他們的外部函數中。
size_t lua_strlen (lua_State *L, int index):返回字符串的實際長度。
int lua_checkstack(lua_State *L, int sz):檢查棧空間。默認有20個空閒的記錄,lua.h中的LUA_MINSTACK宏定義了這個常量。
int lua_is... (lua_State *L, int index):檢查一個元素可否被轉換成指定的類型。
int lua_type (lua_State *L, int idx):返回棧中元素的類型;
const char* lua_typename(lua_State *L, int tp):返回type對應的名字字符串,第二個參數爲lua_type返回的類型
void luaL_checktype (lua_State *L, int arg, int t):返回參數arg是不是類型t,第三個參數爲lua_type的取值。
在lua.h頭文件中,每種類型都被定義爲一個常量:LUA_TNIL、LUA_TBOOLEAN、LUA_TNUMBER、LUA_TSTRING、LUA_TTABLE、LUA_TFUNCTION、LUA_TUSERDATA以及LUA_TTHREAD。
int lua_gettop (lua_State *L):返回棧中元素個數,它也是棧頂元素的索引。
void lua_settop (lua_State *L, int index):設置棧頂元素的索引,至關於設置棧的大小。若是開始的棧頂高於新的棧頂,頂部的值被丟棄。不然,爲了獲得指定的大小這個函數壓入相應個數的空值(nil)到棧上。lua_settop(L,0):清空堆棧。
#define lua_pop(L,n) lua_settop(L, -(n)-1):宏定義,彈出n個元素。
void lua_pushvalue (lua_State *L, int index):壓入堆棧上指定索引的一個摶貝到棧頂,等於拷貝index處的元素,而後添加到棧頂。
void lua_remove (lua_State *L, int index):移除指定索引的元素,並將其上面全部的元素下移來填補這個位置的空白。
void lua_insert (lua_State *L, int index):移動棧頂元素到指定索引的位置,並將這個索引位置上面的元素所有上移至棧頂被移動留下的空隔。
void lua_replace (lua_State *L, int index):從棧頂彈出元素值並將其設置到指定索引位置,沒有任何移動操做。
下面來看一段Lua和C++簡單交互的代碼:
#include <stdio.h> #include <string.h> extern "C" { #include <lua.h> #include <lauxlib.h> #include <lualib.h> } int main (void) { char buff[256]; int error; lua_State *L = lua_open(); /* opens Lua */ luaL_openlibs(L); while (fgets(buff, sizeof(buff), stdin) != NULL) { error = luaL_loadbuffer(L, buff, strlen(buff), "line") || lua_pcall(L, 0, 0, 0); if (error) { fprintf(stderr, "%s", lua_tostring(L, -1)); lua_pop(L, 1);/* pop error message from the stack */ } } lua_close(L); return 0; }
上述代碼在VS2010中正常運行,Lua使用5.1的版本,其中有幾點須要注意的:
(1)lua5.0以前初始化庫的用法以下:
luaopen_base(L); /* opens the basic library */ luaopen_table(L); /* opens the table library */ luaopen_io(L); /* opens the I/O library */ luaopen_string(L); /* opens the string lib. */ luaopen_math(L); /* opens the math lib. */
但5.0以後只須要一句話便可:
luaL_openlibs(L);
(2)須要添加依賴庫:lua5.1.lib lua51.lib。
下面這個例子能夠有助於加深對C和Lua之間堆棧的理解:
// 從棧底到棧頂依次遍歷整個堆棧 static void stackDump(lua_State* L) { int i; int top = lua_gettop(L); for(i = 1; i <= top; ++i) { int t = lua_type(L, i); switch(t) { case LUA_TSTRING: printf("'%s'", lua_tostring(L, i)); break; case LUA_TBOOLEAN: printf(lua_toboolean(L, i) ? "true": "false"); break; case LUA_TNUMBER: printf("'%g'", lua_tonumber(L, i)); break; default: printf("'%s'", lua_typename(L, t)); break; } printf(" "); } printf("\n"); } int main(void) { lua_State* L = lua_open(); luaL_openlibs(L); lua_pushboolean(L, 1); lua_pushnumber(L, 10); lua_pushnil(L); lua_pushstring(L, "hello"); stackDump(L); lua_pushvalue(L, -4); stackDump(L); lua_replace(L, 3); stackDump(L); lua_settop(L, 6); stackDump(L); lua_remove(L, -3); stackDump(L); lua_settop(L, -5); stackDump(L); return 0; }
注意lua_replace首先會彈出棧頂元素,而且須要注意的是lua_replace(L, -1);語句會致使站頂元素彈出,其餘元素不變。
做爲配置語言是LUA的一個重要應用,下面看一個簡單的這方面的例子。有一個含有簡單字段記錄的lua文件以下:
width = 200 height = 300
對該lua文件的解析大媽以下:
#include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> extern "C" { #include <lua.h> #include <lauxlib.h> #include <lualib.h> } void error(lua_State* L, const char* fmt, ...) { va_list argp; va_start(argp, fmt); vfprintf(stderr, fmt, argp); va_end(argp); lua_close(L); exit(EXIT_FAILURE); } void load(char* filename, int* width, int* height) { lua_State* L = lua_open(); luaL_openlibs(L); if(luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0)) { error(L, "cannot run configuration file: %s", lua_tostring(L, -1)); } lua_getglobal(L, "width"); lua_getglobal(L, "height"); if(!lua_isnumber(L, -2)) error(L, "'width' should be a number\n"); if(!lua_isnumber(L, -1)) error(L, "'height' should be a number\n"); *width = (int)lua_tonumber(L, -2); *height = (int)lua_tonumber(L, -1); lua_close(L); } int main(void) { int width, height; load("c:\\luatest\\cfg.lua", &width, &height); printf("width = %d height = %d \n", width, height); return 0; }