基于模板特化的Lua自动绑定系统

许久没有更新过blog了,看看最后一次更新都是3、4年前的事情,差点忘记了它的存在,今天要重拾一下。以前转载过的文章都删除了,只留下自己写过的东西。

Lua可选绑定系统有很多,比如老牌的SWIG、LuaBind、tolua++、LuaThinker等等。

SWIG可以绑定各种语言,比如Python,JS等,本身过于庞大,而且绑定过程中会产生各种Warp,一定程度上影响了性能。

LuaBind看了一眼官方Sample之后就立马喜欢上了,但由于用到了Boost这个大家伙,我们又是做mobile的,果断放弃。

tolua++好像很多人使用,它可以通过头文件就可以自动生成绑定代码,好像也挺方便的,但我不太喜欢这种自动化的东西,而且需要生成一堆的不太喜欢的代码,日后扩展起来恐怕有各种麻烦。

LuaThinker本身很简洁,就一个头文件,但简洁过头了,功能太少,不过实现的方式也是基于模板的,所以我们的方式类似。

上面这几个绑定Lib应该算比较有代表性的啦,还有像一些OOLua什么的,基本都大同小异,如果你对Lua的绑定还不是十分了解的话,我推荐你看一下<<游戏编程精粹6>>里面有篇关于Lua自定绑定的文章,挺不错的。

先给大家看看最终的API用法,已经跟tolua++什么的自动化方式也复杂不了多少,我认为已经很简单了,关键是方便扩展、速度快。

image

下面就来说说我们实现的方式,这里只谈几个要点。

1、参数的传递,看代码片段就有直观的体现

image

通过模板特化可以自动推导匹配的接口类型,可以扩展任意需要的类型,这个也是该系统的主要思想,大家可能注意到了CppClass2Lua是怎么实现的,be patient 往下看

2、类的注册

我们想lua传递类对象的时候是通过userdata来实现的,而我们赋予这个userdata特殊的metatable(而且每一种对象都有其相对应的metatable,存放在lua的全局表中)

image

3、类方法的注册

如果你看过<<游戏编程精粹6>>,你就知道他把类所有相关的方法都存放在自定义的std::map中保存,然后通过方法名作为key来查找对应的方法。而我们的方式有点不同,我们把类所有的方法都保存在上面说的metatable中,以lua的方式来存放。

image

从代码可以看到我们把存放进的类方法包装成一个类,然后作为lightuserdata关联到真正被注册进metatable的MethodFuncCallProxy

4、怎么调用已经注册的类方法

当lua中调用类对象的时候(比如a:Foo())就会自动调用到我们metatable的__index方法,因为我们把方法都存放在metatable,所以我们就需要在回调中通过相关的key找到对应的方法,然后再Push给lua。

比如a:Foo()就会把之前注册的Foo函数找出来给lua,然后lua就调用MethodFuncCallProxy,然后就可以从MethodFuncCallProxy中拿到作为lightuserdata的MethodClass,然后就真正调用了c++的方法了。

类属性、构造方法等也是通过类似的方式来实现的,这里就不详述。

image

image

5、继承的实现

其实超简单,只要在CallOnLuaIndex的时候,如果在当前类的matatable里面找不到相关的方法,只要不断递归向上查找父类的metatable就可以了,就是需要在注册的时候保存好父类的metatable获取方式。

6、生命周期控制

我们的实现方式是引用计数,在c++对象push给lua的时候引用+1,lua对象释放的时候引用-1(通过metatable的__gc方法实现)。

大概就想到这么多了,就这样先吧,好久没写过东西了,非常吃力呀!写得不好,请见谅!

代码仓库:https://github.com/cloudffx/lua-cpp-bind-study

原文地址:https://www.cnblogs.com/cloudffx/p/3308415.html