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

之前在《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

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

  1. 如何在 Unix 和 DOS 格式之间转换文本文件

    本文介绍如何在 Unix 和 DOS 格式之间转换文本文件.DOS 文本文件带有回车符( \r )和换行符( \n )作为它们的换行符,而 Unix 文本文件只有( \n )换行符作为换行符. 有多种 ...

  2. 如何在Mac和Windows PC之间无线共享文件

    有时候,我需要在Mac和PC之间无线共享文件.由于并非所有人都在使用macOS,因此无论是在办公室还是在家里,这种情况都会发生.尽管并非一帆风顺,但有一种无需任何第三方应用程序即可弥合差距的方法. 根 ...

  3. 如何在Windows和centOS系统之间实现复制粘贴?步骤如下

    如何在Windows和centOS系统之间实现复制粘贴?只需简单几步即可! vmtools安装 vmtools安装 安装vmtools步骤: 1.进入centOS 2.点击vm菜单的 -> in ...

  4. android手机icoude邮件,如何在Android设备上访问Apple应用和数据?教程来了!

    原标题:如何在Android设备上访问Apple应用和数据?教程来了! 对于一些出于工作原因,拥有ios和Android不同系统手机的人来说,有时候会觉得在共享资料或数据上会比较麻烦.但是,其实并非如 ...

  5. python线程的注意点(线程之间执行是无序的、主线程会等待所有的子线程执行结束再结束(守护主线程)、线程之间共享全局变量、线程之间共享全局变量数据出现错误问题(线程等待(join)、互斥锁))

    1. 线程的注意点介绍 线程之间执行是无序的 主线程会等待所有的子线程执行结束再结束 线程之间共享全局变量 线程之间共享全局变量数据出现错误问题 2. 线程之间执行是无序的 import thread ...

  6. C语言 —— 获取字符串中两个字符之间的的数据

    问题描述:获取字符串中两个字符之间的数据.最近写SMTP代理,需要获取发送方与接收方的邮件名称. 字符串为 "MAIL FROM:<test1@163.com>\r\n" ...

  7. cocos2d-x lua table数据存储

    cocos2d-x lua table数据存储 version: cocos2d-x 3.6 1. 将table转为json http://blog.csdn.net/songcf_faith/art ...

  8. vscode重置应用程序_如何在Windows 10上重置应用程序的数据

    vscode重置应用程序 With Windows 10's Anniversary Update, you can now reset an app's data without actually ...

  9. html间数据传送,Express框架与html之间如何进行数据传递(示例代码)

    关于Node.js 的Express框架介绍,推荐看菜鸟教程的Express框架,很适合入门,这里不再赘述,这里主要讲一下Express框架与html之间如何进行数据传递 我采用的是JQuery的Aj ...

最新文章

  1. python文件操作(open()、write()、read()、readline()、readlines()、seek()、os)
  2. angular 发布订阅
  3. 使用Android Studio打Andorid apk包的流程
  4. 怎么退出自适应巡航_沃尔-ACC 自适应巡航控制系统
  5. spring—JdbcTemplate使用
  6. 基于SpringBoot 2.0正式版的SpringCloud的微服务实战项目搭建
  7. 苹果测试集了成语音识别和人脸识别的智能家居?
  8. vs2013的mfc开发上位机
  9. win10误删的注册表能还原吗_如何恢复修改过的win10注册表?win10注册表修改后恢复方法...
  10. Detectron2安装教程
  11. 6、Nacos服务注册——PushService类功能
  12. [CF1504E]Travelling Salesman Problem
  13. 微信新动作!加好友解除5000上限,扫码进群开放至200人
  14. matlab 增量学习,支持向量机在线增量学习算法的MATLAB实现
  15. 物联网实战之基于OneNET的智能农业系统
  16. shell检测硬件状态脚本
  17. 多值依赖与部分函数依赖 转
  18. 华米手表会安装鸿蒙系统那,【教程】华米运动手表3安装第三方应用、第三方表盘及使用教程...
  19. AI实战:上海垃圾分类系列(一)之快速搭建垃圾分类模型
  20. 美丽即可用抑或可用即美丽?

热门文章

  1. Password Creator(HTA)
  2. cmd使用另一个Oracle的sid,(转发备用)Oracle SID在本机上已经存在,请指定一个不同的SID”的解决办法...
  3. python后缀名切割_python批量将.xls转化为.xlsx,包括文件名和后缀分割
  4. ecshop api.php,api.php
  5. 设计模式工厂方法模式
  6. Spring系列(十):@Autowired 和@Resource注解用法介绍
  7. 办公技巧:Excel下拉菜单小技巧,赶紧学一下!
  8. 收集100 个网络基础知识
  9. JVM真香系列:.java文件到.class文件
  10. 数据库系统工程师考点分析