• 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自身数据模型就先说到这里,整个体系的详细说明将在后文里继续描述。

  • 相关阅读:
    有关位域的理解和说明
    LINUX 循环fork()
    关于Linux平台malloc的写时拷贝(延迟分配)【转】
    字符串常量到底存放在哪个存储区[转]
    linux下安装eclipse
    安装和布署项目
    linux 的 samba 实现共享文件夹
    php protobuff 使用
    MFC 配合 protobuff libevent 实现的Socket 的GM工具 框架
    php 学习使用
  • 原文地址:https://www.cnblogs.com/wellbye/p/3025277.html
Copyright © 2020-2023  润新知