lua与c++交互全解析

欢迎转载,请保留出处:http://www.cnblogs.com/wellbye/

最近经常见有人在群上问有关lua在c/c++中嵌入使用的问题,但很多问题本身问得就莫名所以,很可能是对一些基本概念还未正确理解就急于使用,遇到难处也没有认真思考问题的本质是什么,自然会问出一些叫人啼笑皆非、欲答无词的问题。正好这段时间赋闲在家,希望能把几年来对lua及c++的理解及经验总结一下,为同样喜欢这两样语言的同好做一个入门介绍。

从以下几个方面逐一解析这个问题:

1、lua的数据模型

2、跨语言交互的实质

3、c++对象模型

4、核心1:在lua中使用c++对象

5、核心2:高效地导出c++对象

一、lua的数据模型。lua是一门非常简单易用的语言,简单就简单在它的数据类型是“封闭”的(相对于python的开放而言)。它所有的类型都由一个TValue表示:

typedef union {
  GCObject *gc;
  void *p;
  lua_Number n;
  int b;
} Value;


/*
** Tagged Values
*/

#define TValuefields    Value value; int tt

typedef struct lua_TValue {
  TValuefields;
} TValue;

实际上TValue就是一个只有2个字段的结构体,其中tt表示类型,共有9种取值,1种是“nil(空)”,3种是简单的“值类型”已直接列在Value中即p/n/b,另外5种作为“引用类型”需要gc管理而由额外的GCObject结构表示:

union GCObject {
  GCheader gch;
  union TString ts;
  union Udata u;
  union Closure cl;
  struct Table h;
  struct Proto p;
  struct UpVal uv;
  struct lua_State th;  /* thread */
};

其中Proto/UpVal是完全由内部使用的数据可以不看。(额外插播:Proto是函数原型信息,UpVal是函数闭包引用的“上值”(即外层函数变量),这些本是纯内部实现相关的结构(即它们不会“上栈”以致被使用方“合法访问”),但它们也是需要动态管理的,那么直接将其当作“标准”数据类型来实现,就可重用那一整套复杂而完善的gc机制了。关于这种“内部功能也使用标准实现”的做法,我在读Python代码时也有类似感受,比如最常用的容器dict,其对应的实现在Objects/dictobject.c文件中,有一堆PyDict_XXXX的函数,按照Python C API的规范实现了一套完善的hashmap数据结构,这套功能当然首先是通过注册导出后,给Python脚本用的,但是在其它一些功能模块中,当要使用到hashmap功能时,居然也直接用这套PyDict_XXXX(而不是一般c语言里常用的hash库或是std::map一类的东西),这也算是对代码的一种自我验证吧)

ts是字符串,u是重型userdata,cl是函数闭包,h是hash表,th是协程。在与c++的交互中,u和h就扮演了最重要的角色。

lua的所有数据类型就到此为止了,且没法扩展(即在c层面自定义类型),因此要在lua里表现其它语言的数据类型如c++类等,就只有用这几个现成的类型去组合模拟。相比Python有很大不同,Python扩展开发者可以通过实现自己的PyTypeObject,在c层面创建全新的类型。在我理解中也正是这种差别,导致了Python无法使用像Lua那样的三色标记法gc,因为自定义结构中各种指针引用字段的存在,让Python无法追踪扫描下去转而只能使用引用记数法,但会导致循环引用,其解决办法仍然是要求类型创建者提供额外的追踪扫描函数,在某种程度上达到与Lua gc相同的功效。

现在重点说明hash表和userdata两种类型。

hash表,是在lua语言中表达描述各种数据结构的最佳也是唯一工具,其除了作为容器的一般用途外,还有两个重要特性:元表和弱表。元表即metatable,它本身是一个普通表,但它的字段描述了其目标表的特殊功能,如对不存在字段的get/set、运算符的重载等;弱表,是一种具有特殊回收机制的表,当它的每一项key或value不再被外部引用(即仅存在于表中)时,会自动销毁,在纯粹的c++里是没有gc机制的,但利用弱表的这个特性,在结合lua使用时反而可以做到c++对象的自动回收。

userdata,是lua用来表示外部(宿主语言)数据的类型,又分成lightuserdata和普通userdata两种。lightuserdata本身没有运算概念,只具有“保存”和“传递”的意义,普通userdata则可以设置其“元表”,从而具有get/set功能,但更重要的是可以设置其gc处理函数,让c++端的资源享受lua gc的便利。

在我设计的绑定体系中,会用一个hash表来表示lua对象比如叫luaobj,而luaobj[1]就是一个存储了对应c++对象指针的userdata,其上挂了gc handler,在此userdata被lua清除时,调用c++ obj的减引用计数函数。另外每一个类也是用一个hash表来表示,里面存储了该类的所有导出函数,并且又通过元表指向其父类所对应的hash表,整个类层次就这样串起来。而每一个luaobj也通过元表链接到其所对应的类表,这样在luaobj上就可以找到调用所有其在c++层面的函数了。

关于lua自身数据模型就先说到这里,整个体系的详细说明将在后文里继续描述。
原文链接: https://www.cnblogs.com/wellbye/archive/2013/05/01/3025277.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/86639

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月9日 下午10:41
下一篇 2023年2月9日 下午10:41

相关推荐