LUA中的类型强转

 

众所周知,lua里的变量都没有类型的定义,是啥东西直接拿出来用就行,用错了运行时会报错不执行,类型强转这种操作似乎没啥用武之地。的确在单独使用脚本语言的环境里语言本身并不关心类型,但如果要与宿主语言交互,并且还利用了userdatalightuserdata对象与C里面的操作和变量关联,那类型强转就很有意义了。

LUA5.1版本后注册对象就逐步取代注册接口成为与宿主交互的首选方式,拿luatinker举例其注册对象的原理是为每个类型注册以类型命名的一个tabletable里记录的是该类在C里面成员变量和成员函数的相对偏移地址,并且把它和表项名所bind,而表项名就是注册给lua使用的对应的类变量名和函数名(e.g. class A{int m_a; int m_b;} 对应A::m_b变量的在table里相对就存在一个名为’m_b’ 表项,而值就是0x00000004,在传入userdatalightuserdata的时候把它们的metatable附上由类型注册的那个table。使用 ‘.’ 调用类变量或 ‘:’ 调用类函数的时候把lightuserdata作为类的this指针传回加上注册的偏移值就可以访问对应的变量和函数了。这种对象访问的实现方式很多lua的封装库都采用了,而luatinker在此之上还利用template来实现对象和类型,userdatametatable表名之间的关联。使用者在调用的时候不用指明要挂接的metatable,统一交给C++编译器对模板的推导。

不可否认模板绝大多数情况下发挥得很给力,但也存在一个现象:编译期抉择的模板类型解决不了运行时才决定的派生类类型。 假设一个常见的应用:有个 lua function 接受值为CBase*userdata作为参数,运行时传入的是它的一个派生类CRole*,而在脚本中希望能掉用到派生类的操作sendMsg。由于对象体系导致CBase并没有sendMsg的虚接口(基类无法为所有的派生类用到的操作都提供虚接口。而往往接口为了统一也只把基类类型作为传递的参数类型) 即便用在lua环境里使用 class_addclass_def引入了CRolesendMsg操作,也用class_inh申明了CRoleCBase的继承关系(这只是强转能产生作用的前提),一旦调用role:sendMsg 同样是一个表对象操作错误。回头想想对照CLUA遇到的情况是相似的,传入 基类指针|userdata 需要调用派生类接口,编译器 编译时|运行时 通过 类型检查|模板推导结果 只能 查找出基类对应的函数表 |userdata挂接上以基类命名的metatable。既然情况一样,那C解决方案也适用LUA --- 强转

知道LUA类和操作的实现,强转也还算简单。 把userdatametatable重新设置成派生类的metatable 就可以了。 首先引入 lua_debug库,然 后再脚本使用变量role前,写上这么一句debug.setmetatable(role,CRole) ,不想引入debug库可以用lua_setmetatable自己实现一个C api的操作。

 

你可能感兴趣的:(LUA中的类型强转)