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.mzph.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;可得全部的组合与它…