Lua C++ Binding之Lunar, Luna
服务端引擎的脚本, 我们项目在老端游项目上发展, 采取的是Lua脚本. 当前服务端的发展趋势是瘦引擎, 胖脚本模式, 基本上引擎负责的功能非常少, 主要是网络, 定帧, 定时器, 引擎通过导出相应的接口给脚本层调用, 数据基本上都是放在脚本层里, 引擎层只记录一些相关功能必须的数据, 如我们的引擎层里面记录了断线重连相关的验证信息, 玩家所在battle信息用于转发battle和client之间的通讯数据等. 瘦引擎胖脚本的好处是基础设施架设好之后, 可以很快的铺玩法, 而且由于脚本虚拟机的存在, 服务端代码的稳定性和健壮性有一定的保证, 缺点可能就是老大难的脚本执行效率问题.
我们的引擎入口是在C++, 也可以认为C++这一层次就是引擎层, 而Lua这一层是脚本层. 回到主题, 引擎层和脚本层需要能够自由切换, 可以方便的从引擎层调用进入脚本层, 脚本层也需要方便的使用引擎层提供的数据对象和接口, 我们服务端采取的是一个很轻量级的binding方案, 就是lunar.h(http://lua-users.org/wiki/CppBindingWithLunar), 可以看到, 这是一个比较古老的方案, 支持Lua5.0, 5.1版本, 5.2, 5.3版本可能需要修改一些接口调用才能编译通过.
对于lunar.h, 它提供了以下功能:
1. 提供了定义机制, 可以在Lua层声明C++对象
2. 提供了push接口, 可以在C++层传入C++对象(唯一的userdata, 就是对同一对象多次调用push, 传入的是同一个userdata对象), 在Lua层可以调用该对象注册给Lua层调用的接口
3. 提供了call接口, 可以在C++层调用Lua层定义的扩展函数, 或者是调用对象的Lua函数接口(http://lua-users.org/wiki/CallingLuaFromCpp), 调用对象的Lua函数接口是指从C++层调用该对象注册给Lua层的接口, 从而达到不用重复实现函数的目的, 但是理论上来说效率会下降, 距离来说, 对象sugar有一个taste接口, 一个c_taste接口(注册给Lua层使用), 两者功能重复, call可以让你只实现c_taste接口, 当C++层需要调用这个接口的时候, 调用c_taste接口, 而不需要重复编写一个taste接口.
4. 对于对象的生命周期, Lunar的默认处理是对象由C++层来销毁, 但是对于在C++层创建的对象, 可以通过在push时设置gc参数为true, 将销毁的行为交由Lua层处理. 而对于在Lua层创建的C++对象, 一定是由C++层来进行销毁的, 这部分要特别特别注意, 我们的做法是这类对象要么是全局对象, 声明周期从创建到服务器关闭, 例如db对象, 要么是一个函数调用中使用, 不会被缓存下来放到别处使用, 而函数调用末进入引擎层接口后, 引擎层会回收这个对象, 如buffer对象, 底层发送完这个网络buffer之后就会delete, 而脚本层不会缓存住这个buffer对象, 之后在某个时刻再次使用(无法判断底层是否已经回收了这个对象), 算是潜规则.
对于Lunar, 其代码注释写的已经不错, 接口不多容易理解, 其实现代码翻译为Lua代码如下(以实现一个Sugar的C++类为例)
methods = { new = new_T, func1 = thunk, --with upvalue func1 RegType func2 = thunk, --with upvalue func2 RegType -- ... } userdata_mt = { __metatable = methods, __index = methods, __tostring = tostring_T, __gc = gc_T, userdata = { __mode = "v", lightuserdata = fulluserdata, ... } ["do not trash"] = { __mode = "k", fulluserdata = true, ... } } -- 存在注册表当中 mt = { __call = new_T } setmetatable( methods, mt ) _G["Sugar"] = methods
Luna.h也是Lua C++ binding的一种方式, 最新的Luna版本是LunaFive(http://lua-users.org/wiki/LunaFive), LunaFive个人认为还未能达到Lunar的使用级别, 它具有一些Lunar没有的优点, 包括
1. 直接支持Lua5.3
2. 支持命名空间, Register接口的第二个参数, 默认为全局表
3. 提供了获取属性的接口函数, 而Lunar需要直接写set/get函数
4. 支持将C函数作为对象保存, 比如local func = obj.func, 之后func()是合法的, 而Lunar不能
相对于Lunar的缺点有:
1. gc行为是默认delete, 也就是说对象生命周期由Lua gc控制, 回收时机不确定
2. 没有考虑同一对象的多次push, 会产生多个userdata, Lunar只会产生一个
LunaFive的代码实现翻译为Lua代码如下, 利用了__index是function时会传入原始的table/userdata来获取C++对象指针, 通过index来对应记录成员变量和成员函数, 最多支持128个变量和128个函数.
userdata_mt = { __gc = gc_obj, __tostring = to_string, __eq = equals, __index = property_getter, __newindex = property_setter, [, [, ... [ | ( << ), [ | ( << ), ... } -- 存在注册表当中 _G["Sugar"] = constructor
我们项目使用的是Lunar, 虽然古老, 但是起码经受了端游项目和手游项目的考验, 虽然没有很花哨的功能, 需要遵守一些潜规则, 但是如果不是追求极致, Lunar这个解决方案应该也是够用了.
现在我在看一个新的解决方案, 是github上的一个开源项目lua-intf(https://github.com/SteveKChiu/lua-intf), 利用了一些C++11的新特性, 看起来蛮优雅的, 搜了一下似乎资料不多, 等细心研读之后有机会再分享.
Lua C++ Binding之Lunar, Luna的更多相关文章
- cpgf如何实现lua script binding的?
Lib: https://github.com/cpgf/cpgf/tree/master 代码 以下是operator的实现函数 int UserData_operator(lua_State * ...
- Lua与javascript的差异
Lua与javascript的差异 2010-03-08 Lua模拟器js方案 1.语法级模拟 lua与js语言差异 1.1注释 js 为//,lua为--. 1.2变量 js利用var来声明全局变量 ...
- 所有的GUI Toolkit,类型之多真开眼界
The GUI Toolkit, Framework Page User interfaces occupy an important part of software development. Th ...
- C++ 100款开源界面库 (10)
(声明:Alberl以后说到开源库,一般都是指著名的.或者不著名但维护至少3年以上的.那些把代码一扔就没下文的,Alberl不称之为开源库,只称为开源代码.这里并不是贬低,像Alberl前面那个系列的 ...
- 仿迅雷播放器教程 -- C++ 100款开源界面库 (10)
(声明:Alberl以后说到开源库,一般都是指著名的.或者不著名但维护至少3年以上的.那些把代码一扔就没下文的,Alberl不称之为开源库,只称为开源代码.这里并不是贬低,像Alberl前面那个系 ...
- cocos2dx 3.x 集成protobuf
vs2013+cocos2dx 3.13.1 这篇博文是集成Lua版本的protobuf,集成C++版本的过程也可参考. 主要参考博文地址<cocos2dx 3.x C++搭建protobuf环 ...
- 【转】cocos2dx 3.x 集成protobuf
http://www.cnblogs.com/chevin/p/6001872.html vs2013+cocos2dx 3.13.1 这篇博文是集成Lua版本的protobuf,集成C++版本的过程 ...
- cocos2dx lua binding ,cocos2dx 绑定lua测试
前面2篇分别简单介绍 手动bind C++ 类和lua:http://blog.csdn.net/chenee543216/article/details/12074771 使用tolua++简化工作 ...
- lua luna工具库
luna工具库 概述 luna库提供了几个lua开发的常见辅助功能: lua/c++绑定 lua序列化与反序列化 变长整数编码,用于lua序列化,当然也可以方便的用于其他场合 这里把代码编译成了动态库 ...
随机推荐
- Informatica - Powercenter 英文版资料(转载)
Informatica - Powercenter 英文版资料 http://gerardnico.com/wiki/powercenter/powercenter
- iptables基本规则配置(二)
注释:文章中fg:为示例 红色标记的为命令 在上篇博文中详细讲解了iptables的原理及一些常用命令,这里在简要的说明一下: Linux防火墙包含了2个部分,分别是存在于内核空间的(netfilt ...
- VS 工程的 输出路径和工作路径的区别
输出路径,是vs编译项目生成可执行文件的路径:工作路径是环境变量,比如我们在程序中写相对路径,就是以这个路径为基础的.在默认情况下,输出路径和工作路径都不写的话,默认是程序的bin下面的debug或者 ...
- Mac上安装与更新Ruby,Rails运行环境
Mac安装后就安装Xcode是个好主意,它将帮你安装好Unix环境需要的开发包,也可以独立安装command_line_tools_for_xcode 1.安装RVM RVM:Ruby Version ...
- JAVA中管道通讯(线程间通讯)例子
Java I/O系统是建立在数据流概念之上的,而在UNIX/Linux中有一个类似的概念,就是管道,它具有将一个程序的输出当作另一个程序的输入的能力.在Java中,可以使用管道流进行线程之间的通信,输 ...
- 火狐浏览器不能获取 event,解决js报错问题
//←的keyCode等于:37 //→的keyCode等于:39 //↑的keyCode等于:38 //↓的keyCode等于:40 function keystoke(obj){ //var e ...
- IDEA安装配置(SVN)
IntelliJ IDEA 14 注册码 Win7系统上配置使用Intellij Idea 13的SVN插件 IntelliJ 设置JDK http://blog.sina.com.cn/s/blog ...
- Django数据操作F和Q、model多对多操作、Django中间件、信号、读数据库里的数据实现分页
models.tb.objects.all().using('default'),根据using来指定在哪个库里查询,default是settings中配置的数据库的连接名称. 外话:django中引 ...
- Java集合与算法
梗概: 集合接口 集合实现(链表.数组列表.散列集.树集.队列与双端队列.映射表) 集合与数组之间的转换 算法(排序.二分查找). 文章链接: http://mp.weixin.qq.com/s?__ ...
- RunLoop笔记
1.Runloop基础知识 - 1.1 字面意思 a 运行循环 b 跑圈 - 1.2 基本作用(作用重大) a 保持程序的持续运行(ios程序为什么能一直活着不会死) b 处理app中的各种事件(比如 ...