lua和C的交互

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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/74668.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/74668.shtml
英文地址,請注明出處:http://en.pswp.cn/web/74668.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java高并發------守護線程Daemon Thread

文章目錄 1.概念2.生命周期與行為2. 應用場景3. 示例代碼4. 注意事項 1.概念 Daemon &#xff1a; 滴門 在Java中&#xff0c;線程分為兩類&#xff1a;用戶線程(User Thread)和守護線程(Daemon Thread)。 守護線程是后臺線程&#xff0c;主要服務于用戶線程&#xff0c;當所…

Docker存儲策略深度解析:臨時文件 vs 持久化存儲選型指南

Docker存儲策略深度解析&#xff1a;臨時文件 vs 持久化存儲選型指南 一、存儲類型全景對比二、臨時存儲適用場景與風險2.1 最佳使用案例2.2 風險警示 三、持久化存儲技術選型3.1 Volume核心優勢Volume管理命令&#xff1a; 3.2 Bind Mount適用邊界掛載模式對比&#xff1a; 四…

【Linux網絡#18】:深入理解select多路轉接:傳統I/O復用的基石

&#x1f4c3;個人主頁&#xff1a;island1314 &#x1f525;個人專欄&#xff1a;Linux—登神長階 目錄 一、前言&#xff1a;&#x1f525; I/O 多路轉接 為什么需要I/O多路轉接&#xff1f; 二、I/O 多路轉接之 select 1. 初識 select2. select 函數原型2.1 關于 fd_set 結…

高級:微服務架構面試題全攻略

一、引言 在現代軟件開發中&#xff0c;微服務架構被廣泛應用于構建復雜、可擴展的應用程序。面試官通過相關問題&#xff0c;考察候選人對微服務架構的理解、拆分原則的掌握、服務治理的能力以及API網關的運用等。本文將深入剖析微服務架構相關的面試題&#xff0c;結合實際開…

使用MQTTX軟件連接阿里云

使用MQTTX軟件連接阿里云 MQTTX軟件阿里云配置MQTTX軟件設置 MQTTX軟件 阿里云配置 ESP8266連接阿里云這篇文章里有詳細的創建過程&#xff0c;這里就不再重復了&#xff0c;需要的可以點擊了解一下。 MQTTX軟件設置 打開軟件之后&#xff0c;首先點擊添加進行創建。 在阿…

【HFP】藍牙Hands-Free Profile(HFP)核心技術解析

藍牙 Hands-Free Profile&#xff08;HFP&#xff09;作為車載通信和藍牙耳機的核心協議&#xff0c;定義了設備間語音交互的標準化流程&#xff0c;并持續推動著無線語音交互體驗的革新。自2002年首次納入藍牙核心規范以來&#xff0c;HFP歷經多次版本迭代&#xff08;最新為v…

輕量化大模型微調工具XTuner指令微調實戰(下篇)

接著上篇文章《輕量化大模型微調工具XTuner指令微調實戰&#xff08;上篇&#xff09;》來接著寫教程。 一、模型轉換 模型訓練后會自動保存成 PTH 模型&#xff08;例如 iter_500.pth&#xff09;&#xff0c;我們需要利用 xtuner convert pth_to_hf 將其轉換為 HuggingFace…

pyTorch框架使用CNN進行手寫數字識別

目錄 1.導包 2.torchvision數據處理的方法 3.下載加載手寫數字的訓練數據集 4.下載加載手寫數字的測試數據集 5. 將訓練數據與測試數據 轉換成dataloader 6.轉成迭代器取數據 7.創建模型 8. 把model拷到GPU上面去 9. 定義損失函數 10. 定義優化器 11. 定義訓練…

強化學習課程:stanford_cs234 學習筆記(3)introduction to RL

文章目錄 前言7 markov 實踐7.1 markov 過程再敘7.2 markov 獎勵過程 MRP&#xff08;markov reward process&#xff09;7.3 markov 價值函數與貝爾曼方程7.4 markov 決策過程MDP&#xff08;markov decision process&#xff09;的 狀態價值函數7.4.1 狀態價值函數7.4.2 狀態…

操作系統 4.5-文件使用磁盤的實現

通過文件進行磁盤操作入口 // 在fs/read_write.c中 int sys_write(int fd, const char* buf, int count) {struct file *file current->filp[fd];struct m_inode *inode file->inode;if (S_ISREG(inode->i_mode))return file_write(inode, file, buf, count); } 進程…

libreoffice-help-common` 的版本(`24.8.5`)與官方源要求的版本(`24.2.7`)不一致

出現此錯誤的原因主要是軟件包依賴沖突&#xff0c;具體分析如下&#xff1a; ### 主要原因 1. **軟件源版本不匹配&#xff08;國內和官方服務器版本有差距&#xff09; 系統中可能啟用了第三方軟件源&#xff08;如 PPA 或 backports 源&#xff09;&#xff0c;導致 lib…

使用Geotools中的原始方法來操作PostGIS空間數據庫

目錄 前言 一、原生PostGIS連接介紹 1、連接參數說明 2、創建DataStore 二、工程實戰 1、Maven Pom.xml定義 2、空間數據庫表 3、讀取空間表的數據 三、總結 前言 在當今數字化與信息化飛速發展的時代&#xff0c;空間數據的處理與分析已成為眾多領域不可或缺的一環。從…

訊飛語音合成(流式版)語音專業版高質量的分析

一、引言 在現代的 Web 應用開發中&#xff0c;語音合成技術為用戶提供了更加便捷和人性化的交互體驗。訊飛語音合成&#xff08;流式版&#xff09;以其高效、穩定的性能&#xff0c;成為了眾多開發者的首選。本文將詳細介紹在 Home.vue 文件中實現訊飛語音合成&#xff08;流…

走進未來的交互世界:下一代HMI設計趨勢解析

在科技日新月異的今天&#xff0c;人機交互界面&#xff08;HMI&#xff09;設計正以前所未有的速度發展&#xff0c;不斷引領著未來的交互世界。從簡單的按鈕和圖標&#xff0c;到如今的智能助手和虛擬現實&#xff0c;HMI設計不僅改變了我們的生活方式&#xff0c;還深刻影響…

洛谷題單3-P1217 [USACO1.5] 回文質數 Prime Palindromes-python-流程圖重構

題目描述 因為 151 151 151 既是一個質數又是一個回文數&#xff08;從左到右和從右到左是看一樣的&#xff09;&#xff0c;所以 151 151 151 是回文質數。 寫一個程序來找出范圍 [ a , b ] ( 5 ≤ a < b ≤ 100 , 000 , 000 ) [a,b] (5 \le a < b \le 100,000,000…

學習筆記,DbContext context 對象是保存了所有用戶對象嗎

DbContext 并不會將所有用戶對象保存在內存中&#xff1a; DbContext 是 Entity Framework Core (EF Core) 的數據庫上下文&#xff0c;它是一個數據庫訪問的抽象層它實際上是與數據庫的一個連接會話&#xff0c;而不是數據的內存緩存當您通過 _context.Users 查詢數據時&…

本地命令行啟動服務并連接MySQL8

啟動服務命令 net start mysql8 關閉服務命令 net stop mysql8 本地連接MySQL數據庫mysql -u [用戶名] -p[密碼] 這里&#xff0c;我遇到了個問題 —— 啟動、關閉服務時&#xff0c;顯示 “發生系統錯誤 5。拒絕訪問。 ” 解法1&#xff1a;在 Windows 上以管理員身份打開…

數據蒸餾:Dataset Distillation by Matching Training Trajectories 論文翻譯和理解

一、TL&#xff1b;DR 數據集蒸餾的任務是合成一個較小的數據集&#xff0c;使得在該合成數據集上訓練的模型能夠達到在完整數據集上訓練的模型相同的測試準確率&#xff0c;號稱優于coreset的選擇方法本文中&#xff0c;對于給定的網絡&#xff0c;我們在蒸餾數據上對其進行幾…

【spring cloud Netflix】Ribbon組件

1.基本概念 SpringCloud Ribbon是基于Netflix Ribbon 實現的一套客戶端負載均衡的工具。簡單的說&#xff0c;Ribbon 是 Netflix 發布的開源項目&#xff0c;主要功能是提供客戶端的軟件負載均衡算法&#xff0c;將 Netflix 的中間層服務連接在一 起。Ribbon 的客戶端組件提供…

P1036 [NOIP 2002 普及組] 選數(DFS)

題目描述 已知 n 個整數 x1?,x2?,?,xn?&#xff0c;以及 1 個整數 k&#xff08;k<n&#xff09;。從 n 個整數中任選 k 個整數相加&#xff0c;可分別得到一系列的和。例如當 n4&#xff0c;k3&#xff0c;4 個整數分別為 3,7,12,19 時&#xff0c;可得全部的組合與它…