1.C調用lua例子
#include <iostream>
#include <lua.hpp>int main()
{//用于創建一個新的lua虛擬機lua_State* L = luaL_newstate();luaL_openlibs(L);//打開標準庫/*if (luaL_dofile(L, "test.lua") != LUA_OK) {std::cerr << "Lua error: " << lua_tostring(L, -1) << std::endl;}luaL_dofile = luaL_loadfile + lua_pcall(L, 0, 0, 0);*//*編譯和解析lua代碼 但是并不會運行lua代碼*/if (luaL_loadfile(L, "test.lua") != LUA_OK){std::cerr << "Lua error: " << lua_tostring(L, -1) << std::endl;return -1;}/*全局變量和函數此時才會注冊到lua虛擬機里面 如果不調用lua_pcall 將獲取不到函數*/lua_pcall(L, 0, 0, 0);lua_getglobal(L, "str");lua_pushstring(L, "ZZH ");lua_pushnumber(L, 25);// 調用函數 (2個參數,1個返回值, 錯誤處理函數的index是0 表示沒有)if (lua_pcall(L, 2, 1, 0) != 0) { std::cerr << "Error: " << lua_tostring(L, -1) << std::endl;return 1;}// 獲取返回值 在棧頂const char *str = lua_tostring(L, -1); lua_pop(L, 1); // 彈出返回值std::cout << "Result: " << str << std::endl;//關閉lua虛擬機lua_close(L);return 0;
}
2.lua虛擬棧
lua是一個腳本,經常用來和宿主程序交互,他們之間通過lua虛擬機溝通
lua的堆棧是用數字索引的,1代表棧底, -1代表棧頂
我們可以看到在上面C調用lua的時候,先獲取到lua中的全局函數str
lua_getglobal這個函數將獲取到的參數壓入棧頂,然后依次調用lua_pushxxxx函數將兩個參數也壓入棧頂。
lua_pcall這個函數是用來調用lua函數的,lua_pcall的第一個參數是lua虛擬機,第二個參數是函數參數個數,第三個參數是期望的返回值個數,第四個參數是錯誤處理函數在棧中的位置。
執行完lua_pcall之后,lua的函數棧自己清理,然后將結果放到棧頂,我們獲取到結果之后,必須調用lua_pop將返回值彈出,否則會內存泄漏
3.往棧中壓入元素
lua的棧中可以壓入任意的lua類型
4.從棧中獲取一個值,index是棧中的位置
5.判斷棧中元素類型
6.通用棧操作
1.lua_gettop返回棧中元素的個數,也就是棧頂索引
2.lua_settop將棧頂設置為一個值,即修改棧中元素數量,多了補nil
3.lua_pushvalue 將棧中指定索引位置處的元素拷貝一份到棧頂
4.lua_rotate 將棧中指定index位置處的元素,向上/向下移動
5.lua_replace彈出一個值,并且棧頂設置為索引上的值
6.lua_copy將一個索引處的值復制到另外一個索引上
7.對table的操作函數
8.lua調用C函數
C函數要想被lua調用,那么函數原型必須是
int (*Cfun)(lua_State*L);返回值代表了參數個數
{return 1; //代表往棧中壓入了1個返回值
}
舉個例子
int Cadd(lua_State* L)
{//lua壓入的第一個參數int a = lua_tonumber(L, 1);//lua壓入的第二個參數int b = lua_tonumber(L, 2);int c = a + b;std::cout << "a = " << a << " b = " << b << std::endl;lua_pushnumber(L, c);//返回一個參數return 1;
}int main()
{//用于創建一個新的lua虛擬機lua_State* L = luaL_newstate();luaL_openlibs(L);//打開標準庫//將C函數放入到lua的虛擬棧上lua_pushcfunction(L, Cadd);//將Cadd這個C函數注冊到globallua_setglobal(L, "Cadd");if (luaL_dofile(L, "test.lua") != LUA_OK) {std::cerr << "Lua error: " << lua_tostring(L, -1) << std::endl;}//關閉lua虛擬機lua_close(L);return 0;
}--lua代碼function CallAdd()print("lua call Cfun ret = " .. Cadd(10, 20))
endCallAdd()
結果
9.lua_Reg批量導入C函數
int Cadd(lua_State* L)
{int a = luaL_checkinteger(L, 1);int b = luaL_checkinteger(L, 2);std::cout << "Cadd a=" << a << " b = " << b << std::endl;lua_pushinteger(L, a + b);return 1;
}int Csub(lua_State* L)
{int a = luaL_checkinteger(L, 1);int b = luaL_checkinteger(L, 2);std::cout << "Csub a=" << a << " b = " << b << std::endl;lua_pushinteger(L, a - b);return 1;
}luaL_Reg g_Cfuns[] = {{"Cadd", Cadd},{"Csub", Csub},{"NULL", nullptr}
};int main()
{//用于創建一個新的lua虛擬機lua_State* L = luaL_newstate();luaL_openlibs(L);//打開標準庫luaL_newlib(L, g_Cfuns);lua_setglobal(L, "g_Cfuns"); // 將 table 綁定到全局變量 "g_Cfuns"if (luaL_dofile(L, "test.lua") != LUA_OK) {std::cerr << "Lua error: " << lua_tostring(L, -1) << std::endl;}//關閉lua虛擬機lua_close(L);return 0;
}
function CallAdd()print("lua call Cfun ret = " .. g_Cfuns.Cadd(10, 20))
print("lua call Cfun ret = " .. g_Cfuns.Csub(20, 10))
endCallAdd()
拿一段skynet中的代碼來演示另外一種用法
luaL_Reg l[] = {{ "start", lstart }, // 綁定 C 函數 lstart{ "stop", lstop }, // 綁定 C 函數 lstop{ "resume", luaB_coresume },// 綁定 C 函數 luaB_coresume{ "wrap", luaB_cowrap }, // 綁定 C 函數 luaB_cowrap{ NULL, NULL }, // 結束標記
};luaL_newlibtable(L, l); // 1. 創建一個與 l 數組大小相同的 table(但此時 table 為空)lua_newtable(L); // 2. 創建一個新的 table(用于存儲線程 start 時間)
lua_newtable(L); // 3. 創建一個新的 table(用于存儲線程 total 運行時間)lua_newtable(L); // 4. 創建一個弱表(用于存儲線程數據)lua_pushliteral(L, "kv"); // 5. 壓入字符串 "kv",表示 key-value 方式的弱表模式
lua_setfield(L, -2, "__mode"); // 6. 設置 `__mode` 字段,使該 table 變成弱引用表lua_pushvalue(L, -1); // 7. 復制弱表,使兩個線程時間表共享相同的弱表
lua_setmetatable(L, -3); // 8. 設置 `thread->start time` 表的 metatable(弱引用)
lua_setmetatable(L, -3); // 9. 設置 `thread->total time` 表的 metatable(弱引用)luaL_setfuncs(L, l, 2); // 10. 綁定 l 中的 C 函數,并將棧頂的 2 個 table 作為 upvalue 傳入
10.數組操作 lua_geti
第一個參數是lua虛擬機,第二個參數是數組在棧中的索引,第三個參數是在表中的下標
int visitArr(lua_State* L)
{luaL_checktype(L, 1, LUA_TTABLE); //檢驗是否是tablelua_len(L, 1);int len = lua_tointeger(L, -1);lua_pop(L, 1);for (int i = 1; i <= len; i++){lua_pushvalue(L, 1);lua_geti(L, 1, i);std::cout << " i = " << i << " num=" << lua_tointeger(L, -1) << std::endl;lua_pop(L, 1);}return 0;
}luaL_Reg g_Cfuns[] = {{"visitArr", visitArr},{"NULL", nullptr}
};int main()
{//用于創建一個新的lua虛擬機lua_State* L = luaL_newstate();luaL_openlibs(L);//打開標準庫luaL_newlib(L, g_Cfuns);lua_setglobal(L, "g_Cfuns"); // 將 table 綁定到全局變量 "g_Cfuns"if (luaL_dofile(L, "test.lua") != LUA_OK) {std::cerr << "Lua error: " << lua_tostring(L, -1) << std::endl;}//關閉lua虛擬機lua_close(L);return 0;
}function testArr()g_Cfuns.visitArr({7,5,3,4})
endtestArr()
11.字符串的操作
當C函數接收到一個lua字符串為參數時,必須遵守兩個原則:1.使用字符串期間不能將其從棧中彈出 2.不應該修改字符串
const char* str = "Hello World";lua_getglobal(L, "testStr");lua_pushlstring(L, str + 0, strlen(str));function testStr(str)print("lua str = " .. str)end
一旦調用了lua_pushlstring,那么在lua棧上就會產生一個字符串的副本,lua會管理這部分內存,C上面的字符串釋放與否,都沒關系
lua_pushfstring
12.在C函數中保存狀態
注冊表registry,也就等于是提供了一個全局變量吧,但是lua全局變量會被lua代碼訪問修改 不安全
注冊表是一個只能被C代碼訪問的全局表, 總是位于LUA_REGISTRYINDEX中,偽索引就像是棧中的索引,但是關聯的值不在棧中。
獲取注冊表中鍵為key的值,可以使用
lua_getfield(L, LUA_REGISTRYINDEX, "key");設置注冊表中鍵為key的值
lua_pushlightuserdata(L, 一個指針);
lua_setfield(L, LUA_REGISTRYINDEX, "key");
注冊表是一個普通的lua表,但是不能用數字作為鍵
upvalue上值實現了一種類似于C語言靜態變量的機制,每一次在lua中創建新的C函數時候,可以將任意數量的上值和這個函數關聯起來,每個上值保存一個lua值,后面調用該函數的時候,通過偽索引自由訪問這些值
#include <lua.h>
#include <lauxlib.h>// 計數器增加
int lua_counter_add(lua_State *L) {int count = lua_tointeger(L, lua_upvalueindex(1));count += luaL_checkinteger(L, 1); // 加上 Lua 傳入的值lua_pushinteger(L, count);lua_replace(L, lua_upvalueindex(1)); // 更新 upvaluereturn 1;
}// 計數器讀取
int lua_counter_get(lua_State *L) {lua_pushinteger(L, lua_tointeger(L, lua_upvalueindex(1))); // 返回 upvaluereturn 1;
}// 初始化模塊
int luaopen_counter(lua_State *L) {lua_pushinteger(L, 0); // 計數器初始值 0lua_pushcclosure(L, lua_counter_add, 1); // 綁定 upvaluelua_setglobal(L, "add_count"); // 綁定到全局變量 add_countlua_pushinteger(L, 0); // 計數器初始值 0lua_pushcclosure(L, lua_counter_get, 1); // 綁定 upvaluelua_setglobal(L, "get_count"); // 綁定到全局變量 get_countreturn 0;
}add_count(5)
add_count(3)
print(get_count()) -- 8
add_count(2)
print(get_count()) -- 10
luaL_setfuncs 這個也可以為函數集合設置他們的upvalue
13.require
14.C中的 用戶自定義類型
一個簡單的例子
struct Pos
{int x;int y;
};int setX(lua_State* L)
{Pos * p = (Pos*)lua_touserdata(L, 1);int v = lua_tointeger(L, 2);p->x = v;return 0;
}int setY(lua_State* L)
{Pos* p = (Pos*)lua_touserdata(L, 1);int v = lua_tointeger(L, 2);p->y = v;return 0;
}int getX(lua_State* L)
{Pos* p = (Pos*)lua_touserdata(L, 1);lua_pushinteger(L, p->x);return 1;
}int getY(lua_State* L)
{Pos* p = (Pos*)lua_touserdata(L, 1);lua_pushinteger(L, p->y);return 1;
}int createNewPos(lua_State* L)
{Pos * p = (Pos*)lua_newuserdata(L, sizeof(Pos));int x = lua_tointeger(L, 1);int y = lua_tointeger(L, 2);p->y = y;p->x = x;return 1;
}luaL_Reg g_Cfuns[] = {{"createNewPos", createNewPos},{"setX", setX},{"setY", setY},{"getX", getX},{"getY", getY},{"NULL", nullptr}
};int main()
{//用于創建一個新的lua虛擬機lua_State* L = luaL_newstate();luaL_openlibs(L);//打開標準庫luaL_newlib(L, g_Cfuns);lua_setglobal(L, "g_Cfuns"); // 將 table 綁定到全局變量 "g_Cfuns"if (luaL_dofile(L, "test.lua") != LUA_OK) {std::cerr << "Lua error: " << lua_tostring(L, -1) << std::endl;}//關閉lua虛擬機lua_close(L);return 0;
}local pos = g_Cfuns.createNewPos(10, 20)
print(g_Cfuns.getX(pos))
但是這個用法不規范,lua不能直接調用他的方法 也不能安全訪問字段,一般需要綁定元表
#include <lua.hpp>
#include <iostream>struct Pos {int x;int y;
};int setX(lua_State* L) {Pos* p = (Pos*)luaL_checkudata(L, 1, "PosMeta"); // 安全獲取 userdatap->x = luaL_checkinteger(L, 2);return 0;
}int setY(lua_State* L) {Pos* p = (Pos*)luaL_checkudata(L, 1, "PosMeta");p->y = luaL_checkinteger(L, 2);return 0;
}int getX(lua_State* L) {Pos* p = (Pos*)luaL_checkudata(L, 1, "PosMeta");lua_pushinteger(L, p->x);return 1;
}int getY(lua_State* L) {Pos* p = (Pos*)luaL_checkudata(L, 1, "PosMeta");lua_pushinteger(L, p->y);return 1;
}int createNewPos(lua_State* L) {int x = luaL_checkinteger(L, 1);int y = luaL_checkinteger(L, 2);Pos* p = (Pos*)lua_newuserdata(L, sizeof(Pos));p->x = x;p->y = y;luaL_getmetatable(L, "PosMeta");lua_setmetatable(L, -2); // 綁定元表return 1;
}luaL_Reg pos_methods[] = {{"setX", setX},{"setY", setY},{"getX", getX},{"getY", getY},{nullptr, nullptr}
};extern "C" int luaopen_mylib(lua_State* L) {luaL_newmetatable(L, "PosMeta");lua_pushvalue(L, -1);lua_setfield(L, -2, "__index"); // 讓對象可以用 `pos:getX()` 訪問方法luaL_setfuncs(L, pos_methods, 0); // PosMeta 綁定方法 lua_newtable(L);lua_pushcfunction(L, createNewPos);lua_setfield(L, -2, "createNewPos");return 1;
}int main() {lua_State* L = luaL_newstate();luaL_openlibs(L);luaopen_mylib(L); // 注冊模塊lua_setglobal(L, "PosLib");if (luaL_dofile(L, "test.lua") != LUA_OK) {std::cerr << "Lua error: " << lua_tostring(L, -1) << std::endl;}lua_close(L);return 0;
}------lua
local pos = PosLib.createNewPos(10, 20)print(pos:getX()) -- 10
print(pos:getY()) -- 20pos:setX(30)
pos:setY(40)print(pos:getX()) -- 30
print(pos:getY()) -- 40