如何在Lua与C/C++之间实现table数据的交换

为什么80%的码农都做不了架构师?>>> hot3.png

    之前在《C/C++和Lua是如何进行通信的?》一文中简单的介绍了lua与宿主之间的通信。简单的说两种不同的语言之间数据类型不一样又如何进行数据交换呢?那就是lua_State虚拟栈,通过栈操作和lua库函数,我们很轻松就能完成两者之间的数据交换。

    开始之前,明确几个问题,lua中的虚拟栈的索引编号问题(我们假设栈大小为n),编号1是栈底,n视栈顶,编号-1是栈顶,-n是栈底。lua中的库函数需要访问和操作栈上的数据都是通过索引编号定位的。但是我们需要明确一点,有些API并没有使用索引编号作为参数,意味着默认对栈顶进行操作。如lua_pushnumber(L, 66)将数值66压入栈顶,lua_tonumber(L, -1)取编号-1(栈顶)元素等等,如果这些基本知识和API的都已经熟悉了,那么lua与宿主之间的数据交换就很容易理解了。

    姿势准备好了,那么问题来了。需求:我们现在要设计一个UI界面,我们希望这个UI是可以重用的。为了满足这个需求,显然我们必须将UI界面与显示数据分离。使用lua初始化数据后,将数据传递给UI界面然后显示。这样如果需求变更(游戏开发中经常产生这样的需求),我们也只需改变lua脚本就能重用UI界面,听上去真是程序猿的福音啊~~

    用于显示UI的数据必定很多,需要使用lua中的table来封装这些数据,现在给定如下lua table数据:

local tTest = 
{gdp = 1234,info = "this is test about exchange table data!",task = {12, 23, 34, 45},
};

我们的脚本将调用一个程序封装好的c API(TestTable函数),然后将tTest作为参数,压入虚拟栈中,如下:

local tRet = TestTable(tTest);

虽然tTest table已经传给了程序,我们还需要对TestTable这个c API进行定制,使它能够正确的理解这个table中的数据,实现代码如下(LuaTestTable函数类型是lua_CFuntion类型,注册到lua虚拟机中的函数名为TestTable):

int LuaTestTable(lua_State* L)
{printf("stack size = %d\n", lua_gettop(L)); //打印栈中元素的个数lua_pushstring(L, "gdp");            //将gdp字符串压入栈顶//根据栈顶的key获取table中的value,将key(这里的“gdp”)移除,再将value压入栈顶lua_gettable(L, 1);                      printf("%s\n", lua_tostring(L, -1)); //取栈顶元素(注意这里的整型值都是string类型)lua_pop(L, 1); //取完之后清理栈顶printf("stack size = %d\n", lua_gettop(L)); //打印栈中元素的个数lua_pushstring(L, "info");   //同上lua_gettable(L, 1);printf("%s\n", lua_tostring(L, -1));lua_pop(L, 1);printf("stack size = %d\n", lua_gettop(L));lua_pushstring(L, "task"); //这里的value值是一个table哦,没关系栈操作都是一样的lua_gettable(L, 1);for (int i = 0; i < 4; ++i){lua_pushnumber(L, i+1);lua_gettable(L, -2);printf("%s\n", lua_tostring(L, -1));lua_pop(L, 1);}lua_pop(L, 1);printf("stack size = %d\n", lua_gettop(L)); //到这里tTest表依然在栈底,但不影响后面的操作。//------华丽的分割线------------////到这里table数据的解析就结束了,以下内容是c API给lua返回table数据lua_newtable(L);//要给lua脚本返回一个table类型,先要new一个,压入栈顶lua_pushnumber(L, 1); //将key先压入栈lua_pushstring(L, "table2lua"); //再将value压入栈lua_settable(L, -3);//settable将操作-2,-1编号的键值对,设置到table中,并把key-value从栈中移除lua_pushstring(L, "key"); //同上lua_newtable(L); //这里有个子tablelua_pushstring(L, "capi");//这里的value类型使用lua_CFunction类型,可用做c API调用,函数实现请参看附录1lua_pushcfunction(L, LuaSayHello); lua_settable(L, -3);lua_pushnumber(L, 2);lua_pushnumber(L, 10086);lua_settable(L, -3);lua_settable(L, -3); //这个从这里“lua_pushstring(L, "key"); //同上”开始匹配的printf("stack size = %d\n", lua_gettop(L));return 1; //返回栈顶1个元素
}

需要说明的是在lua中tTest["gdp"]和tTest.gdp的调用形式是一样的,这是lua的语法糖。当然操作栈中的table方法除了lua_gettable和lua_settable还有其它方法,请参看lua_rawget和lua_rawset。理解栈中的元素变化是非常重要的。

    LuaTestTable函数API的后面部分介绍了构造一个任意table作为返回值,返回给lua脚本。首先使用lua_newtable库函数新建一个table类型的数据,并压入栈。然后将键值对key-value依次压入栈,调用lua_settable(L, index)将key-value设置到table中,子table操作也是一样的(这里的index指的是要设置的table在栈中的索引编号)。

    好了,基本介绍完了,最后来编写脚本,看看效果(以下是程序调用的脚本):

--file: test.lua
local tTest = {gdp = 1234,info = "this is test about exchange table data!",task = {12, 23, 34, 45},
};
local tRet = TestTable(tTest);
printTable(tRet);    //实现请参看附录2
tRet.key.capi(); //实现请参看附录1

TestTable成功解析tTest的数据,并且返回一个table类型的tRet。

printTable(tRet);

printTable简单的实现了一个table打印的脚本将tRet打印输出。

tRet.key.capi();

脚本调用tRet中返回的c API类型的函数。具体实现请参见附录。

运行结果:

stack size = 1  //以下是LuaTestTable的输出
1234
stack size = 1
this is test about exchange table data!
stack size = 1
12
23
34
45
stack size = 1   
stack size = 2   //以下是printTable的输出
{[1] = table2lua[key] = {[2] = 10086[capi] = function: 0x409795}
}
Lua call c/c++:SayHello() //这里是 [capi] = function: 0x409795被调用的输出
Hello Everyone!

综上述,我们只需要修改tTest中的数据(结构不能改,改了需要修改LuaTestTable函数)就能改变UI界面的显示,成功的解决了UI界面的复用问题。(文中没有具体讲到如何UI截面相关的细节,请参照例子自行脑补。)

附录1:

//注册到lua虚拟机中的c API函数
int LuaSayHello(lua_State* L)
{printf("Lua call c/c++:SayHello()\n");printf("Hello Everyone!\n");return 0;
}

附录2:

//脚本函数实现打印一个table
function printTable(t, n)if "table" ~= type(t) thenreturn 0;endn = n or 0;local str_space = "";for i = 1, n dostr_space = str_space.."  ";endprint(str_space.."{");for k, v in pairs(t) dolocal str_k_v = str_space.."  ["..tostring(k).."] = ";if "table" == type(v) thenprint(str_k_v);printTable(v, n + 1);elsestr_k_v = str_k_v..tostring(v);print(str_k_v);endendprint(str_space.."}");
end

完整项目托管在github上: https://github.com/xlplbo/Lua2Game.git(支持vs2013和cmake编译)

转载请申明出处,如有任何疑问或建议指出,谢谢~

转载于:https://my.oschina.net/xlplbo/blog/398792

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/289202.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为ar2200路由器 策略路由_路由交换篇 06 华为路由器RIP协议的配置

任务2 RIP路由协议的配置■ 任务背景某大型企业已有12台路由器&#xff0c;需要组建内部网络实现资源共享&#xff0c;考虑公司规模较大不适合采用静态路由组网&#xff0c;工程师对公司的实际情况进行了分析&#xff1a;1、路由跳数较多&#xff0c;不适合使用静态路由组网&am…

c均值算法的设计与实现_如何使用C链表实现 LRU 算法

什么是 LRU 算法LRU 是一种缓存淘汰策略。计算机的缓存容量有限&#xff0c;如果缓存满了就要删除一些内容&#xff0c;给新的内容腾位置。但是要删除哪些内容呢&#xff1f;我们肯定希望删掉那些没有用的缓存&#xff0c;而把有用的数据继续留在缓存中&#xff0c;方便之后继续…

VS Code部署Teams webhook到Azure Functions

点击上方蓝字关注我们&#xff08;本文阅读时间&#xff1a;6分钟&#xff09;Microsoft Teams这款产品对于我们来说已经很熟悉了&#xff0c;作为开发者&#xff0c;我们也可以通过官方的一些开发模式来build我们自己的Teams应用。今天快速跟大家分享一下&#xff0c;如何在VS…

记一次TCP连接异常故障解决

为什么80%的码农都做不了架构师&#xff1f;>>> 一.情况表现为 1.在公司内网对站点的http访问&#xff1a; linux主机出现故障&#xff1a;curl以及抓包分析&#xff0c;发现服务端不响应linux客户端的请求&#xff0c;无法建立TCP连接&#xff0c;浏览器返回“无法…

微软官方pe工具_微软官方下载工具

二、进入官网下载百度搜索“win10下载”进入微软官网win10下载地址&#xff0c;点击“立即下载工具”开始下载&#xff1b;三、运行软件下载完成后&#xff0c;右键以“管理员身份”打开&#xff0c;点击“接受”。四、选择用途选择第一个升级自己的电脑&#xff1b;选择第二个…

Task.Factory.StartNewTResult 和 Task.RunTResult 到底有什么区别?

前言这不是和《Task.Factory.StartNew 和 Task.Run 到底有什么区别&#xff1f;》一样吗&#xff0c;怎么又写一篇&#xff1f;起先我也是这么觉得的&#xff0c;但实际发现并非如此。实现代码查看这 2 个方法的内部实现&#xff0c;其内部实现逻辑其实是一样的&#xff0c;只是…

【遥感物候】C#遥感数据GIMMS 3G NDVI头文件批量生成器(几何校正)

长时间序列全球NDVI数据GIMMS 3g(点击下载),原数据无投影,格式为VI3g,IE浏览器下载的为.txt格式。GIS软件无法直接打开,Envi 5可以打开。 ENVI中点击File->Open File as->Binary会弹出填写元数据的窗口,填写基本信息: Envi5.1中打开二进制(Binary)数据是,需要…

分享一个基于.NET6包含DDD,ES,CQRS等概念的开源项目

当你在学习DDD、CQRS或时间溯源时&#xff0c;除了大量的学习资源&#xff08;比如书籍和文章&#xff09;之外&#xff0c;你还接触到了许多概念&#xff0c;这些资源只是在讨论理论问题。这很好&#xff0c;我们知道他们在说什么&#xff0c;但我们如何在一个真正的项目中使用…

React Native之通过createStackNavigator实现携带参数的页面与页面之间的跳转

1 实现的功能 在网上看React Native文档,我特码就想实现一个页面到另外一个页面的跳转,然后另外一个页面怎么获取参数,特么没找到一个说清楚的,要么太复杂,要么说了不理解,下面是我自己写的一个App.js文件,实现一个Home页面跳到另外Details页面,并且携带了参数怎么在Details页…

google浏览器插件 开发 获取页面指定数据_程序员必备的4款Chrome插件,编程神器...

一直有粉丝留言&#xff0c;想要大侠推荐几款程序员使用的插件&#xff0c;大侠特意去问了隔壁的程序员哥哥&#xff0c;终于被我问出了这4款编程神器&#xff01;这4款插件不仅仅是提高效率那么简单哦&#xff0c;还可以让你的Chrome浏览器变得高端大气&#xff0c;一起来看看…

MATLAB多元非线性回归

解释变量&#xff1a;商品价格&#xff08;x1&#xff09;人均月收入&#xff08;x2&#xff09;&#xff0c;被解释变量&#xff1a;商品需求量&#xff08;y&#xff09;&#xff0c;进行二元回归分析&#xff0c;并进行检验 商品价格(元/件) 月收入(元) 需求(件) 89 …

.NET高级调试 | 通过JIT拦截无侵入调试 C# Emit 生成的动态代码

大家还记得上一篇的测试代码吗&#xff1f;我们用了&#xff1a;Console.WriteLine("Function Pointer: 0x{0:x16}", Marshal.GetFunctionPointerForDelegate(addDelegate).ToInt64());来获得 委托 的 函数指针 地址&#xff0c;通过这个突破口最终实现了 动态代码 的…

使用IAR开发CC2530遇到的两个问题

2019独角兽企业重金招聘Python工程师标准>>> 首先说明&#xff0c;IAR for 8051为7.51版本&#xff0c;操作系统为windows7 32位。 上手CC2530&#xff0c;在IDE的使用上就遇到了2个问题。 一个是用SmartRF Programmer Flash下载HEX文件不成功&#xff0c;提示说cou…

openssh登陆时提示服务器拒绝了密码

升级openssh7.5后&#xff0c;登陆报错按照网上的说法是不允许root用户登陆但是&#xff0c;/etc/ssh/sshd_config 已经写入PermitRootLogin yes解决方法&#xff1a;设置/etc/sysconfig/selinux 中的SELINUXdisabled然后重启就OK了转载于:https://blog.51cto.com/adamcrab/194…

Blazor University (10)组件 — 捕获意外参数

原文链接&#xff1a;https://blazor-university.com/components/capturing-unexpected-parameters/捕获意外参数源代码[1]之前我们已经看到了如何使用特定名称声明参数和级联参数。例如&#xff0c;一个将 <img> 元素包装在一些自定义 HTML 中的自定义组件。<div cla…

React Native之最构建对象通过构造方法传递值然后再获取值

1 问题 在一个文件构建一个对象,然后在另外一个文件里面new这个对象,通过构造方法传递参数,然后再获取这个参数 2 测试代码 Student.js文件如下 use strict;import React from reactimport {NativeModules, NativeEventEmitter, DeviceEventEmitter,Alert} from react-nativ…

.NET点滴:SpanT

昨天小桂问了一个问题&#xff0c;把一个数组的全部元素加1&#xff0c;有什么好办法&#xff0c;于是有了下面的分析&#xff1a;var arr new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; //方法一 foreach (var i in arr) {i; } //方法二 for (var i 0; i < arr.Length; i) {…

React Native之通过DeviceEventEmitter发送和接收事件

1 怎么实现发送和接收事件 理论上封装了Android原生广播的代码,需要注册和反注册,这里用DeviceEventEmitter实现 //增加监听 DeviceEventEmitter.addListener //取消监听 //this.emitter.remove(); 这里可也可以通过安卓原生向页面js发送消息,可以参考我的这篇博客 React Nat…

navicat循环执行上下两行相减sql语句_SQL太难?你离完全理解SQL就差这10步!

- 点击上方“中国统计网”设置⭐星标不迷路&#xff01;-很多程序员视 SQL 为洪水猛兽。SQL 是一种为数不多的声明性语言&#xff0c;它的运行方式完全不同于我们所熟知的命令行语言、面向对象的程序语言、甚至是函数语言(尽管有些人认为 SQL 语言也是一种函数式语言)。我们每天…

mysql游标书写_mysql中光标如何书写

mysql中光标书写的方法&#xff1a;首先声明光标&#xff1b;然后开启光标&#xff0c;代码为【OPEN cursor_name】&#xff1b;接着捕获光标&#xff1b;最后关闭光标&#xff0c;代码为【CLOSE cursor_name】。本教程操作环境&#xff1a;windows7系统、mysql5.8版&#xff0…