如何在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之间 ...
随机推荐
- CSS学习总结4:派生选择器学习总结
派生选择器:通过依据元素在其位置的上下文关系来定义样式,你可以使标记更加简洁.派生选择器中一共分为三种:后代选择器.子元素选择器.相邻兄弟选择器. 1.初识派生选择器 实例:你希望列表中的 stron ...
- sql中with as测试实例
一.使用场景 1.多处使用才有必要2.一方面减少代码数量便于理解维护3.一方面跟代码一样一次计算到处用 二.实例(本处示例仅为测试,实际用join比较好) 1.不使用with as 2.使用with ...
- ramfs的两种制作方法
制作方法1 1 准备一个已经可以使用的文件系统,假设目录为/rootfsLinux内核需要支持ext2文件系统及ramdisk支持(fs相应的选项要勾上)2 在pc上制作ramdisk镜像(1)dd ...
- spring mvc+mybatis 构建 cms + 实现UC浏览器文章功能
最近公司在模拟UC浏览器做一个简单的cms系统,主要针对于企业内部的文章浏览需求,这边考虑用户大多用mobile浏览文章内容,故使用原生的ios和android进行开发,后面也会集成html5. 1. ...
- Sum of Even Numbers After Queries LT985
We have an array A of integers, and an array queries of queries. For the i-th query val = queries[i] ...
- 将小程序的logo换成属于自己的头像
$logo = file_get_contents(IMG_URL.$logo); //根据头像的路径 $logo = yuanImg1($logo); //将头像改为圆形 $data = file_ ...
- android开发环境配置以及测试所遇到的的问题
今天我没有继续进行,整理了一线之前犯下的错误.在一开始的android的环境配置的时候,按照网上的教程,我还是走了许多弯道,其中遇到了不少的问题,但是现在都一一解决了. 配置时安装东西少 在配置的时候 ...
- 展示博客---Alpha版本展示
Alpha版本展示 1. 团队成员的简介和个人博客地址,团队的源码仓库地址. 成员 简介 个人博客地址 祁泽文 被动态统计图搞扒下的我 http://www.cnblogs.com/jiaowoxia ...
- Java变成思想总结
Java容器 collection一个独立的元素序列,这些元素都服从一条或多条规则.list必须按照插入的顺序保存元素,set不能有重复元素,queue按照排队的顺序确定对象产生的顺序.map一组成对 ...
- java技术突破要点
一.源码分析 源码分析是一种临界知识,掌握了这种临界知识,能不变应万变,源码分析对于很多人来说很枯燥,生涩难懂. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 我认为是阅读源码的最核心 ...