Lua C API

C API

  • 云風Blog:Lua C API 的正確用法
  • C讀取和調(diào)用Lua文件的庫:lua.h, lauxlib.h, lualib.h
  • 包括:讀寫Lua全局變量的函數(shù)、調(diào)用Lua函數(shù)的函數(shù)、運行Lua代碼片段的函數(shù)、注冊C函數(shù)然后可以在Lua中被調(diào)用的函數(shù)
  • C和Lua之間的數(shù)據(jù)交換,通過對棧上的值進行操作。棧的使用解決:Lua會自動進行垃圾回收,而C要求顯示的分配內(nèi)存單元;Lua中的動態(tài)類型和C的靜態(tài)類型。
  • 壓入元素
void lua_pushnil(lua_State *L);                                        //插入空值
void lua_pushboolean(lua_State *L, int bool);                        //插入布爾值
void lua_pushnumber(lua_State *L, double n);                        //插入double
void lua_pushlstring(lua_State *L, const char* s, size_t length);    //插入任意字符串
void lua_pushstring(lua_State *L, const char* s);                    //插入帶'\0'的字符串
  • 查詢元素
    1表示第一個被壓棧的元素;-1表示棧頂元素;當一個C函數(shù)返回后,Lua會清理他的棧,所以,有一個原則:永遠不要將指向Lua字符串的指針保存到訪問他們的外部函數(shù)中。
int lua_is*(lua_State *L, int index);        //檢查一個元素是否指定類型(number,string,boolean,table)

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);
size_t lua_strlen(lua_State *L, int index);
  • 其他堆棧操作
int lua_gettop(lua_State *L);                    //返回堆棧中的元素個數(shù)
void lua_settop(lua_State *L, int index);        //設置棧頂為一個指定的值,多余值被拋棄,否則壓入nil值
void lua_pop(lua_State *L, int n);                //lua_settop(L, -n - 1),從堆棧中彈出n個元素
void lua_pushvalue(lua_State *L, int 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);        //從棧頂中彈出元素并將其設置到指定索引位置
  • 錯誤處理
    應用程序中(調(diào)用Lua的C代碼)的錯誤處理:當Lua遇到類似內(nèi)存分配失敗的情況,大部分會拋出異常,調(diào)用panic函數(shù)退出應用;其余情況會忽略異常,在保護模式下運行代碼(Lua通過調(diào)用lua_pcall來運行)。
    類庫(被Lua調(diào)用的C函數(shù))中的錯誤處理:C函數(shù)發(fā)現(xiàn)錯誤只要調(diào)用luaL_error函數(shù),清理所有在Lua中需要被清理的,然后和錯誤信息一起回到最初執(zhí)行lua_pcall的地方。
  • 表操作
void lua_gettable(lua_State *L, int idx);                    //以棧頂元素為key值,獲取指定索引的表的值到棧頂
void lua_getfield(lua_State *L, int idx, const char *k);    //獲取指定索引的表對應key的值到棧頂
void lua_getglobal(lua_State *L, const char *name);            //等于lua_getfield(L, LUA_GLOBALSINDEX, (name))。獲取全局表的變量到棧頂
void lua_settable(lua_State *L, int idx);                    //以棧頂元素為value,棧頂下一元素為key,設置指定索引的表的值
void lua_setfield(lua_State *L, int idx, const char *k);    //彈出棧頂元素,并設置為指定索引的表對應key的值
void lua_setglobal(lua_State *L, const char *name);            //等于lua_setfield(L, LUA_GLOBALSINDEX, (name))。設置全局變量的值
void lua_rawgeti(lua_State *L, int idx, int n);                //獲得idx索引的表以n為key的值
void lua_rawget(lua_State *L, int idx);                        //獲得idx索引的表以棧頂為key的值
void lua_rawseti(lua_State *L, int idx, int n);                //設置idx索引的表以n為key的值
void lua_rawset(lua_State *L, int idx);                        //設置idx索引的表以棧頂下一個元素為key的值
  • 調(diào)用函數(shù)
    使用API調(diào)用函數(shù)的方法是很簡單的:首先,將被調(diào)用的函數(shù)入棧;第二,依次將所有參數(shù)入棧;第三,使用lua_pcall調(diào)用函數(shù);最后,從棧中獲取函數(shù)執(zhí)行返回的結果。
    在將結果入棧之前,lua_pcall會將棧內(nèi)的函數(shù)和參數(shù)移除。如果lua_pcall運行時出現(xiàn)錯誤,lua_pcall會返回一個非0的結果,并將錯誤信息入棧(依然會先移除棧內(nèi)函數(shù)和參數(shù))
    錯誤處理函數(shù)需要在被調(diào)用函數(shù)和參數(shù)之前入棧,參數(shù)errfunc為錯誤函數(shù)在棧中的索引。一般錯誤返回LUA_ERRRUN。內(nèi)存分配錯誤返回LUA_ERRMEM;在錯誤處理函數(shù)中出錯返回LUA_ERRERR,這兩種情況并不會調(diào)用錯誤處理函數(shù)。
int  lua_pcall(lua_State *L, int nargs, int nresults, int errfunc);        //調(diào)用棧頂函數(shù),指定參數(shù)格式nargs,返回結果個數(shù),nresults,和錯誤函數(shù)
  • 調(diào)用C函數(shù)
    Lua調(diào)用C函數(shù),使用棧進行交互,用來交互的棧不是全局變量,每一個函數(shù)都有他自己的私有棧,第一個參數(shù)總是在這個私有棧的index = 1的位置。
    一個Lua庫實際上是一個定義了一系列Lua函數(shù)的chunk,并將這些函數(shù)保存在適當?shù)牡胤剑ǔW鳛?code>table 的域來保存。LuaC庫就是這樣實現(xiàn)的。
    在宿主程序中調(diào)用C函數(shù)
//注冊被Lua調(diào)用的C函數(shù)的格式,以a+b為例
static int func(lua_State *L)
{
    //從棧中獲取函數(shù)參數(shù)并檢查合法性,1表示Lua調(diào)用時的第一個參數(shù),以此類推
    double arg1 = luaL_checknumber(L, 1);
    double arg2 = luaL_checknumber(L, 2);
    
    //將函數(shù)結果入棧,如果有多個返回值,可以多次入棧
    lua_pushnumber(L, arg1 + arg2);
    
    //返回值表示該函數(shù)的返回值的數(shù)量
    return 1;
}

int main()
{
    lua_State* L = luaL_newstate();
    luaL_openlibs(L);
    
    //注冊被Lua調(diào)用的C函數(shù),參數(shù)"add"表示Lua調(diào)用時使用的全局函數(shù)名,func為被調(diào)用的C函數(shù)
    lua_register(L, "add", func);
    luaL_dostring("print(add(1, 2))");
    
    lua_close(L);
    return 0;
}

C函數(shù)庫

//注冊被Lua調(diào)用的C函數(shù)的格式,以a+b為例,函數(shù)必須以C的形式被導出
extern "C" int func(lua_State *L)
{
    //從棧中獲取函數(shù)參數(shù)并檢查合法性,1表示Lua調(diào)用時的第一個參數(shù),以此類推
    double arg1 = luaL_checknumber(L, 1);
    double arg2 = luaL_checknumber(L, 2);
    
    //將函數(shù)結果入棧,如果有多個返回值,可以多次入棧
    lua_pushnumber(L, arg1 + arg2);
    
    //返回值表示該函數(shù)的返回值的數(shù)量
    return 1;
}

static luaL_Reg mylibs[] = 
{
    {"add", func},
    {NULLL, NULL},
};

//注冊mylibs庫,Lua中使用該庫的用法:testlibs.add(1, 2)
extern "C" __declspec(dllexport)
int luaopen_testlibs(lua_State* L) 
{
    luaL_newlib(L, mylibs);
    return 1;
}
  • 字符串處理
    C函數(shù)接受一個來自lua的字符串作為參數(shù)時,有兩個規(guī)則必須遵守:當字符串正在被訪問的時候不要將其出棧;永遠不要修改字符串。
    C函數(shù)需要創(chuàng)建一個字符串返回給lua的時候,C代碼負責緩沖區(qū)的分配和釋放,負責處理緩沖溢出等情況。
void lua_concat(lua_State *L, int n);        //連接棧頂開始的n個字符串,彈出這n個字符串并壓棧結果
const char *lua_pushfstring(lua_State *L, const char *fmt, ...);    //根據(jù)格式串fmt的要求創(chuàng)建一個新的字符串。
  • 注冊表
    使用LUA_REGISTRYINDEX索引來保存注冊表中的Lua值。任何C庫都可以在這張表里保存數(shù)據(jù), 為了防止沖突,可以使用保護庫名前綴的名字作為key值。注冊表中的整數(shù)key用于應用機制luaL_ref。
  • 閉包
    一個C函數(shù)和它的upvalues的組合稱為閉包。upvalues為函數(shù)能訪問的外部局部變量。
static int counter(lua_State *L)
{
    double val = lua_tonumber(L, lua_upvalveindex(1));
    lua_pushnumber(L, ++val);
    lua_pushvalue(L, -1);
    lua_replace(L, lua_upvalueindex(1));
    return 1;
}

int newCounter(lua_State *L)
{
    lua_pushnumber(L, 0);
    //第二個參數(shù)為基本函數(shù),第三個參數(shù)是upvalues的個數(shù)
    lua_pushcclosure(L, &counter, 1);
    return 1;
}
  • Userdata
void *lua_newuserdata(lua_State *L, size_t size);        //按照指定size的大小分配一段內(nèi)存放入棧內(nèi),并返回這個地址
  • Metatable
int luaL_newmetatable(lua_State *L, const char* tname);             //棧頂創(chuàng)建metatable,建立表和registry中類型名的雙向關系(分別以tname為表的key和以表為tname的key)
void luaL_getmetatable(lua_State *L, const char* tname);             //獲取registry中tname對應的表
void* luaL_checkudata(lua_State *L, int index, const char* tname);    //檢查指定索引的元素是否為帶有給定tname的metatable的useratum,如果是,返回useratum的地址,否則返回NULL
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 第一篇 語言 第0章 序言 Lua僅讓你用少量的代碼解決關鍵問題。 Lua所提供的機制是C不擅長的:高級語言,動態(tài)...
    testfor閱讀 2,954評論 1 7
  • Nginx API for Lua Introduction ngx.arg ngx.var.VARIABLE C...
    吃瓜的東閱讀 6,096評論 0 5
  • 第5章 引用類型(返回首頁) 本章內(nèi)容 使用對象 創(chuàng)建并操作數(shù)組 理解基本的JavaScript類型 使用基本類型...
    大學一百閱讀 3,677評論 0 4
  • 原文地址:C語言函數(shù)調(diào)用棧(一)C語言函數(shù)調(diào)用棧(二) 0 引言 程序的執(zhí)行過程可看作連續(xù)的函數(shù)調(diào)用。當一個函數(shù)執(zhí)...
    小豬啊嗚閱讀 4,971評論 1 19
  • 鐵皮火車停在一個小站 有一陣一動不動。 門怦然關上,鋪路石踩在腳下, 有人道著永別。 一隻手套墜下,日影轉(zhuǎn)暗。 門...
    東豐林波閱讀 292評論 0 0

友情鏈接更多精彩內(nèi)容