• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

lua与c++交互全解析

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

欢迎转载,请保留出处: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自身数据模型就先说到这里,整个体系的详细说明将在后文里继续描述。


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
Lua入门教程发布时间:2022-07-22
下一篇:
lua闭包发布时间:2022-07-22
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap