C++ 类 lua 调用 | C++提供接口使得lua可以创建并使用C++类

C++ 类 lua 调用

标题很纠结,来看看具体需求:

在C++中有类A如下:

wp

希望在lua中可以创建并使用这些类的不同实例,并语法尽量简单,如下:

2

好,首先我们知道,lua中可以很容易的用table表示类,函数名作为key存C++注册的函数,不过有一个难点在于,类的不同实例怎处理。我们知道,在C++中,类的不同实例拥有自己的数据段,但共用同一个函数段,我们怎么在lua里面模仿这个特性呢?

这里要依靠lua的一个特性,原表的__index元方法。假设我们有一原表mt{},里面保存C++类的函数,然后所有实例自己仅仅是一个整数,保存的是实例地址,然后所有的实例都setmetatable到mt上,就可以完成我们每个实例对应自己的数据段,共用函数段了。

我们来详细看看原表调用的过程:

3

参考手册里面讲得是,如果你对一个lua对象进行下标访问时,会判断对象是否为table,如果是table且key存在,就会返回值。如果是table但key不存在,则调用原表的__index。如果没有原表,或原表没有__index,则返回nil。

如果对非table对象进行下标访问,会直接取到对象的原表的__index,如果是一个方法,则用table,key调用,如果是一张表,则用key在这张表内索引。

利用这个特性,我们就可以在一张表AFunctionList{}内放A类的调用方法,并把表AFunctionList.__index指回自己(AFunctionList.__index = AFunctionList),然后CreateA返回的是一张表,表的“Object”字段是变量地址,然后设置原表为AFunctionList。然后,当我们在lua里a0:Show();时,a0表没有Show方法,就会通过原表的__index指向AFunctionList{},检索出AFunctionList{}的“Show”,并利用a0:Show == a0.Show(a0)这语法糖,把保存实例地址的a0表也传进函数中,从而实现C++的thisCall。

来看看具体CreateA怎么做:

createa

还有A类Show方法的导出函数AShow:

ashow

认真看可能会发现,其实代码做所的跟上面所讲的还不完全一样。还多了一个设置注册表引用的过程

ref

为什么这么做呢?考虑一下如果C++方面销毁了一个变量,C++是无法通知lua,lua也是无法得知的。这时lua里a0.Object变量还是这个已经非法的地址,再通过这个地址调用函数时就会出问题。所以我们一定要把实例保存在一个lua与C++都能访问到的全局table里面,而且C++类里面要有变量保存这个Regesiter表的index,当C++销毁了实例时才能通知索引Regesiter表相应的区域,进行改动,从而通知lua。

上述是将C对象导出成table到lua环境,导出成为table的一个好处是lua层面能够直接在上面挂数据。

还有另外一种方法,是将lua对象导出成为userdata,这种做法导出到脚本层面的就是一个userdata,不能在上面挂数据,但可以利用userdata的一些特性,例如引用被GC回收时,就会调用__gc方法。如果希望同一个对象在lua层面都是同一个userdata,可以在metatable中创建一个值是弱引用的week table,用C++对象指针做key,每次导出对象时就去用指针值查week table,如果有用原来的值,没有就创建。 同时,当userdata是被弱引用的,当被GC回收的时候,week table中它所在的键值对自动被销毁了。

这种方法适合导出C层面也是唯一的对象,GC回收时销毁C对象这个特性也要慎重考虑是否合适。

Tagged , . Bookmark the permalink.

发表评论

邮箱地址不会被公开。