如何在Lua与C/C++之间实现table数据的交换
之前在《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 = ,
info = "this is test about exchange table data!",
task = {, , , },
};
我们的脚本将调用一个程序封装好的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, );
printf("%s\n", lua_tostring(L, -)); //取栈顶元素(注意这里的整型值都是string类型)
lua_pop(L, ); //取完之后清理栈顶
printf("stack size = %d\n", lua_gettop(L)); //打印栈中元素的个数 lua_pushstring(L, "info"); //同上
lua_gettable(L, );
printf("%s\n", lua_tostring(L, -));
lua_pop(L, );
printf("stack size = %d\n", lua_gettop(L)); lua_pushstring(L, "task"); //这里的value值是一个table哦,没关系栈操作都是一样的
lua_gettable(L, );
for (int i = ; i < ; ++i)
{
lua_pushnumber(L, i+);
lua_gettable(L, -);
printf("%s\n", lua_tostring(L, -));
lua_pop(L, );
}
lua_pop(L, );
printf("stack size = %d\n", lua_gettop(L)); //到这里tTest表依然在栈底,但不影响后面的操作。 //------华丽的分割线------------//
//到这里table数据的解析就结束了,以下内容是c API给lua返回table数据 lua_newtable(L);//要给lua脚本返回一个table类型,先要new一个,压入栈顶
lua_pushnumber(L, ); //将key先压入栈
lua_pushstring(L, "table2lua"); //再将value压入栈
lua_settable(L, -);//settable将操作-2,-1编号的键值对,设置到table中,并把key-value从栈中移除 lua_pushstring(L, "key"); //同上
lua_newtable(L); //这里有个子table
lua_pushstring(L, "capi");
//这里的value类型使用lua_CFunction类型,可用做c API调用,函数实现请参看附录1
lua_pushcfunction(L, LuaSayHello);
lua_settable(L, -);
lua_pushnumber(L, );
lua_pushnumber(L, );
lua_settable(L, -);
lua_settable(L, -); //这个从这里“lua_pushstring(L, "key"); //同上”开始匹配的
printf("stack size = %d\n", lua_gettop(L));
return ; //返回栈顶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 = ,
info = "this is test about exchange table data!",
task = {, , , },
};
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 = //以下是LuaTestTable的输出 stack size =
this is test about exchange table data!
stack size = stack size =
stack size = //以下是printTable的输出
{
[] = table2lua
[key] =
{
[] =
[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 ;
}
附录2:
//脚本函数实现打印一个table
function printTable(t, n)
if "table" ~= type(t) then
return ;
end
n = n or ;
local str_space = "";
for i = , n do
str_space = str_space.." ";
end
print(str_space.."{");
for k, v in pairs(t) do
local str_k_v = str_space.." ["..tostring(k).."] = ";
if "table" == type(v) then
print(str_k_v);
printTable(v, n + );
else
str_k_v = str_k_v..tostring(v);
print(str_k_v);
end
end
print(str_space.."}");
end
完整项目托管在github上:(支持vs2013和cmake编译)
转载请申明出处,如有任何疑问或建议指出,谢谢~
如何在Lua与C/C++之间实现table数据的交换的更多相关文章
- 如何在docker和宿主机之间复制文件
如何在docker和宿主机之间复制文件 最近在用Docker布署hadoop,要将文件上传到HDFS首先文件得在Docker容器中吧,网上提供的方法差不多有三种 1.用-v挂载主机数据卷到容器内 ...
- Android File Transfer Mac: 如何在 macOS 和 Android 系统之间移动数据
三大 Mac OS X 系统 Android 文件传输软件 谷歌出品的 Android File Transfer 如何在 Mac 系统上使用 Android File Transfer Androi ...
- 如何在Mac和Windows PC之间无线共享文件
有时候,我需要在Mac和PC之间无线共享文件.由于并非所有人都在使用macOS,因此无论是在办公室还是在家里,这种情况都会发生.尽管并非一帆风顺,但有一种无需任何第三方应用程序即可弥合差距的方法. 根 ...
- 不制作证书是否能加密SQLSERVER与客户端之间传输的数据?
不制作证书是否能加密SQLSERVER与客户端之间传输的数据? 在做实验之前请先下载network monitor抓包工具 微软官网下载:http://www.microsoft.com/en-us/ ...
- Android消息机制之实现两个不同线程之间相互传递数据相互调用
目的:实现两个不同线程之间相互传递数据相互调用方法. 线程一中定义mainHandler 并定义一个方法mainDecode 线程二中定义twoHandler 并定义一个方法twoEncode 实现当 ...
- cocos2d-x lua table数据存储
cocos2d-x lua table数据存储 version: cocos2d-x 3.6 1. 将table转为json http://blog.csdn.net/songcf_faith/art ...
- 在Activity之间如何传递数据,请尽可能说出你所知道的传递数据的方法,并详细描述其实现过程。
在Activity之间如何传递数据,请尽可能说出你所知道的传递数据的方法,并详细描述其实现过程. 答案:可以通过Intent对象.静态变量.剪切板和全局对象进行数据传递,具体的数据传递方法如下. 1. ...
- Unity 3D Framework Designing(5)——ViewModel之间如何共享数据
对于客户端应用程序而言,单页应用程序(Single Page Application)是最常见的表现形式.有经验的开发人员往往会把一个View分解多个SubView.那么,如何在多个SubView之间 ...
- Unity应用架构设计(5)——ViewModel之间如何共享数据
对于客户端应用程序而言,单页应用程序(Single Page Application)是最常见的表现形式.有经验的开发人员往往会把一个View分解多个SubView.那么,如何在多个SubView之间 ...
随机推荐
- 【转】权限管理学习 一、ASP.NET Forms身份认证
[转]权限管理学习 一.ASP.NET Forms身份认证 说明:本文示例使用的VS2017和MVC5. 系统无论大小.牛逼或屌丝,一般都离不开注册.登录.那么接下来我们就来分析下用户身份认证. 简单 ...
- Android Studio开发环境搭建和HelloWorld
跟着教程做的,已经有了JDK,直接进行后面的步骤,下载安装Android SDK 没有FQ,教程里的网址打不开,就换了个.网址 http://tools.android-studio.org/inde ...
- 2019.02.21 bzoj5317: [Jsoi2018]部落战争(凸包+Minkowski和)
传送门 题意:qqq次询问把一个凸包整体加一个向量(x,y)(x,y)(x,y)之后是否与另外一个凸包相交. 思路:转化一下发现只要会求A+B={v⃗=a⃗+b⃗∣a⃗∈A,b⃗∈B}A+B=\{\v ...
- Django基础—1
一. Django的安装1. 查看已安装的Django的版本 进入到终端以及Python的交互模式 python3/ ipython32. 交互模式中输入import django ...
- java 支持 超大上G , 多附件上传
首先 确定要上传的目录 WEB.XML 文件 Java代码 <listener> <listener-class><!-- 临时文件收集器 , 支持超大附件必须项 - ...
- [solution] JZOJ-5795 词典
[solution]JZOJ-5795 词典 题面 Description 小C有$n$个字符串$T_1 T_n$,给出$m$个询问 第$i$个询问给出一个字符串$S_i$,对于每个询问,我们可以得到 ...
- Docker 启动不了容器的问题
今天在运行 docker 的时候,就是执行 docker exec 命令的时候,发现一直报错.具体的报错信息如下: Error response from daemon: Container XXX ...
- SQL Server使用证书最小粒度授权
最近在项目中某个功能遇到了阻塞造成的Time out错误.所以需要在执行该功能的时候开启一个线程检测SQL Server中阻塞会话.但是程序本身使用的sql帐号本身不具备VIEW Sever Stat ...
- 使用ILMerge 打包C# 绿色免安装版程序
使用ILMerge工具,将C#项目debug目录下的exe及其依赖的dll文件打包成一个exe文件,直接双击就可运行. 使用工具: ILMerge :http://www.microsoft.com/ ...
- Bashu2445 -- 【网络流24题-10】餐巾问题
2445 -- [网络流24题-10]餐巾问题 Description 一个餐厅在相继的n天里,每天需要用的餐巾数不尽相同.假设第i天需要ri块餐巾(i=1,2,…,n).餐厅可以购买新的餐巾,每块餐 ...